# Yarr! A map to the hidden treasure!

### Introduction

"Yarr!! We had a laddie who called himself a "programmer" make a map t' our hidden treasure! But 'tis written wit' weird numbers 'n letters! "E5, N2, E3"... what does it even mean? Madness! Can't even scribe a proper treasure map, t' useless cretin. Fix it fer us! We'll gift ye a share o' t' treasure!"

### Challenge Description

A group of pirates are having trouble reading a treasure map. Can you write a program to convert it into a more... piratey form?

As input, you'll receive the original treasure map. It's a list of comma separated strings, each string which consists of a letter portion (which tells the pirates which direction they need to walk in), and a number portion (which tells the pirates how many steps to take in that direction). For instance, the following treasure map:

E2,N4,E5,S2,W1,S3


would mean, "walk two steps to the east, walk four steps to the north, walk five steps to the east, walk two steps to the south, walk one step to the west, then walk three steps to the south."

As output, you'll output the map in a graphical form, using the characters >, ^, v, and < as pointers. Here's the output for the above input:

  >>>>>v
^    v
^   v<
^   v
>>^   X


Notice that we've replaced the last step to the south with an X instead. This is because the last step is where the treasure is, and as we all know, pirates must have an X on their treasure maps, otherwise they won't know how to read it.

By the way, the map won't ever cross itself, so you don't need to worry about dealing with overlaps. Also, you are allowed to have a trailing new line at the end of the output.

### Sample Inputs and Outputs

S5,W2


 v
v
v
v
v
X<


N1,E1,S1,E1,N1,E1,S2


>v>v
^>^X


N1


X


N6,E6,S6,W5,N5,E4,S4,W3,N3,E2,S2,W1,N2


>>>>>>v
^>>>>vv
^^>>vvv
^^^Xvvv
^^^^<vv
^^^<<<v
^^<<<<<


E21,S2


>>>>>>>>>>>>>>>>>>>>>v
X


N12,E11,S12,W2,N4


>>>>>>>>>>>v
^          v
^          v
^          v
^          v
^          v
^          v
^          v
^          v
^        X v
^        ^ v
^        ^ v
^        ^<<

• Are we allowed to have trailing spaces on each line? Is the number always going to be less than ten? – Downgoat Aug 6 '15 at 3:59
• I really think the X should mark the step after the last move, like all other moves are counted. Imagine the last step is N3: you walk three steps to the north and dig, but there is nothing here, you had to walk 2 steps instead. I don't mind if you keep the existing rule, because it adds a little corner case to handle. But remember what happened to that laddie. – coredump Aug 6 '15 at 7:39
• @coredump Or maybe we want to mislead the pirates, so that we can take the treasure for ourselves ;) No, you're correct, the pirates are digging one step too soon. Given there are already three answers, I'll keep the rule as it is to avoid invalidating existing solutions. – absinthe Aug 6 '15 at 7:58
• @jpmc26 Well, these pirates don't know much of the alphabet... they spent the last few years at C :) – absinthe Aug 7 '15 at 10:39
• The fourth example is just trolling the pirates... – justhalf Aug 8 '15 at 3:12

# Ruby, 213 209 198 186 178

M={};x=0,m=q=0
gets.scan(/.(\d+)/){?1.upto($1){m,y=x x[d=$&.ord%10%7-2]+=1|($u=M[y]||={})[m]=d m<q&&q=m}}$u[m]=2
puts M.sort.map{|_,b|(q..b.max[0]).map{|k|">vX <^"[b[k]||3]}*""}


Pass input via stdin.

This uses a y -> x -> char dictionary to construct the map, where both x and y can be negative. Once the input has been parsed, the global minimum of the x coordinate is extracted. For each row, it then iterates over a range going from the global minimum to the maximum index for the current line, and prints the correct character for that index.

To stay with the theme, the expressions to turn NESW into the proper indices were shamelessly pirated from Sp3000's answer.

Original version that used a [x,y] -> char dictionary:

