Yarr! A map to the hidden treasure!

50

8

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
^        ^<<

absinthe

Posted 2015-08-06T02:33:43.397

Reputation: 8 359

1Are we allowed to have trailing spaces on each line? Is the number always going to be less than ten? – Downgoat – 2015-08-06T03:59:00.113

@vihan You are not allowed to have trailing spaces on each line. The number is not always going to be less than ten. – absinthe – 2015-08-06T04:15:12.513

@Sp3000 The number of steps can be more than one digit. I added a new test case for more than one digit steps. – absinthe – 2015-08-06T05:57:23.470

@Sp3000 Leading spaces before lines are allowed (for some of the inputs, some of the lines need to be "padded" with spaces). Leading spaces after lines are not allowed. Leading spaces after or before the whole grid are allowed. – absinthe – 2015-08-06T06:19:29.550

9I 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 – 2015-08-06T07:39:08.147

6@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 – 2015-08-06T07:58:39.463

Is there any limit to how long a certain direction will be? – The_Basset_Hound – 2015-08-06T18:32:32.990

@BassetHound There's no limit, but you don't need to handle integer overflow or anything like that. – absinthe – 2015-08-07T01:53:11.053

What kind of pirates can't follow a compass? I don't think I'll be sailing with them anytime soon. – jpmc26 – 2015-08-07T08:33:57.583

4@jpmc26 Well, these pirates don't know much of the alphabet... they spent the last few years at C :) – absinthe – 2015-08-07T10:39:38.730

Can we store the output in a global variable after the function is called? (to avoid having to return) Sorry if there is a standard rule for that or something, I'm new at this... – jrich – 2015-08-07T23:24:38.267

4The fourth example is just trolling the pirates... – justhalf – 2015-08-08T03:12:20.490

1@UndefinedFunction No, you need to return a value. – absinthe – 2015-08-08T09:27:07.307

Answers

8

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

Ventero

Posted 2015-08-06T02:33:43.397

Reputation: 9 842

20

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)

Sp3000

Posted 2015-08-06T02:33:43.397

Reputation: 58 729

1(d + 1 & 2) - 1 – joriki – 2015-08-06T17:50:46.460

1@joriki Ah that's a nice expression - thanks! – Sp3000 – 2015-08-06T23:50:22.017

2Here'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 – 2015-08-06T23:55:18.457

3@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 – 2015-08-07T00:17:13.110

14

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

jrich

Posted 2015-08-06T02:33:43.397

Reputation: 3 898

3That'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 – 2015-08-06T18:50:59.067

1Also, 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 – 2015-08-06T18:58:21.837

@ETHproductions Thanks! It's down to 347! – jrich – 2015-08-06T19:19:58.230

Update: moving the F="forEach" declaration to the first place it is used saved another 2. – jrich – 2015-08-06T19:36:02.883

1Try using .map instead of .forEach. It's so short you should not even shorten it to F – edc65 – 2015-08-07T13:29:33.080

@edc65 Nice. Down to 317! Think it's about as golfed as it's going to get... – jrich – 2015-08-07T17:31:35.053

1@UndefinedFunction you might want to use shorthands for the ifs, it might help if you decrement variables at the same time also – Downgoat – 2015-08-07T21:38:37.200

@vihan Down to 263! I changed the ifs to ternary, but couldn't figure out how to shorten it by incrementing or decrementing variables at the same time. If you could point out how, that would be nice. Thanks! – jrich – 2015-08-07T23:07:59.013

1If my understanding is correct, does q=x=y=2e3 mean that the output would be incorrect if I did, say, W9999? – Sp3000 – 2015-08-08T05:53:54.933

@Sp3000 easily solved at no cost ... 2e5 ... 2e9 ? The output string is limited anyway – edc65 – 2015-08-08T11:13:47.457

@Sp3000 Correct, the program wouldn't work correctly in that case. However, as edc65 mentioned in theory this could be replaced with 9e9 at no cost. (I used 2e3 to make it run faster, although up to 9e5 seems to run reasonably quickly on my computer) – jrich – 2015-08-08T15:06:59.400

