Python 2, 249 248 244 239 237 bytes
D={}
m=X=Y=0
for s in input().split(","):d=ord(s[0])%10%7;exec"a,b=X,Y;E=D[Y]=D.get(Y,{});E[X]='<^>v'[d];m=min(m,X);%c+=d-2|1;"%(88+d%2)*int(s[1:])
D[b][a]="X"
for Y in sorted(D):print"".join(D[Y].get(n," ")for n in range(m,max(D[Y])+1))
Input like "E2,N4,E5,S2,W1,S3"
.
NSEW
is mapped to [1, 3, 2, 00]
by d=ord(c)%10%7
. Whether to change y
or x
is then decided by d%2
, and whether to increment or decrement is decided by d-2|1
. The first and third expressions were found by brute force.
Other than that, it's a simple usage of a nested dictionary of the form {y: {x: char}}
.
(Thanks to @joriki for help with mapping)