M={};x=0,0
gets.scan(/.(\d+)/){(?1..$1).map{x[d=$&.ord%10%7-2]+=1|M[$y=x+[]]=d}} M[$y]=2
a,*q=M.minmax.flatten
M.map{|(x,y),v|($*[y-M.map{|a,|a[1]}.min]||=?\s.*q[2]-a)[x-a]=">vX<^"[v]} puts$*.map &:rstrip


# Python 2, 249248244239 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, 0] 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)

• (d + 1 & 2) - 1 – joriki Aug 6 '15 at 17:50
• @joriki Ah that's a nice expression - thanks! – Sp3000 Aug 6 '15 at 23:50
• Here's some code I wrote (in a different context) for finding simple expressions for integer functions. I didn't use it for this, but just thought that it might be interesting for you. (The relevant code starts where it says "This is the code that I used to optimize the encoding".) – joriki Aug 6 '15 at 23:55
• @joriki Brute forcing's a great idea - just turned up 1|d%-3 (which is the negation, but I just realised that's okay too)! – Sp3000 Aug 7 '15 at 0:17

# Javascript (ES6), 260

This was an interesting one...

Thanks @ETHproductions, @edc65, and @vihan for the help!

s=>{z=o=""
m=[]
q=x=y=2e3
s.split,.map(v=>z+=v[0].repeat(+v.slice(1)))
for(i=0;d=z[i];q=x<q?x:q)(m[y]=m[y]||[])[x]=z[++i]?d=="N"&&--y?"^":d=="S"&&++y?"v":d=="W"&&--x?"<":++x?">":o:"X"
m.map(a=>a.map((b,j)=>o+=" ".repeat(-p-1+(p=j))+b,p=q-1,o+=
))
return o}


This defines an anonymous function, so to call it add f= to the beginning to give it a name.

To test: console.log(f("E2,N4,E5,S2,W1,S3"))

### Explanation:

s=>{ //define function w/ parameter s
z=o=""      //z=modified input, o=final output
m=[]        //map of characters
q=x=y=2e3   //q=minimum value of x; x+y=coordinates. These start high to leave room to go backwards
s.split,.map(v=>z+=v[0].repeat(+v.slice(1)))    //change "N3,E4" -> "NNNEEEE", and put in z
for(i=0;d=z[i];q=x<q?x:q)   //for each direction d in z, while updating q:
(m[y]=m[y]||[])[x]=     //in the right place on the map, put:
z[++i]?                 //if last character of z, "X"
d=="N"&&--y?"^":
d=="S"&&++y?"v":    //otherwise get the right character and change x+y accordingly
d=="W"&&--x?"<":
++x?">":o
:"X"
m.map(a=>a.map((b,j)=>o+=" ".repeat(-p-1+(p=j))+b,p=q-1,o+=
)) //dump map to o, with correct padding
return o}   //return

• That's an nice way to separate statements! It's certainly much more readable than putting everything on one line and separating them with semicolons. If I may offer my suggestion: you could save a byte by moving your i++ from the for loop to the last place it's used, in this case c=i++>r-2?"X":c. – ETHproductions Aug 6 '15 at 18:50
• Also, since you're using ES6, I would suggest using v[0].repeat(+v.slice(1)) in place of Array(v.slice(1)- -1).join(v[0]), and " ".repeat(j-p-1) in place of Array(j-p).join(" "), saving 11 bytes overall. I think you could also place F='forEach' at the beginning of the function, then change each .forEach from there on to [F], saving another 4. – ETHproductions Aug 6 '15 at 18:58
• Try using .map instead of .forEach. It's so short you should not even shorten it to F – edc65 Aug 7 '15 at 13:29
• @UndefinedFunction you might want to use shorthands for the ifs, it might help if you decrement variables at the same time also – Downgoat Aug 7 '15 at 21:38
• If my understanding is correct, does q=x=y=2e3 mean that the output would be incorrect if I did, say, W9999? – Sp3000 Aug 8 '15 at 5:53

# PHP, 431 417 bytes

