Blackbeard was an English pirate of the early 18th century. Although he was known for looting and taking ships, he commanded his vessels with the permission of their crews. There are no accounts of him ever harming or murdering his captives.

This challenge is in honor of the infamous Blackbeard and inspired by International Talk Like a Pirate Day (September 19). It is also the inverse of this challenge by Pyrrha.

The Challenge

Create a program that takes a treasure map as an input (composed of the characters listed below), and outputs it's directions.


All inputs will consist of v, >, <, ^, whitespace, and a single X.

You can assume the following:

  • the map will never loop or cross itself

  • the starting arrow will always be the bottommost character in the leftmost column

  • there will always be a treasure (X)

A sample input is shown below.

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


The output should be a ", "-delimited string of directions. Below is the correct output from the above map.

E2, N6, E2, S4, E4, N4, E6, S2, W2, S2, E2

A single trailing newline or space is permitted.



E15, S3, E4

^     v
^     >>>>X

N2, E6, S2, E4



^     v
^     v

N2, E6, S4, E6


E1, N3




E5, N3, W5, S2, E4, N1, W3

Happy International Talk Like a Pirate Day!

  • \$\begingroup\$ You may want to include an example where there's more than one right-pointing arrow in the left-most column, i.e. where the path loops back into the first column. In that case, it's slightly tricky to identify the start of the path. \$\endgroup\$ – Reto Koradi Sep 19 '15 at 18:38
  • \$\begingroup\$ I've added an example per your request. I've also added the detail that the starting character will be the bottommost of the column. @RetoKoradi \$\endgroup\$ – Zach Gates Sep 19 '15 at 18:43
  • \$\begingroup\$ Since I raised some concern in the related question about the length of the last segment, I'll nitpick once again and say that this is not exactly the reverse question here. Someone is trying to trick the pirates once again, me thinks. \$\endgroup\$ – coredump Sep 19 '15 at 19:41
  • \$\begingroup\$ The only difference is the step count of the last direction. @coredump At least, as far as I can tell. \$\endgroup\$ – Zach Gates Sep 19 '15 at 19:44
  • 1
    \$\begingroup\$ @ZachGates Yes, exactly (and just to be clear, I am not saying that the question should be modified, it is good as it currently is). \$\endgroup\$ – coredump Sep 19 '15 at 19:55

CJam, 78 bytes