7

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.

axiac

Posted 2015-08-06T02:33:43.397

Reputation: 749

I would love to see an ungolfed version. – Lars Ebert – 2015-08-10T08:03:08.580

1@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 – 2015-08-10T12:28:56.570

$argn save 3 Bytes chop save 1 Byte "X" -> X use constants save more bytes – Jörg Hülsermann – 2017-07-12T12:14:45.877

@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 – 2017-07-12T12:24:13.207

7

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< ^<<
 >>>>>^

LukStorms

Posted 2015-08-06T02:33:43.397

Reputation: 1 776

3If 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. – 2015-08-06T15:50:05.850

Thanks Alex A. for the tips. That shaved some bytes off. A replacement with == didn't work, but the space after 'eq' was indeed removable. Also the initialization of the number variables to 0 wasn't needed either. – LukStorms – 2015-08-06T21:22:26.657

1If 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. – 2015-08-06T22:52:20.800

Thanks. In hindsight that was an obvious one that slipped in because of habbit. I now also removed the initialization of the array with the spaces, and instead default the undefined values to space when I print. – LukStorms – 2015-08-07T06:53:35.283

1Hey @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 – 2015-08-09T06:58:12.740

1If 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 – 2015-08-09T07:06:35.380

1

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 http://codegolf.stackexchange.com/questions/5105/tips-for-golfing-in-perl. Good luck!

– Dom Hastings – 2015-08-09T07:15:26.657

Thanks Dom for all those usefull tips. Can't believe how many bytes could still be shaved off like that. And I still have to look into that map and the postfix loop, but even then it trimmed down from 546 to 474 bytes! – LukStorms – 2015-08-10T09:57:35.587

I got rid of 101 bytes by assuming that pirates don't like to walk to far in north or west direction. So now the code doesn't have to go 2 times through the moves array. – LukStorms – 2015-08-11T10:17:39.373

1

@LukStorms I worked on golfing this a bit more as I was enjoying the golf action! I got it down to 262 still using your main routines/approach. I wanted to share with you to review and see if there were any helpful snippets that weren't included in the Perl tips thread or anything: https://gist.github.com/anonymous/956afef36c14efec0e85 Happy to remove this if it's not appropriate!

– Dom Hastings – 2015-08-12T12:29:17.583

Amazing. I thought I reached the limit of what could be done via the 2D array. And I had to ungolf your code just to grasp the tricks you used. Well done. I changed the post accordingly. – LukStorms – 2015-08-13T09:38:42.620

@Dom. Your version in the post has 2 less bytes now. Using a FOR instead of a MAP at the end turned out to be smaller in this case. – LukStorms – 2015-08-13T10:36:09.563

Awesome! I hope some of those techniques were useful! I'm glad to be able to share! I probably should have provided more explanation with each of them, sorry about that... I do hope it was interesting though! Good spot on for vs. map too! – Dom Hastings – 2015-08-13T10:42:13.677

5

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.

Alex L

Posted 2015-08-06T02:33:43.397

Reputation: 761

4

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

?>

Lars Ebert

Posted 2015-08-06T02:33:43.397

Reputation: 256

1Can 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 – 2015-08-07T14:42:44.013

1Instead 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 – 2015-08-08T14:27:57.060

4

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

Kniffler

Posted 2015-08-06T02:33:43.397

Reputation: 41

3

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>

edc65

Posted 2015-08-06T02:33:43.397

Reputation: 31 086

2

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.

LambdaBeta

Posted 2015-08-06T02:33:43.397

Reputation: 2 499

1

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()}

dbramwell

Posted 2015-08-06T02:33:43.397

Reputation: 201

1

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
^        ^<<

coredump

Posted 2015-08-06T02:33:43.397

Reputation: 6 292

1

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>

metalim

Posted 2015-08-06T02:33:43.397

Reputation: 523

0

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.

Nick Kennedy

Posted 2015-08-06T02:33:43.397

Reputation: 11 829