$g=explode(',',$argv[1]);$x=$y=$a=$b=$c=$d=$e=$f=0;
foreach($g as$i=>$h){list($k,$l,$m)=
['N'=>[-1,0,'^'],'E'=>[0,1,'>'],'S'=>[1,0,'v'],'W'=>[0,-1,'<']][$h[0]]; for($s=substr($h,1);$s--;){$z[$f=$y][$e=$x]=$m;$y+=$k;$x+=$l;}
if($i==count($g)-1){$x=$e;$y=$f;}
$a=min($a,$x);$b=max($b,$x);$c=min($c,$y);$d=max($d,$y);
}$z[$y][$x]='X';for($y=$c;$y<=$d;$y++)
{$o='';for($x=$a;$x<=$b;$x++)$o.=$z[$y][$x]?:' ';echo rtrim($o)."\n";}  Put it into a file (treasure.php), remove the indentation, join the lines (it is wrapped here for readability), put the <?php marker at the beginning of the file (not displayed here as it is technically not a part of the program). Example of execution: $ php -d error_reporting=0 treasure.php E2,N4,E5,S2,W1,S3
>>>>>v
^    v
^   v<
^   v
>>^   X
$ The option -d error_reporting=0 is needed to suppress notices about values not found at specified indices in $z.

Update:

While I was preparing the ungolfed version of the code for posting I discovered it contained two unneeded assignments (12 bytes) and a whitespace that can be removed(as$i); also, by replacing a while with a for loop and squeezing an assignment into it (not possible using the while loop) I saved another byte. • I would love to see an ungolfed version. – Lars Ebert Aug 10 '15 at 8:03 • @LarsEbert I updated the answer with a link to the ungolfed code. I checked your solution now (didn't do it before); we have basically used the same algorithm. Yours handles the last step better than mine. I can strip 25 more bytes if I implement $count --;. – axiac Aug 10 '15 at 12:28
• $argn save 3 Bytes chop save 1 Byte "X" -> X use constants save more bytes – Jörg Hülsermann Jul 12 '17 at 12:14 • @JörgHülsermann I don't get the $argn hint. I am aware of the "X"->X trick but I probably forgot about it when I wrote this solution. I write PHP code since 2002 but until today I didn't notice that PHP provides the chop() function. Thank you for this hint. – axiac Jul 12 '17 at 12:24

# Perl, 702613546474439338 260 bytes

Thanks to Dom Hastings for his help and his supergolfed version.
The code uses a 2D array.

Version by Dom Hastings:

$x=$y=$a=$b=99;map{/^./;$a=($c=$x)<$a?$x:$a,$A=$x>$A?$x:$A,$b=($C=$y)<$b?$y:$b,$B=$y>$B?$y:$B,$q[$c][$C]={split'','W<E>N^Sv'}->{$&},$x+={W,-1,E,1}->{$&},$y+={N,-1,S,1}->{$&}for 1..$'}split',',pop;$q[$c][$C]=X;for$y($b..$B){print$q[$_][$y]||$"for$a..$A;print$/}


My lesser golfed version of 338 bytes (for reference):

@m=split(',',pop);$x=$y=$a=$b=99;map{($d,$s)=/^(.)(.+)$/;for(1..$s){$c=$x;$C=$y;if($x<$a){$a=$x}if($x>$A){$A=$x}if($y<$b){$b=$y}if($y>$B){$B=$y}if($d eq"W"){$r="<";$x--}if($d eq"E"){$r=">";$x++}if($d eq"N"){$r="^";$y--}if($d eq"S"){$r=v;$y++}$q[$c][$C]=$r}}@m;$q[$c][$C]=X;for$y($b..$B){for$x($a..$A){$t=$q[$x][$y];print$t?$t:$"}print$/}  Test $ perl piratemap_golf.pl E4,N3,W6,S10,W1,S1,E5,N1,W2,N6,E6,N5,W10,S1,E2
v<<<<<<<<<<
>Xv<<<<<< ^
v     ^ ^
v     ^ ^
v >>>>^ ^
v >>>>>>^
v ^
v ^
v ^
v ^
v ^
v< ^<<
>>>>>^

• If you aren't using use strict;, you don't need all the mys, which will save you at least a few bytes. Also == is shorter than eq since the latter requires spaces. – Alex A. Aug 6 '15 at 15:50
• If I'm not mistaken, you're only calling $m once, so rather than storing the command line argument as a variable, you can call it directly in split, i.e. @m=split(',',$ARGV[0]). – Alex A. Aug 6 '15 at 22:52
• Hey @LukStorms, glad to see more Perl golfers! A few things to help save some bytes! Your $d and $s variables can be grabbed using regex to save you some bytes ($d,$s)=/^(.)(.+)$/, and all foreachs can be for (as they're the same. You might also be able to save some chars replacing some of these with map{...}@x since you can ignore the parens around the iterated item (this works well if you have to contain other loops). If you're using $ARGV[0] you can replace that with pop, but if you use the script as in perl script.pl <<< "text" you can use <> instead! – Dom Hastings Aug 9 '15 at 6:58
• If you want to keep the script using args, you can use pop to save a couple. Instead of use Swtich and the switch/case statements, you could do individual checks which might save you bytes. Something like $y-="N"eq$d will work as well (since true 1 and false is ''). Often, you can have words as barewords, so $y-=N eq$d will work! There are some magic variables you can use to save bytes, $/ is '\n' and $" is ' ', but sometimes a literal newline might help save a char too. Another (dirty!) trick is multiple assigns to save a few more, as in $a=0;$b=0; can be $a=$b=0. – Dom Hastings Aug 9 '15 at 7:06
• Just a few more, I promise. I hope this is the kinda info you're after! Missing off parens on function calls is a pretty standard change, so substr($_,0,1) can be substr$_,0,1. Postfix for loops and if checks can be useful too as in for(@c){...} vs. ...for@c but you can't use ; in the code, you'd have to comma separate instead (which doesn't always work when you're calling functions). There are so many great tips also at codegolf.stackexchange.com/questions/5105/…. Good luck! – Dom Hastings Aug 9 '15 at 7:15

## Python 2, 394 bytes

Run the program then paste into standard input as e.g. "E2,N4,E5,S2,W1,S3"

m=input().split(',')
def f(x,y,h,o,s=[]):
for c in m:
for _ in range(int(c[1:])):
a,b,l={'E':(1,0,'>'),'W':(-1,0,'<'),'N':(0,1,'^'),'S':(0,-1,'v')}[c[0]]
if o:o[h-y][x]=l
s+=[(x,y)];x+=a;y+=b
if o:o[h-y+b][x-a]='X'
return s
p,q=zip(*f(*[0]*4))
w,h=max(p)-min(p),max(q)-min(q)
o=[[' ']*-~w for _ in range(h+1)]
f(-min(p),-min(q),h,o)
print'\n'.join(["".join(l).rstrip()for l in o])


This is not very optimized. First it runs through the input to record the path. Then it does some math to determine the right starting position and size of o. Then it runs through again and sets the appropriate entries of o as one of >v<^X. The main cleverness is in reusing the same function for both these traversals.

# XQuery 3.0, 498

declare variable $v external;let$m:=<v>{tokenize($v,',')!(for$j in(1 to xs:int(substring(.,2)))return<c>{translate(substring(.,1,1),'NESW','^>v<')}</c>)}</v>/c!(let $p:=./preceding-sibling::c return<p x="{count($p[.='>'])-count($p[.='<'])}" y="{count($p[.='v'])-count($p[.='^'])}">{if(./following::*)then .else'X'}</p>)for$y in(min(xs:int($m/@y))to max(xs:int($m/@y)))return string-join(for $x in(min(xs:int($m/@x))to max(xs:int($m/@x)))let$d:=$m[@x=$x and @y=$y]return if($d)then$d else' ','')  XQuery isn't often even slightly competitive, so this was fun. ## Ungolfed declare variable$v external;
let $map := <vector>{ tokenize($v,',') !
(for $j in (1 to xs:int(substring(.,2))) return <step>{ translate(substring(.,1,1),'NESW','^>v<') }</step> ) }</vector>/step ! (let$path_so_far := ./preceding-sibling::step
return <point
x="{ count($path_so_far[.='>']) - count($path_so_far[.='<']) }"
y="{ count($path_so_far[.='v']) - count($path_so_far[.='^']) }">
{if(./following::*) then string(.) else 'X'}
</point>)
for $y in (min(xs:int($map/@y)) to max(xs:int($map/@y))) return string-join( for$x in (min(xs:int($map/@x)) to max(xs:int($map/@x)))
let $d :=$map[@x=$x and @y=$y]
return if($d) then string($d) else ' '
,'')


# PHP, 496 514528

I tried my luck in PHP, the result is rather long, I still want to post it, just for fun.

function a($c){global$a,$b;$a[$b[1]][$b[0]]=$c;}$c=explode(',',$argv[1]);$a=[];$b=[0,0];foreach($c as$d=>$e){$f=substr($e,1);if($d==count($c)-1)$f--;for($i=0;$i++<$f;){if($e[0]==N){a('^');$b[1]--;}elseif($e[0]==E){a('>');$b[0]++;}elseif($e[0]==S){a(v);$b[1]++;}else{a('<');$b[0]--;}}}a(X);$d=$e=$f=$g=0;foreach($a as$y=>$h){$f=min($f,$y);$g=max($g,$y);foreach($h as$x=>$i){$d=min($d,$x);$e=max($e,$x);}}for($y=$f;$y<=$g;$y++){for($x=$d;$x<=$e;$x++)echo isset($a[$y][$x])?$a[$y][$x]:' ';echo " ";}  ## Ungolfed <?php function setInMap($char) {
global $map,$position;
$map[$position[1]][$position[0]] =$char;
}

$instructions = explode(',',$argv[1]);

$map = [];$position = [0, 0];

foreach($instructions as$index => $instruction) {$count = substr($instruction, 1); if($index === count($instructions) - 1) {$count--;
}
for($i = 0;$i < $count;$i++) {
if($instruction[0] === 'N') { setInMap('^');$position[1]--;
} elseif($instruction[0] === 'E') { setInMap('>');$position[0]++;
} elseif($instruction[0] === 'S') { setInMap('v');$position[1]++;
} else($instruction[0] === 'W') { setInMap('<');$position[0]--;
}
}
}
setInMap('X');

$minX =$maxX = $minY =$maxY = 0;
foreach($map as$y => $row) {$minY = min($minY,$y);
$maxY = max($maxY, $y); foreach($row as $x =>$cell) {
$minX = min($minX, $x);$maxX = max($maxX,$x);
}
}
for($y =$minY; $y <=$maxY; $y++) { for($x = $minX;$x <= $maxX;$x++) {
if(isset($map[$y][$x])) { echo$map[$y][$x];
} else {
echo ' ';
}
}
echo "\n";
}

?>

• Can be reduced a lot. For instance, you can just write for(;$i++<$f;), try to remove unnecessary brackets, use undefined constants (N) instead of strings ('N'), … – Blackhole Aug 7 '15 at 14:42
• Instead of ifs, try using trenary operators or logical ands. Also, it will help if you use PHP4.1 and use a GET array with the points. – Ismael Miguel Aug 8 '15 at 14:27

# JavaScript (ES6), 244 249 274

Leading spaces and newlines added for clarity and not counted, except the newline near the end in the join call, that is significant and counted.

Test running the snippet (being ECMAScript 6, Firefox and Safari 9 only)

F=m=>(
x=y=0,p=[],
m.replace(/\w(\d+)/g,(d,z)=>{
for(d='NWSE'.search(d[0]);
z--&&(p=~x?~y?p:[y=0,...p]:p.map(r=>' '+r,x=0));
p[u=y]=(w=r.slice(0,x))+'^<v>'[d]+(v=r.slice(x+1)),
d&1?x+=d-2:y+=d-1)
for(r=p[y]||'';!r[x];)r+=' ';
}),
p[u]=w+'X'+v,
p.join

)

// TEST

out=x=>O.innerHTML+=x.replace(/</g,'&lt;')+'\n'

;['S5,W2','N1,E1,S1,E1,N1,E1,S2','N1','N6,E6,S6,W5,N5,E4,S4,W3,N3,E2,S2,W1,N2','E21,S2','N12,E11,S12,W2,N4']
.forEach(a=>out(a+'\n'+F(a)+'\n'))
<pre id=O></pre>

# C, 557

main(_,a,minX,maxX,minY,maxY,x,y,v,dir,dist)char**a;char*v;{char o[998][999];for(y=0;y-998;++y){for(x=0;x-998;++x)o[y][x]=32;o[y][998]=0;}y=x=minY=minX=maxY=maxX=499;v=a[1];while(*v){dir=*v++;dist=atoi(v);while(*v&&*v!=44)v++;v+=!!*v;if(dir==78){while(dist--)o[y--][x]=94;if(y<minY)minY=y;y+=!*v;}if(dir==69){while(dist--)o[y][x++]=62;if(x>maxX)maxX=x;x-=!*v;}if(dir==83){while(dist--)o[y++][x]=86;if(y>maxY)maxY=y;y-=!*v;}if(dir==87){while(dist--)o[y][x--]=60;if(x<minX)minX=x;x+=!*v;}}o[y][x]=88;for(y=minY;y<=maxY;++y){o[y][maxX+1]=0;puts(o[y]+minX);}}


Ungolfed version:

#include <stdio.h>

#define MAX_WIDTH 998
#define MAX_HEIGHT 998

int main(int argc, char *argv[]) {
int minX,maxX,minY,maxY;
int x,y;
char output[MAX_HEIGHT][MAX_WIDTH+1];
char *v;

for (y=0; y<MAX_HEIGHT; ++y) {
for (x=0; x<MAX_WIDTH; ++x)
output[y][x] = ' ';
output[y][MAX_WIDTH] = 0;
}

x = minX = maxX = MAX_WIDTH/2;
y = minY = maxY = MAX_HEIGHT/2;

v = argv[1];
while (*v) {
char dir; int dist;
dir = *(v++);
dist = atoi(v);
while (*v && *v != ',') v++;
if (*v) v++;

switch (dir) {
case 'N':case 'n':
while (dist--) output[y--][x] = '^';
if (y < minY) minY = y;
if (!*v) y++;
break;
case 'E':case 'e':
while (dist--) output[y][x++] = '>';
if (x > maxX) maxX = x;
if (!*v) x--;
break;
case 'S':case 's':
while (dist--) output[y++][x] = 'v';
if (y > maxY) maxY = y;
if (!*v) y--;
break;
case 'W':case 'w':
while (dist--) output[y][x--] = '<';
if (x < minX) minX = x;
if (!*v) x++;
break;
}
}

output[y][x] = 'x';
for (y = minY; y <= maxY; ++y) {
output[y][maxX+1] = 0;
puts(output[y]+minX);
}

return 0;
}


Dynamic memory allocation isn't much harder, but malloc is far too long an identifier to be used in code golf. I feel like there should be some kind of PCG.h header legally auto-included for golfing in c, just to shorted some identifiers.

# Groovy, 359

c=args[0].split(',').collect{[it[0],it[1..-1]as int]}
m=[[]]
x=y=0
d=["N":["^",0,1],"S":["v",0,-1],"E":[">",1,0],"W":["<",-1,0]]
m.reverse().each{println it.collect{it?:" "}.join()}


# Common Lisp - 603

(lambda(s)(do((x 0)i(y 0)j(p 0)r(q 0)(g(mapcar(lambda(x)(,(aref x 0),(parse-integer x :start 1)))(split-sequence:split-sequence #\, s))(cdr g))c)((not g)(setf x 0 y 0)(dolist(e(stable-sort(sort r #'<= :key #'car)#'< :key #'cadr))(dotimes(_(-(cadr e)p y))(terpri)(incf y)(setf x 0))(dotimes(_(-(car e)q x))(princ" ")(incf x))(princ(caddr e))(incf x)))(case(caar g)(#\N(setf i 0 j -1 c #\^))(#\E(setf i 1 j 0 c #\>))(#\W(setf i -1 j 0 c #\<))(#\S(setf i 0 j 1 c #\v)))(dotimes(_(-(cadar g)(if(cdr g)0 1)))(push(,x,y,c)r)(incf x i)(incf y j))(setf q(min q x)p(min p y))(unless(cdr g)(push(,x,y #\X)r))))


Array-free implementation: prints from top to bottom, from left to right.

• Parse and expands directions into a trace of (x y char) elements:

The simple "N3" input produces ((0 0 #\^) (0 -1 #\^) (0 -2 #\X))

• Also, compute the minimal x and y
• Sort the resulting trace by y first and then by x
• Iterate over the sorted list while moving cursor

1. Add newlines and spaces to move current cursor at right position
2. When at position x - minx, y - miny, print the desired character

### Examples

(loop for input in  '("N6,E6,S6,W5,N5,E4,S4,W3,N3,E2,S2,W1,N2"
"N1,E1,S1,E1,N1,E1,S2"
"N12,E11,S12,W2,N4")
do (fresh-line)
(terpri)
(funcall *fun* input))


Result:

>>>>>>v
^>>>>vv
^^>>vvv
^^^Xvvv
^^^^<vv
^^^<<<v
^^<<<<<

>v>v
^>^X

>>>>>>>>>>>v
^          v
^          v
^          v
^          v
^          v
^          v
^          v
^          v
^        X v
^        ^ v
^        ^ v
^        ^<<


# CoffeeScript, 303  285 bytes

Y=(s)->o=[];t=l=x=y=0;q='';q+=s[0]for[1..s[1..]]for s in s.split ',';q=q[..-2];(i='NWSE'.search c;(o[y]?=[])[x]='^<v>'[i];j=(i&2)-1;x+=j*(i&1);y+=j*(!(i&1));y<t&&t=y;x<l&&l=x)for c in q;(o[y]?=[])[x]='X';((o[y][x]||' 'for x in[l...o[y].length]).join ''for y in[t...o.length]).join '\n'


<script src="http://coffeescript.org/extras/coffee-script.js"></script>
<script type="text/coffeescript">
Y=(s)->o=[];t=l=x=y=0;q='';q+=s[0]for[1..s[1..]]for s in s.split ',';q=q[..-2];(i='NWSE'.search c;(o[y]?=[])[x]='^<v>'[i];j=(i&2)-1;x+=j*(i&1);y+=j*(!(i&1));y<t&&t=y;x<l&&l=x)for c in q;(o[y]?=[])[x]='X';((o[y][x]||' 'for x in[l...o[y].length]).join ''for y in[t...o.length]).join '\n'

#------------------
document.write '<pre>'
z=[
'E2,N4,E5,S2,W1,S3'
'S5,W2'
'N1,E1,S1,E1,N1,E1,S2'
'N1'
'N6,E6,S6,W5,N5,E4,S4,W3,N3,E2,S2,W1,N2'
'E21,S2'
'N12,E11,S12,W2,N4'
]
for w in z
document.write "\n\n#{w}\n"
document.write Y(w).replace /</g,'&lt;'

</script>

# Jelly, 61 60 bytes

“ESWN”iⱮo)µịØ.,U;N$¤Wẋɗ/€Ẏḣ1;Ɗ+\ṖZṂ€ạƊ‘ŒṬ€a"Œṙ-0¦$Sị“>v<^X ”


Try it online!

A monadic link taking as its argument the directions as a list of lists, each of which has the compass direction character as its first member and the number of moves as an integer as its second. Returns a list of Jelly strings representing the map.

# Charcoal, 32 bytes

ＷＳ«≔⌕ENW§ι⁰θ✳⊗θ×§>^<vθΣι»Ｍ✳⁺⊗θ⁴X
`

Try it online!

Input taken as a multiline string.

Link is to verbose version of code.

Golfed from 43 bytes with Neil's help.