Where is the Arrow Pointing?

In this challenge, your goal is to follow an arrow and output the character it is pointing to.

Examples

Input:

d  S------+    b
|
|
c      +--->a


Output: a

Input:

S-----+---a->c
|
V
b


Output: b

The arrow is not pointing at c because it is divided by the a, meaning this path never leads to an arrow head.

Input:

a S      s
|      |
V      V
b      c


Output: b

Input:

d s<+S+--V
|||  Q
-++


Output: Q

This path starts at the S, goes down, to the right, goes up, to the right, then points down at the Q. Note that the path does not go straight from S to +.

Input:

d s-+   +-S  +--+
+-->b |  |  |
|  | +--+  |
+--+ A<----+


Output: A

Input:

S-----+
|     +-^
+---+->B
+---^


Output: B

Because the valid line will never lead to a whitespace character. The only line that doesn't lead to a whitespace character leads to a B

Challenge

The input will be a multi-line string in which you need to find the character the arrow is pointing at. There will only be one valid arrow. The valid arrow will only point to alphanumeric characters excluding S. A line will never overlap itself. e.g. -|-

• S (capital) represents where the arrow starts.
• - represents a horizontal line
• + represents a possible change in axis. A valid arrow will never start with a +.
• | represents a vertical line
• > < V ^ any of these represent the arrow head. These will never connect to a +.

There will only be one S in the string. The input will also be padded to be a rectangle (not necessarily a square).

• "This will never appear adjacent to a S." Should probably be reworded to "This will never be the first character of the arrow." (Because the Q example has a + adjacent to an S.) "+ represents a change in axis." might better be "+ represents a possible in change in axis." (Because the B example shows that you can move through a + without changing direction.) Otherwise, nice challenge. :) – Martin Ender Sep 15 '15 at 8:42
• I made a change to one of your test cases. I think that will make it harder to write programs that only happen to get the right answer, like mine did at first. – El'endia Starman Sep 18 '15 at 23:59
• Can the arrow head change direction, i.e ---^? In other words, if in B example, could the B stay in the first row? – edc65 Sep 20 '15 at 10:57
• @edc65 do you mean like in the Q example? – Downgoat Sep 20 '15 at 15:53
• An arrow head will not be connected to '+'. But could it be connected to 'S'? Is S>a valid ? – edc65 Sep 22 '15 at 8:26

JavaScript (ES6), 195 245 231 242 246 250

Edit4 Now, a single recursive function. Probably can't be golfed more

Edit3 Test for straight line and test for arrowhead merged in T function, S and H functions removed.

