51
\$\begingroup\$

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
^        ^<<
\$\endgroup\$
13
  • 1
    \$\begingroup\$ Are we allowed to have trailing spaces on each line? Is the number always going to be less than ten? \$\endgroup\$
    – Downgoat
    Commented Aug 6, 2015 at 3:59
  • 9
    \$\begingroup\$ 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. \$\endgroup\$
    – coredump
    Commented Aug 6, 2015 at 7:39
  • 6
    \$\begingroup\$ @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. \$\endgroup\$
    – absinthe
    Commented Aug 6, 2015 at 7:58
  • 4
    \$\begingroup\$ @jpmc26 Well, these pirates don't know much of the alphabet... they spent the last few years at C :) \$\endgroup\$
    – absinthe
    Commented Aug 7, 2015 at 10:39
  • 4
    \$\begingroup\$ The fourth example is just trolling the pirates... \$\endgroup\$
    – justhalf
    Commented Aug 8, 2015 at 3:12

17 Answers 17

20
\$\begingroup\$

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, 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)

\$\endgroup\$
4
  • 1
    \$\begingroup\$ (d + 1 & 2) - 1 \$\endgroup\$
    – joriki
    Commented Aug 6, 2015 at 17:50
  • 1
    \$\begingroup\$ @joriki Ah that's a nice expression - thanks! \$\endgroup\$
    – Sp3000
    Commented Aug 6, 2015 at 23:50
  • 2
    \$\begingroup\$ 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".) \$\endgroup\$
    – joriki
    Commented Aug 6, 2015 at 23:55
  • 3
    \$\begingroup\$ @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)! \$\endgroup\$
    – Sp3000
    Commented Aug 7, 2015 at 0:17
14
\$\begingroup\$

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
\$\endgroup\$
11
  • 3
    \$\begingroup\$ 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. \$\endgroup\$ Commented Aug 6, 2015 at 18:50
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Commented Aug 6, 2015 at 18:58
  • 1
    \$\begingroup\$ Try using .map instead of .forEach. It's so short you should not even shorten it to F \$\endgroup\$
    – edc65
    Commented Aug 7, 2015 at 13:29
  • 1
    \$\begingroup\$ @UndefinedFunction you might want to use shorthands for the ifs, it might help if you decrement variables at the same time also \$\endgroup\$
    – Downgoat
    Commented Aug 7, 2015 at 21:38
  • 1
    \$\begingroup\$ If my understanding is correct, does q=x=y=2e3 mean that the output would be incorrect if I did, say, W9999? \$\endgroup\$
    – Sp3000
    Commented Aug 8, 2015 at 5:53
8
\$\begingroup\$

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
\$\endgroup\$
7
\$\begingroup\$

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.

\$\endgroup\$
4
  • \$\begingroup\$ I would love to see an ungolfed version. \$\endgroup\$ Commented Aug 10, 2015 at 8:03
  • 1
    \$\begingroup\$ @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 --;. \$\endgroup\$
    – axiac
    Commented Aug 10, 2015 at 12:28
  • \$\begingroup\$ $argn save 3 Bytes chop save 1 Byte "X" -> X use constants save more bytes \$\endgroup\$ Commented Jul 12, 2017 at 12:14
  • \$\begingroup\$ @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. \$\endgroup\$
    – axiac
    Commented Jul 12, 2017 at 12:24
7
\$\begingroup\$

Perl, 702 613 546 474 439 338 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< ^<<
 >>>>>^
\$\endgroup\$
13
  • 3
    \$\begingroup\$ 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. \$\endgroup\$
    – Alex A.
    Commented Aug 6, 2015 at 15:50
  • 1
    \$\begingroup\$ 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]). \$\endgroup\$
    – Alex A.
    Commented Aug 6, 2015 at 22:52
  • 1
    \$\begingroup\$ 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! \$\endgroup\$ Commented Aug 9, 2015 at 6:58
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Commented Aug 9, 2015 at 7:06
  • 1
    \$\begingroup\$ 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! \$\endgroup\$ Commented Aug 9, 2015 at 7:15
5
\$\begingroup\$

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.

\$\endgroup\$
4
\$\begingroup\$

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 ' '
    ,'')
\$\endgroup\$
4
\$\begingroup\$

PHP, 496 514 528

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";
    }

?>
\$\endgroup\$
2
  • 1
    \$\begingroup\$ 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'), … \$\endgroup\$
    – Blackhole
    Commented Aug 7, 2015 at 14:42
  • 1
    \$\begingroup\$ 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. \$\endgroup\$ Commented Aug 8, 2015 at 14:27