qN/_:,$W=:Tf{Se]}s:U,T-{_U="><^vX"#"1+'E1-'WT-'NT+'S0"4/=~_@\}g;;]e`{(+}%", "*

Try it online.


The main idea here is to find the longest line (we'll call this length T), then pad all lines to the same length and concatenate them (this new string is U). This way only a single counter is needed to move in the map. Adding/subtracting 1 means moving to the right/left in the same line, adding/subtracting T means moving down/up one line.

qN/    e# Split the input on newlines
_:,    e# Push a list of the line lengths
$W=:T  e# Grab the maximum length and assign to T
f{Se]} e# Right-pad each line with spaces to length T
s:U    e# Concatenate lines and assign to U

Now it's time to set up the loop.

,T-    e# Push len(U) - T
       e# i.e. position of first char of the last line
{...}g e# Do-while loop
       e# Pops condition at the end of each iteration

The loop body uses a lookup table and eval to choose what to do. At the beginning of each iteration, the top stack element is the current position. Underneath it there are all the processed NSWE directions. At the end of the iteration, the new direction is placed underneath the position and a copy of it is used as the condition for the loop. Non-zero characters are truthy. When an X is encountered, 0 is pushed as the direction, terminating the loop.

_U=      e# Push the character in the current position
"><^vx"# e# Find the index in "><^Vx"
"..."4/  e# Push the string and split every 4 chars
         e# This pushes the following list:
         e# [0] (index '>'): "1+'E" pos + 1, push 'E'
         e# [1] (index '<'): "1-'W" pos - 1, push 'W'
         e# [2] (index '^'): "T-'N" pos - T, push 'N'
         e# [3] (index 'v'): "T+'S" pos + T, push 'S'
         e# [4] (index 'X'): "0"    push 0
=~       e# Get element at index and eval
_@\      e# From stack: [old_directions position new_direction]
         e# To stack: [old_directions new_direction position new_direction]
         e# (You could also use \1$)
         e# new_direction becomes the while condition and is popped off

Now the stack looks like this: [directions 0 position]. Let's generate the output.

;;    e# Pop position and 0 off the stack
]     e# Wrap directions in a list
e`    e# Run length encode directions
      e# Each element is [num_repetitions character]
{     e# For each element:
 (+   e#   Swap num_repetitions and character
}%    e# End of map (wraps in list)
", "* e# Join by comma and space

CJam, 86 bytes

qN/_,{1$=cS-},W=0{_3$3$==_'X-}{"^v<>"#_"NSWE"=L\+:L;"\(\ \)\ ( )"S/=~}w];Le`{(+}%", "*

Try it online


qN/     Get input and split into rows.
_,      Calculate number of rows.
{       Loop over row indices.
  1$=     Get row at the index.
  c       Get first character.
  S-      Compare with space.
},      End of filter. The result is a list of row indices that do not start with space.
W=      Get last one. This is the row index of the start character.
0       Column number of start position. Ready to start tracing now.
{       Start of condition in main tracing loop.
  _3$3$   Copy map and current position.
  ==      Extract character at current position.
  _'X-    Check if it's the end character `X.
}       End of loop condition.
{       Start of loop body. Move to next character.
  "^v<>"  List of directions.
  #       Find character at current position in list of directions.
  _       Copy direction index.
  "NSWE"  Matching direction letters.
  =       Look up direction letter.
  L\+:L;  Append it to directions stored in variable L.
  "\(\ \)\ ( )"
          Space separated list of commands needed to move to next position for each of
          the 4 possible directions.
  S/      Split it at spaces.
  =       Extract the commands for the current direction.
  ~       Evaluate it.
}w      End of while loop for tracking.
];      Discard stack content. The path was stored in variable L.
Le`     Get list of directions in variable L, and RLE it.
{       Loop over the RLE entries.
  (+      Swap from [length character] to [character length].
}%      End of loop over RLE entries.
", "*   Join them with commas.

Javascript (ES6), 239 bytes

`,b.reverse().some((c,d)=>c[0]!=' '&&((e=d)||1)),j=[],eval("for(g=b[e][f=0];b[e][f]!='X';g=b[e][f],j.push('NSEW'['^v><'.indexOf(i)]+h))for(h=0;g==b[e][f];e+=((i=b[e][f])=='^')-(i=='v'),f+=(i=='>')-(i=='<'),h++);j.join`, `"))


    b = a.split('\n'),
    // loops through list from bottom to find arrow
        (c, d)=>
            // if the leftmost character is not a space, saves the index and exit
            // the loop
            // in case d == 0, the ||1 makes sure the loop is exited
            c[0] != ' ' && ((e = d) || 1)
    j = [], // array that will hold the instructions
    eval("  // uses eval to allow a for loop in a lambda without 'return' and {}

        // loops through all sequences of the same character
        // e is the first coordinate of the current character being analyzed
        // f is the second coordinate
        // defines g as the character repeated in the sequence
        // operates on reversed b to avoid using a second reverse
        // flips ^ and v to compensate

        for(g = b[e][f = 0];
            b[e][f] != 'X'; // keep finding sequences until it finds the X
            g = b[e][f],    // update the sequence character when it hits the start of a
                            // new sequence
            j.push('NSEW'['^v><'.indexOf(i)] + h)) // find the direction the sequence is
                                                   // pointing to and add the
                                                   // instruction to j

            // loops through a single sequence until it hits the next one
            // counts the length in h
            for(h = 0;
                g == b[e][f]; // loops until there is a character that isn't part of
                              // the sequence
                // updates e and f based on which direction the sequence is pointing
                // sets them so that b[e][f] is now the character being pointed toward
                e += ((i = b[e][f]) == '^') - (i == 'v'),
                f += (i == '>') - (i == '<'),
                // increments the length counter h for each character of the sequence

            // return a comma separated string of the instructions
            j.join`, `

JavaScript(ES6), 189

Test running the snippet below in an EcmaScript 6 compliant browser.

`).map((r,i)=>r[0]>' '?y=i:0);for(x=o=l=k=p=0;p!='X';++l,y+=(k==1)-(k<1),x+=(k>2)-(k==2))c=m[y][x],c!=p?(o+=', '+'NSWE'[k]+l,l=0):0,k='^v<>'.search(p=c);alert(o.slice(7))}

// Testable version, no output but return (same size)
`).map((r,i)=>r[0]>' '?y=i:0);for(x=o=l=k=p=0;p!='X';++l,y+=(k==1)-(k<1),x+=(k>2)-(k==2))c=m[y][x],c!=p?(o+=', '+'NSWE'[k]+l,l=0):0,k='^v<>'.search(p=c);return o.slice(7)}

out = x => O.innerHTML += x+'\n';

test = 
               >>>>X`,'E15, S3, E4']
^     v
^     >>>>X`,'N2, E6, S2, E4']
^     v
^     v
      >>>>>>X`,'N2, E6, S4, E6']
,[` X
>^`,'E1, N3']
>>>>>^`,'E5, N3, W5, S2, E4, N1, W3']];

  var k = t[1];
  var r = f(t[0]);
  out('Test ' + (k==r ? 'OK' : 'Fail')
      +'\n'+t[0]+'\nResult: '+r
      +'\nCheck:  '+k+'\n');
<pre id=O></pre>

Less golfed

  m=m.split('\n'); // split in rows

  x = 0; // Starting column is 0
  m.forEach( (r,i) => r[0]>' '? y=i : 0); // find starting row

  o = 0; // output string
  p = 0; // preceding character
  l = 0; //  sequence length (this starting value is useless as will be cutted at last step)
  k = 0; //  direction (this starting value is useless as will be cutted at last step)
  while(p != 'X') // loop until X found
    c = m[y][x]; // current character in c
    if (c != p) // changing direction
      o +=', '+'NSWE'[k]+l; // add current direction and length to output
      l = 0 // reset length
    p = c;
    k = '^v<>'.search(c); // get new current direction
    // (the special character ^ is purposedly in first position)
    ++l; // increase sequence length
    y += (k==1)-(k<1); // change y depending on direction
    x += (k>2)-(k==2); // change x depending on direction
  alert(o.slice(7)); // output o, cutting the useless first part

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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