Edit2 Revised and longer :( after this this clarification

Edit Small improvements, cutting some char here and there, waiting for CJammers to step in

Test running the snippet below in an EcmaScript 6 compliant browser. (works on Firefox. Chrome still missing the spread operator ...)

f=(s,p=s[I='indexOf']S,w=m=[o=~s[I]
,1,-o,-1],q,v,r)=>m.some((d,i)=>{for(v=w,q=p;r=s[q+=d],r=='|-'[i&1];)v='+';return r=r==v?f(s,q,v):/\w/.test(r=s[q+m['^>V<'[I](r)]])&&r},s=[...s],s[p]=0)&&r

// Less golfed
// U: recursive scan function
U = (s, // input string or array
p = s.indexOfS, // current position, defult to position of S into string (at first call)
w = // char to compare looking for a '+', at first call default to garbage as you can't follow '+' near 'S'
m = [// m, global, offset array form movements.
o = ~s.indexOf\n, // o, global, row line +1 (negated)
1, -o, -1], // complete the values of m array
// m and o are assigned again and again at each recursive call, but that is harmless
// params working as local variables as the function is recursive
q,v,r) =>
{
s = [...s]; //convert to array, as I have to modify it while scanning
//the conversion is done again and again at each recursive call, but that is harmless
s[p] = 0; // make s[p] invalid to avoid bouncing back and forth

m.some( // for each direction, stop if endpoint found
(d,i ) => {
q = p; // start at current position, then will add the offset relative to current direction
v = w; // for each direction, assign the starting value that can be '+' or garbage at first call
// compare char at next position with the expected char based on direction (- for horiz, | for vertical)
// in m array, values at even positon are vertical movements, odds are horizontal
while (s[q+=d] == '|-'[i&1]) // while straight direction, follow it until it ends
v = '+'; // from now on '+' is allowed
r = s[q];
if (r == v) // check if found a '+'
r = U(s, q, v) // recursive call to check all directions from current position
else
{
r = s[q + m['^>V<'.indexOf(r)]], // pointed char in p (if arrowhead, else will return undefined)
r = /\w/.test(r) && r // if alphanumeric, then we found our result - else r = false
}
return r; // returning r to .some; if r is truthy, the .some function will stop
}
)
return r; // result if found, else undefined or null
}

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

// Using explicit newlines '\n' to ensure the padding to a rectangle
;[
['z<+S-+->a','a']
,['a<-+S>b','b']
,['S-+\n  |\n  V\n  a','a']
,['a S      s\n  |      |\n  V      V\n  b      c  ','b']
,['S-----+  \n|     +-^  \n+---+->B \n    +---^','B']
,['d s<+S+--V\n    |||  Q\n    -++    ','Q']
,['d s-+   +-S  +--+\n    +-->b |  |  |\n     |  | +--+  |\n     +--+ A<----+  ','A']
,['S-----+   \n|     +-^ \n+---+->B  \n    +---^ ','B']
].forEach(t=>{
r=f(t[0])
k=t[1]
out('Test '+(r==k?'OK':'Fail')+'\n'+t[0]+'\nResult: '+ r +'\nCheck: '+k+'\n')
})
<pre id=O></pre>

JavaScript 2016, 264 263 249 240 235 234 bytes

Run it in Firefox:

m=l=>{o=(q,e)=>q.indexOf(e)
s=[-1,1,c=~o(l,
),-c]
f(o(l,'S'))}


m(d s-+   +-S  +--+
+-->b |  |  |
|  | +--+  |
+--+ A<----+)


Scattered in some of my notes:

m=l=>{o=(q,e)=>q.indexOf(e) //shorthand for indexOf
s=[-1,1,c=o(l,
)+1,-c] // get all possible moves in 1d
w=[] // to keep track of the already-visited indexes
f=i=>{c=l[i] // current character
if(!w[i]&&c) // only continue if the index wasn't already visited and we aren't out of bounds
{r=l[i+s[o(a="<>^V",w[i]=c)]] // sets w[i] to a truthy value and maps the arrows to their corresponding moves in s. If c is an arrow, r is the character it's pointing to.
if(!r||r<"0"||r>"z"||r=="S"||alert(r)) // if r is an alphanumeric character that isn't r, show it and return. If not, continue.
for(j of (c!='+'?a:"")+"-|+") // iterate over all characters to make sure plusses are checked out last, which is necessary at the start.
for(k of s) // check out all possible moves
l[i+k]!=j|| // check if the move leads to the character we're looking for
~o(l[i]+j,k*k>1?"-":"|")|| // make sure the current nor that character goes against the direction of the move
f(i+k)}} // make the move, end of f
f(o(l,'S'))} // start at S

• You may save some bits by doing o = 'indexOf' and then doing q[o](e) when you want to use it. – Not that Charles Sep 22 '15 at 15:21
• Also, in code golf, for(;;) loops are usually most efficient. Might be wrong in this case, but try it out. – Not that Charles Sep 22 '15 at 15:22
• test case a<-+S->b I think it should give b only, as A valid arrow will never start with a + – edc65 Sep 23 '15 at 19:20
• Fixed that and turned f into a one-liner. I really liked your approach to this problem, by the way. – bopjesvla Sep 24 '15 at 2:09
• A side note: I really love array comprehension, but it's not in any version of EcmaScript standard anymore. I suggest to tag you anser JavaScript 2016 (stiil a valid and good answer, not problem with it) – edc65 Sep 24 '15 at 14:20

VBA Excel 2007, 894 bytes

Well this started out a lot better then it ended. I have a feeling my logic is flawed and i could have saved a tonne of bytes if i reordered some of my logic, But Too much time has been sunk into this already =P

The input for this is Column A of whatever sheet you are on. This method uses the fact that Excel has this nice grid and Breaks everything out so you can see what it is doing more clearly.

Sub m() is just taking the Copy pasted data from Column A and Breaking it out by char. If We allow a Modified input then if you preformat the maze into 1 char per cell you can save a few bytes by removing sub m()

Paste a Maze into Excel any size upto 99 rows by 27 char across. If you want bigger mazes its only 2 extra Bytes to increase the scope to 999 rows and ZZ columns

Also might need a judges call on whether a Excel Sheet is Valid "Standard Input" for a VBA answer. If not, Its pretty much impossible to give VBA Multi-line input VIA the Immediate window

To run this code just paste this code into a Excel module, Paste a maze into A1 and run sub j()

Sub m()
For k=1 To 99
u=Cells(k,1).Value
For i=1 To Len(u)
Cells(k,i+1).Value=Mid(u,i,1)
Next
Next
Columns(1).Delete
End Sub
Sub j()
m
With Range("A:Z").Find("S",,,,,,1)
h .row,.Column,0
End With
End Sub
Sub h(r,c,t)
If t<>1 Then l r-1,c,1,0,r
If t<>-1 Then l r+1,c,-1,0,r
If t<>2 Then l r,c-1,2,0,r
If t<>-2 Then l r,c+1,-2,0,r
End Sub
Sub l(r,c,y,p,i)
On Error GoTo w
q=Cells(r,c).Text
If q="V" Or q="^" Or q="<" Or q=">" Then
p=1
End If
f="[^V<>]"
If p=1 And q Like "[0-9A-UW-Za-z]" Then
MsgBox q: End
Else
If q="^" Then p=1: GoTo t
If q="V" Then p=1: GoTo g
If q="<" Then p=1: GoTo b
If q=">" Then p=1: GoTo n
Select Case y
Case 1
t:If q="|" Or q Like f Then l r-1,c,y,p,q
Case -1
g:If q="|" Or q Like f Then l r+1,c,y,p,q
Case 2
b:If q="-" Or q Like f Then l r,c-1,y,p,q
Case -2
n:If q="-" Or q Like f Then l r,c+1,y,p,q
End Select
If q="+" And i<>"S" Then h r,c,y*-1
p=0
End If
w:
End Sub


Python 3, 349 bytes

Ugh, so many bytes.

S=input().split('\n')
Q=[[S[0].find('S'),0]]
D=[[1,0],[0,1],[-1,0],[0,-1]]
A='-S--++-'
B='|S||++|'
P='>V<^'
i=0
while D:
a,b=Q[i];i+=1
j=S[b][a]
for x,y in D:
try:
c,d=a+x,b+y;k=S[d][c]
if k in P:
z=P.index(k);print(S[d+D[z][1]][c+D[z][0]]);D=[]
if (j+k in A and x)+(j+k in B and y)*~([c,d]in Q):Q+=[[c,d]]
except IndexError: pass


Essentially a breadth-first search. Bonus: this actually exits gracefully instead of using exit(), which is longer anyway.

• This doesn't implement about half of the search constraints, man. ideone.com/OzoWRX – bopjesvla Sep 18 '15 at 23:30
• @bopjesvla: Drat, you're right. Good spot! – El'endia Starman Sep 18 '15 at 23:34
• How are you able to use a multiline string with input()? It's problematic for me. – The_Basset_Hound Sep 22 '15 at 0:51
• @The_Basset_Hound: You can't manually enter the newlines; you have to copy-paste the entire thing at once. – El'endia Starman Sep 22 '15 at 3:07
• @El'endiaStarman I'm copy/pasting and it's still only reading the first line it seems. – The_Basset_Hound Sep 22 '15 at 11:16

Perl 5

The solution turned out longer than other solutions.
Even after golfing it. So this is the ungolfed version.
It prints the map so that you can follow the cursor.

The way it works? At each step it puts possible moves on the stack. And it keeps on running till there's nothing left on the stack, or a solution is found.
It can be easily modified to find all solutions and choose the nearest --> while(@_){...

while(<>){
chomp;
@R=split//;
$j++;$i=0;
for(@R){$nr++;$i++;$A[$i][$j]=$_;if('S'eq$_){$x=$i;$y=$j}}$xm=$i,if$xm<$i;$ym=$j; } push(@_,[($x,$y,'S',0)]);$cr='';
while(@_&&''eq$cr){ @C=pop@_; ($x,$y,$d,$n)=($C[0]->[0],$C[0]->[1],$C[0]->[2],$C[0]->[3]);$c=$A[$x][$y];$A[$x][$y]='.';
if($c=~m/[S+]/){ if('L'ne$d&&$A[$x+1][$y]=~m/[+-]/){push(@_,[($x+1,$y,'R',$n+1)])}
if('D'ne$d&&$A[$x][$y-1]=~m/[+|]/){push(@_,[($x,$y-1,'U',$n+1)])} if('R'ne$d&&$A[$x-1][$y]=~m/[+-]/){push(@_,[($x-1,$y,'L',$n+1)])}
if('U'ne$d&&$A[$x][$y+1]=~m/[+|]/){push(@_,[($x,$y+1,'D',$n+1)])} } if($c eq'|'){
if($d ne'U'&&$A[$x][$y+1]=~m/[+|<>^V]/){push(@_,[($x,$y+1,'D',$n+1)])} if($d ne'D'&&$A[$x][$y-1]=~m/[+|<>^V]/){push(@_,[($x,$y-1,'U',$n+1)])}
}
if($c eq'-'){ if($d ne'L'&&$A[$x+1][$y]=~m/[+-<>^V]/){push(@_,[($x+1,$y,'R',$n+1)])}
if($d ne'R'&&$A[$x-1][$y]=~m/[+-<>^V]/){push(@_,[($x-1,$y,'L',$n+1)])} } if($c=~m/[<>^V]/&&$n<$nr){
if($c eq'>'&&$A[$x+1][$y]=~m/\w/){$cr=$A[$x+1][$y];$nr=$n}
if($c eq'<'&&$A[$x-1][$y]=~m/\w/){$cr=$A[$x-1][$y];$nr=$n}
if($c eq'V'&&$A[$x][$y+1]=~m/\w/){$cr=$A[$x][$y+1];$nr=$n}
if($c eq'^'&&$A[$x][$y-1]=~m/\w/){$cr=$A[$x][$y-1];$nr=$n}
}
print_map()
}
print "$cr\n"; sub print_map { print "\033[2J"; #clearscreen print "\033[0;0H"; #cursor at 0,0 for$j(1..$ym){for$i(1..$xm){print (($x==$i&&$y==$j)?'X':$A[$i][$j])}print"\n"}
sleep 1;
}


Test

$cat test_arrows6.txt S-----+ | +-^ +---+->B +---^$ perl arrows.pl < test_arrows6.txt
.-----+
.     +-^
......XB
.....
B

• I saw the title and thought "Perl" and "done in 5 bytes". heart attack – Conor O'Brien Oct 6 '15 at 3:15

PHP version (comments are french, sorry)

<?php
/*
* By Gnieark https://blog-du-grouik.tinad.fr oct 2015
* Anwser to "code golf" http://codegolf.stackexchange.com/questions/57952/where-is-the-arrow-pointing in PHP
*/
//ouvrir le fichier contenant la "carte et l'envoyer dans un array 2 dimmension
$mapLines=explode("\n",file_get_contents('./input.txt'));$i=0;
foreach($mapLines as$ligne){
$map[$i]=str_split($ligne,1); if((!isset($y)) && in_array('S',$map[$i])){
//tant qu'à parcourir la carte, on cherche le "S" s'il n'a pas déjà été trouvé.
$y=$i;
$x=array_search('S',$map[$i]); }$i++;
}
if(!isset($y)){ echo "Il n'y a pas de départ S dans ce parcours"; die; } echo "\n".file_get_contents('./input.txt')."\nLe départ est aux positions ".$map[$y][$x]." [".$x.",".$y."]. Démarrage du script...\n";
$previousX=-1; // Contiendra l'ordonnée de la position précédente. (pour le moment, une valeur incohérente)$previousY=-1; // Contiendra l'absycede la position précédente. (pour le moment, une valeur incohérente)
$xMax=count($map[0]) -1;
$yMax=count($map) -1;
$previousCrosses=array(); //On ne gardera en mémoire que les croisements, pas l'ensemble du chemin. while(1==1){ // C'est un défi de codagee, pas un script qui sera en prod. j'assume. switch($map[$y][$x]){
case "S":
//même fonction que "+"
case "+":
//on peut aller dans les 4 directions
$target=whereToGoAfterCross($x,$y,$previousX,$previousY); if($target){
go($target[0],$target[1]);
}else{
goToPreviousCross();
}
break;
case "s":
goToPreviousCross();
break;
case "-":
//déplacement horizontal
if($previousX <$x){
//vers la droite
$targetX=$x+1;
if(in_array($map[$y][$targetX],array('-','+','S','>','^','V'))){ go($targetX,$y); }else{ //On est dans un cul de sac goToPreviousCross(); } }else{ //vers la gauche$targetX=$x-1; if(in_array($map[$y][$targetX],array('-','+','S','<','^','V'))){
go($targetX,$y);
}else{
//On est dans un cul de sac
goToPreviousCross();
}
}
break;
case "|":
//déplacement vertical
if($previousY <$y){
//on descend (/!\ y augmente vers le bas de la carte)
$targetY=$y+1;
if(in_array($map[$targetY][$x],array('|','+','S','>','<','V'))){ go ($x,$targetY); }else{ goToPreviousCross(); } }else{ //on Monte (/!\ y augmente vers le bas de la carte)$targetY=$y - 1; if(in_array($map[$targetY][$x],array('|','+','S','>','<','V'))){
go ($x,$targetY);
}else{
goToPreviousCross();
}
}
break;
case "^":
case "V":
case ">":
case "<":
wheAreOnAnArrow($map[$y][$x]); break; } } function wheAreOnAnArrow($arrow){
global $x,$y,$xMax,$yMax,$map; switch($arrow){
case "^":
$targetX=$x;
$targetY=$y -1;
$charsOfTheLoose=array(" ","V","-","s"); break; case "V":$targetX=$x;$targetY=$y + 1;$charsOfTheLoose=array(" ","^","-","s");
break;
case ">":
$targetX=$x + 1;
$targetY=$y;
$charsOfTheLoose=array(" ","<","|","s"); break; case "<":$targetX=$x - 1;$targetY=$y;$charsOfTheLoose=array(" ",">","|","s");
break;
default:
break;
}
if(($targetX <0) OR ($targetY<0) OR ($targetX>$xMax) OR ($targetY>$yMax) OR (in_array($map[$targetY][$targetX],$charsOfTheLoose))){
goToPreviousCross();
}else{
if(preg_match("/^[a-z]$/",strtolower($map[$targetY][$targetX]))){
//WIN
echo "WIN: ".$map[$targetY][$targetX]."\n"; die; }else{ //on va sur la cible go($targetX,$targetY); } } } function whereToGoAfterCross($xCross,$yCross,$previousX,$previousY){ //haut if(canGoAfterCross($xCross,$yCross +1 ,$xCross,$yCross,$previousX,$previousY)){ return array($xCross,$yCross +1); }elseif(canGoAfterCross($xCross,$yCross -1 ,$xCross,$yCross,$previousX,$previousY)){ //bas return array($xCross,$yCross -1); }elseif(canGoAfterCross($xCross-1,$yCross,$xCross,$yCross,$previousX,$previousY)){ //gauche return array($xCross-1,$yCross); }elseif(canGoAfterCross($xCross+1,$yCross,$xCross,$yCross,$previousX,$previousY)){ //droite return array($xCross+1,$yCross); }else{ //pas de direction possible return false; } } function canGoAfterCross($xTo,$yTo,$xNow,$yNow,$xPrevious,$yPrevious){ global$previousCrosses,$xMax,$yMax,$map; if(($xTo < 0) OR ($yTo < 0) OR ($xTo >= $xMax) OR ($yTo >= $yMax)){return false;}// ça sort des limites de la carte if( ($map[$yTo][$xTo]==" ") // on ne va pas sur un caractere vide
OR (($xTo==$xPrevious)&&($yTo==$yPrevious)) //on ne peut pas revenir sur nos pas (enfin, ça ne servirait à rien dans cet algo)
OR (($xTo==$xNow)&&($map[$yTo][$xTo]=="-")) //Déplacement vertical, le caractere suivant ne peut etre "-" OR (($yTo==$yNow)&&($map[$yTo][$xTo]=="|")) // Déplacement horizontal, le caractère suivant ne peut être "|"
OR ((isset($previousCrosses[$xNow."-".$yNow])) && (in_array($xTo."-".$yTo,$previousCrosses[$xNow."-".$yNow]))) //croisement, ne pas prendre une direction déjà prise
){
return false;
}
return true;
}
function go($targetX,$targetY){
global $previousX,$previousY,$x,$y,$previousCrosses,$map;
if(($map[$y][$x]=='S')OR ($map[$y][$x]=='+')){
//on enregistre le croisement dans lequel on renseigne la direction prise et la direction d'origine
$previousCrosses[$x."-".$y][]=$previousX."-".$previousY;$previousCrosses[$x."-".$y][]=$targetX."-".$targetY;
}
$previousX=$x;
$previousY=$y;
$x=$targetX;
$y=$targetY;
//debug
echo "deplacement en ".$x.";".$y."\n";
}
function goToPreviousCross(){
global $x,$y,$previousX,$previousY,$xMax,$yMax,$previousCrosses; /* * On va remonter aux précédents croisements jusqu'à ce * qu'un nouveau chemin soit exploitable */ foreach($previousCrosses as $key =>$croisement){
list($crossX,$crossY)=explode("-",$key);$cross=whereToGoAfterCross($crossX,$crossY,-1,-1);
if($cross){ go($crossX,$crossY); return true; } } //si on arrive là c'est qu'on est bloqués echo "Aucun chemin n'est possible\n"; die; }  Haskell, 268 bytes Congratulations to the Javascripters! Gave up the bounty, but here is what I got. May/Might not work in all cases, but actually handles arrows starting in and arrowheads conncting to +es, as far as I know. Didn't even include searching for the S, is just (0,0) for now. import Data.List x%m=length m<=x||x<0 a s(x,y)z|y%s||x%(head s)=[]|0<1=b s(x,y)z$s!!y!!x
b s(x,y)z c|g c>[]=filter(>' ')$concat[a s(x+i k,y+i(3-k))k|k<-g c,k-zmod4/=2]|0<1=[c] i=([0,-1,0,1]!!) g c=findIndices(celem)$words\$"V|+S <-+S ^|+S >-+S"
f s=a(lines s)(0,0)0


I would like to see an APL version in spirit of https://www.youtube.com/watch?v=a9xAKttWgP4

As a start, a vectorized Julia solution which I think can be translated 1:0.3 to APL or J. It takes a string R representing a L x K arrowgram. It first translates the matrix of symbols into a matrix of small 3x3 matrices whose patterns are the binary expansions of the letters of the string "\0\x18\fH\t]]\x1cI". For example '+' is encoded as reshape([0,digits(int(']'),2,8)],3,3)

  0 2 0
2 2 2
0 2 0


In this representation, the path consists of 2's and gets flooded by 3's from the starting point.

A=zeros(Int,L*3+3,K*3+3)
s(i,j=i,n=2)=A[i:end+i-n,j:end+j-n]
Q=Int['A':'Z';'a':'z']
for k=0:K-1,l=0:L-1
r=R[K*l+k+1]
q=search(b"V^><+S|-",r)
d=reverse(digits(b"\0\x18\fH\t]]\x1cI"[q+1],2,8))
A[1+3l:3l+3,1+3k:3k+3]=2*[d;0]
A[3l+2,3k+2]+=r*(r in Q&&r!='V'&&r!='^')+(1-r)*(r=='S')+3(5>q>0)
end
m=0
while sum(A)>m
m=sum(A)
for i=0:1,j=1:2,(f,t)=[(3,6),(11,15)]
A[j:end+j-2,j:end+j-2]=max(s(j),f*(s(j).*s(2-i,1+i).==t))
end
end
for i=[1,4],k=Q
any(s(1,1,5).*s(5-i,i,5).==11*k)&&println(Char(k))
end


To test,

   R=b"""
d  S------+    b
|
|
c      +--->a
""";

K=search(R,'\n') # dimensions
L=div(endof(R),K)

include("arrow.jl")

a


By the way, I think the clause "Another + may be adjacent but the arrow should prioritize going on a - or | first." puts a vector approach at a disadvantage. Anyway, I just ignored it.