3
\$\begingroup\$

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>

\$\endgroup\$
2
\$\begingroup\$

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.

\$\endgroup\$
1
2
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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]]
c.each{z->(1..z[1]).each{if(x<0){m*.add(0," ");x=0};if(y<0){m.add(0,[]);y=0};m[y]=m[y]?:[];m[y][x]=d[z[0]][0];if(c.last()==z&&it==z[1])m[y][x]='X';y+=d[z[0]][2];x+=d[z[0]][1]}}
m.reverse().each{println it.collect{it?:" "}.join()}
\$\endgroup\$
1
\$\begingroup\$

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
^        ^<<
\$\endgroup\$
1
\$\begingroup\$

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>

\$\endgroup\$
1
\$\begingroup\$

Charcoal, 32 bytes

WS«≔⌕ENW§ι⁰θ✳⊗θק>^<vθΣι»M✳⁺⊗θ⁴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.

\$\endgroup\$
1
\$\begingroup\$

Vyxal, 390 bitsv2, 48.75 bytes

⌐⌊›?Ǎf`NESW`kd√:£Ŀ:ṅ¥`^>v<`ĿfW∩vǔṫ⁽‹ẇwJ:tt\X1"pwJ⟑÷ø∧

Try it Online!

Bitstring:

000110000111010100000001000100000101011110110001100110111110111101111011001110011001001000011101100001111110011101110101110100100100110111110011000000010110101101110001011010011010000010011111100110010101010011010000110000010100000110001000100010100110010101010000110101110111100001000011001100000001110101110111000000101111101011000110101100100110111100111111000101110011110111110101001011
⌐⌊›?Ǎf`NESW`kd√:£Ŀ:ṅ¥`^>v<`ĿfW∩vǔṫ⁽‹ẇwJ:tt\X1"pwJ⟑÷ø∧­⁡​‎‎⁡⁠⁡‏⁠‎⁡⁠⁢‏⁠‎⁡⁠⁣‏‏​⁡⁠⁡‌⁢​‎‎⁡⁠⁤‏⁠‎⁡⁠⁢⁡‏⁠‎⁡⁠⁢⁢‏‏​⁡⁠⁡‌⁣​‎‎⁡⁠⁢⁣‏⁠‎⁡⁠⁢⁤‏⁠‎⁡⁠⁣⁡‏⁠‎⁡⁠⁣⁢‏⁠‎⁡⁠⁣⁣‏⁠‎⁡⁠⁣⁤‏⁠⁠‏​⁡⁠⁡‌⁤​‎‎⁡⁠⁤⁡‏⁠‎⁡⁠⁤⁢‏⁠‎⁡⁠⁤⁣‏‏​⁡⁠⁡‌⁢⁡​‎‎⁡⁠⁤⁤‏⁠‎⁡⁠⁢⁡⁡‏‏​⁡⁠⁡‌⁢⁢​‎‎⁡⁠⁢⁡⁢‏‏​⁡⁠⁡‌⁢⁣​‎‎⁡⁠⁢⁡⁣‏⁠‎⁡⁠⁢⁡⁤‏⁠‎⁡⁠⁢⁢⁡‏⁠‏​⁡⁠⁡‌⁢⁤​‎‎⁡⁠⁢⁢⁢‏⁠‎⁡⁠⁢⁢⁣‏⁠‎⁡⁠⁢⁢⁤‏⁠‎⁡⁠⁢⁣⁡‏⁠‎⁡⁠⁢⁣⁢‏⁠‎⁡⁠⁢⁣⁣‏⁠‎⁡⁠⁢⁣⁤‏‏​⁡⁠⁡‌⁣⁡​‎‎⁡⁠⁢⁤⁡‏⁠‎⁡⁠⁢⁤⁢‏⁠‎⁡⁠⁢⁤⁣‏‏​⁡⁠⁡‌⁣⁢​‎‎⁡⁠⁢⁤⁤‏⁠‎⁡⁠⁣⁡⁡‏‏​⁡⁠⁡‌⁣⁣​‎‎⁡⁠⁣⁡⁢‏⁠‎⁡⁠⁣⁡⁣‏⁠‎⁡⁠⁣⁡⁤‏⁠‎⁡⁠⁣⁢⁡‏⁠‎⁡⁠⁣⁢⁢‏⁠‎⁡⁠⁣⁢⁣‏‏​⁡⁠⁡‌⁣⁤​‎‎⁡⁠⁣⁢⁤‏⁠‎⁡⁠⁣⁣⁡‏⁠‎⁡⁠⁣⁣⁢‏‏​⁡⁠⁡‌⁤⁡​‎‎⁡⁠⁣⁣⁣‏⁠‎⁡⁠⁣⁣⁤‏⁠‎⁡⁠⁣⁤⁡‏⁠‎⁡⁠⁣⁤⁢‏‏​⁡⁠⁡‌⁤⁢​‎‎⁡⁠⁣⁤⁣‏⁠‎⁡⁠⁣⁤⁤‏⁠‎⁡⁠⁤⁡⁡‏‏​⁡⁠⁡‌⁤⁣​‎‎⁡⁠⁤⁡⁢‏⁠‎⁡⁠⁤⁡⁣‏⁠‎⁡⁠⁤⁡⁤‏⁠‎⁡⁠⁤⁢⁡‏‏​⁡⁠⁡‌­
⌐⌊›                                                    # ‎⁡split by comma, get number part, increment
   ?Ǎf                                                 # ‎⁢get directions and flatten
      `NESW`                                           # ‎⁣Directions
            kd√                                        # ‎⁤"02468"
               :£                                      # ‎⁢⁡duplicate and push to register
                 Ŀ                                     # ‎⁢⁢transliterate directions to corresponding canvas numbers
                  :ṅ¥                                  # ‎⁢⁣duplicate, join by nothing, push register
                     `^>v<`Ŀ                           # ‎⁢⁤Transliterate each direction to corresponding arrow text
                            fW∩                        # ‎⁣⁡flatten, wrap the stack and transpose
                               vǔ                      # ‎⁣⁢‎⁣⁤rotate each sublist to order [text, length, direction] for canvas
                                 ṫ⁽‹ẇwJ                # ‎⁣⁣decrement the middle of the last triplet 
                                       :tt             # ‎⁣⁤get the very last direction
                                          \X1"         # ‎⁤⁡["X", 1]
                                              pwJ      # ‎⁤⁢prepend it to the direction and put it back (Draws the X) 
                                                 ⟑÷ø∧  # ‎⁤⁣draw each triplet to the canvas
💎

Created with the help of Luminespire.

\$\endgroup\$
0
\$\begingroup\$

05AB1E, 37 35 (or 42 38) bytes

38 bytes: Input as comma-separated string, as mentioned in the challenge description:

',¡εć"NESW"skxs"^>v<"è)}øR`ā¤Ê+ŠΛ1'X0Λ

Try it online.

35 bytes: Input as a list of pairs of direction-character and distant-integer, by removing the first three bytes ',¡:
Try it online.

Explanation (of the strict I/O program):

',¡           '# Split the (implicit) input-string on commas
   ε           # Map over each string:
    ć          #  Extract its head; push distant and direction-char separately
     "NESW"sk  #  Get the 0-based index of the character in "NESW"
             x #  Double it (without popping)
     s         #  Swap so the index is at the top again
      "^>v<"è  #  Convert the index to the correct output character
     )         #  Wrap all three values into a list
    }ø         # After the map: zip/transpose the list of triplets; swapping rows/columns
      R        # Reverse the list of triplets
       `       # Pop and push all three lists separately to the stack
        ā      # Push a list in the range [1,length]
         ¤     # Push its last item (without popping the list)
          Ê    # Check for each that it's NOT equal to this last item
           +   # Add the values at the same positions
               # (so 1 is added to all distances, except the last one)
        Š      # Triple-swap the three lists on the stack
         Λ     # Canvas builtin with these three arguments
               # (which is output immediately)
1'X0Λ         '# And another Canvas builtin with arguments 1,"X",0
               # (which is done on top of the other Canvas and also output)

The Canvas builtin Λ takes three arguments:

  1. Length(s): which are the modified distances (+1 for every distance except the last one, since the Canvas builtin draws with overlapping corners)
  2. The characters we want to display
  3. The directions to draw in, which is in this case is \$0\$=North; \$2\$=South; \$4\$=South; \$6\$=West.

For example: input E2,N4,E5,S2,W1,S3 will for ',¡εć"NESW"skxs"^>v<"è)}øR`ā¤Ê+Š result in the three lists:

  1. Lengths: [3,5,6,3,2,3]
  2. Characters: [">","^",">","v","<","v"]
  3. Directions: [2,0,2,4,6,4]

And will print them as:

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

After which the 1'X0Λ will overwrite the final v with X.

See this 05AB1E tip of mine for an in-depth explanation of the Canvas builtin.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.