A laser shoots a straight beam in one of the four orthogonal directions, indicated by <>^v. Determine whether it will hit the target O on a rectangular grid.

Each of these will hit (True):





These will miss (False):





Input: A rectangular grid of ., sized at least 2x2, with exactly one target O and one laser that's one of <>^v. The lines can be a list of strings, a 2D array or nested list of characters, or a single newline-separated string with an optional trailing newline.

Output: A consistent truthy value if the laser beam hits the target, and a consistent falsy value if it misses.

I'll consider submissions that don't use regular expressions (or built-in pattern-based string matching) as a separate category. If you put (no regex) after the language name, your answer will appear separately in the leaderboard.

var QUESTION_ID=80196,OVERRIDE_USER=20260;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/80196/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

  • 8
    \$\begingroup\$ Related \$\endgroup\$ Commented May 19, 2016 at 17:36
  • 6
    \$\begingroup\$ I was hoping you would include mirrors with / and \ . Maybe for another question... \$\endgroup\$
    – vsz
    Commented May 19, 2016 at 20:24
  • 2
    \$\begingroup\$ @Mego ... which makes this challenge a lot simpler and allows for very different approaches. \$\endgroup\$ Commented May 20, 2016 at 7:05
  • 2
    \$\begingroup\$ @Mego I disagree; by that logic, the plain hello world challenge is a duplicate of dozens of other challenges at the same time. Anyway, thanks for notifying me about the power I now have to close/reopen code golf challenges, I wasn't aware of that. \$\endgroup\$ Commented May 21, 2016 at 10:17
  • 5
    \$\begingroup\$ @Mego Although this question is a special case of the other one, I don't believe it to be a dupe because the answers use totally different approaches. On that question, they all compute the path the beam takes. Portals can move the path from anywhere to anywhere which doesn't seem to a allow a shortcut, and reflectors are tricky to handle. Answers here instead mostly check or match some property of the input string. Sure, you can copy a path-tracing answer from the other challenge and remove the extra bits, but this method is overkill and gives a needlessly long solution. \$\endgroup\$
    – xnor
    Commented May 21, 2016 at 18:56

18 Answers 18


Snails, 19 bytes


The spec for this one can be implemented as literally as possible, no thinking required.

  • 6
    \$\begingroup\$ Can you add an explanation of how this works? \$\endgroup\$
    – anon
    Commented May 19, 2016 at 18:16
  • 5
    \$\begingroup\$ @QPaysTaxes Until feersum gets around to it, hopefully this will help: Snails is a 2D pattern matching language. udlr set the direction of the snail to up/down/left/right. | works like it does in regular regex, and ) doesn't need a matching open parenthesis. So the code pretty directly translates to "Find one of v<>^ and set the direction appropriately, then try to find an O in that direction." \$\endgroup\$ Commented May 19, 2016 at 19:13
  • \$\begingroup\$ Yeah, what Eggman said. The only other thing is that , is like the * of regex. \$\endgroup\$
    – feersum
    Commented May 19, 2016 at 21:42

Retina, 56 52 42 38 31 30 bytes

Saved 1 byte thanks to @MartinBüttner


Abuses properties of rectangles. Requires input to have a trailing newline.

Try it online


This works in three parts:

  • Matching >
  • Matching <
  • Matching ^ and v this is because the logic for ^ and v are really the same, just the characters.

Validating <

This is simple:


This matches an O, optionally followed by non-newline chars, then a <

Validating >

This is much the same as the previous way except the other way around. First a > is matched, then the O

Validating ^ and v

This was difficult to golf and took adverting of the input always being valid. First, we match whether it's v or an O:


If it's an ^, the first character that's encountered should be a O. So, this matches the first character to match. Next we count the amount of .s following it up to the newline:


Next, this can go into two parts, I'll cover the first:

So first, we match until the following O, using:


This optionally matches all non-O characters until an O is encountered, if this is successful, then it continues... if not, then the following happens...

Now, it attempts to find the ^ using:


^ is a special character in regex so it needs to be escaped. [^^] matches all characters except ^, this works the same as the above, if this succeeds, then the following happens...

So now, one of the above has matched succesfully, the \1 checks and sees if the capture group from before (.*\n), this capture group stored the amount of .s there were after either the v or O from before, so now \1 just checks if the amount of dots in the same.

  • \$\begingroup\$ You can save a byte by using instead of \n (Retina can handle source code in ISO 8859-1.) \$\endgroup\$ Commented May 20, 2016 at 7:10
  • \$\begingroup\$ @MartinBüttner thought was only \n in replacements, thanks for the tip! \$\endgroup\$
    – Downgoat
    Commented May 20, 2016 at 23:57
  • \$\begingroup\$ no it works anywhere in the source code. After splitting the file into lines, the first thing Retina does is replace the everywhere, before doing any further parsing. \$\endgroup\$ Commented May 21, 2016 at 6:19

Java (no regex), 413 412 246 242 212 211 209 198 bytes

Competing in golf challenges using java has less sense than take a part in Formula 1 race on bicycle, but I'm not always doing thinks which makes any sense.

Here is my extremely long java solution Golfed version

boolean l(char[][]w){int[]t={},l={};for(int y=0;y<w.length;y++)for(int x=0;x<w[0].length;x++){if(w[y][x]=='O')t=new int[]{x,y};if(w[y][x]=='<')l=new int[]{x,y,1};if(w[y][x]=='>')l=new int[]{x,y,2};if(w[y][x]=='v')l=new int[]{x,y,3};if(w[y][x]=='^')l=new int[]{x,y,4};};return(l[2]==1&&l[1]==t[1]&&l[0]>t[0])||(l[2]==2&&l[1]==t[1]&&l[0]<t[0])||(l[2]==3&&l[0]==t[0]&&l[1]<t[1])||(l[2]==4&&l[0]==t[0]&&l[1]>t[1]);}

and ungolfed

boolean l(char[][] w) {
    int[] t = {}, l = {};
    for (int y = 0; y < w.length; y++)
        for (int x = 0; x < w[0].length; x++) {
            if (w[y][x] == 'O')
                t = new int[] { x, y };
            if (w[y][x] == '<')
                l = new int[] { x, y, 1 };
            if (w[y][x] == '>')
                l = new int[] { x, y, 2 };
            if (w[y][x] == 'v')
                l = new int[] { x, y, 3 };
            if (w[y][x] == '^')
                l = new int[] { x, y, 4 };
    return (l[2] == 1 && l[1] == t[1] && l[0] > t[0])
            || (l[2] == 2 && l[1] == t[1] && l[0] < t[0])
            || (l[2] == 3 && l[0] == t[0] && l[1] < t[1])
            || (l[2] == 4 && l[0] == t[0] && l[1] > t[1]);

Seems like my entire concept was wrong, here is my shorter solution

boolean z(char[][]w){int x=0,y=0,i=0,a=w.length,b=w[0].length;for(;w[y][x]!=79;)if(++y==a){y=0;x++;}for(;i<(a<b?b:a);)if(i<b&w[y][i]==(i<x?62:60)|i<a&w[i][x]==(i++<y?'v':94))return 1<2;return 1>2;}

and ungolfed version

oolean z(char[][] w) {
        int x = 0, y = 0, i = 0, a = w.length, b = w[0].length;
        for (; w[y][x] != 79;)
            if (++y == a) {
                y = 0;
        for (; i < (a < b ? b : a);)
            if (i < b & w[y][i] == (i < x ? 62 : 60) | i < a
                    & w[i][x] == (i++ < y ? 'v' : 94))
                return 1 < 2;
        return 1 > 2;

EDIT I rewrote code for looking for 'O', now it contains single loop is much shorter, and I also used @Frozn suggestion to replace some of characters with their ascii values.

In result,another 30 bytes bites the dust.

Another suggestion from @Frozn, and we are couple bytes closer to Python solution

Another rewrite drop one loop, and combine two if statements

  • 1
    \$\begingroup\$ +1 You can remove the space between return and ( to save a byte, though. The space isn't necessary when your return value is inside parenthesis (or quotes for Strings). Source from the Java code-golfing tips. \$\endgroup\$ Commented May 20, 2016 at 11:43
  • \$\begingroup\$ @KevinCruijssen not saving much, but thanks:) \$\endgroup\$
    – user902383
    Commented May 20, 2016 at 12:27
  • \$\begingroup\$ Every little bit (or should I say byte) helps I guess. ;) Besides, I don't think Java will ever win a code-golf challenge anyway. I still like doing code-golf challenges in Java though, especially since I'm currently working with Java at work. \$\endgroup\$ Commented May 20, 2016 at 13:10
  • \$\begingroup\$ You can replace the chars by their ASCII value: 'O' = 79, '>' = 62, '<' = 60, '^' = 94. For 'v' it's 118 but that doesn't shorten the code. \$\endgroup\$
    – Frozn
    Commented May 26, 2016 at 11:13
  • \$\begingroup\$ @Frozn as Kevin said, in every byte counts. \$\endgroup\$
    – user902383
    Commented May 26, 2016 at 11:30

MATL (no regex), 26 25 24 22 bytes


Try it Online!

Modified version for all test cases


        % Implicitly grab input
'>v<^'  % String literal indicating the direction chars
XJ      % Store in the J clipboard
y       % Copy the input from the bottom of the stack
m       % Check to see which of the direction chars is in the input. The
        % result is a 1 x 4 logical array with a 1 for the found direction char
f       % Get the 1-based index into '>v<^' of this character
X!      % Rotate the input board 90 degrees N times where N is the index. This
        % Way we rotate the board so that, regardless of the direction char,
        % the direction char should always be BELOW the target in the same column
t       % Duplicate
Zp      % Determine if any elements are prime ('O' is the only prime)
Ys      % Compute the cumulative sum of each column
w       % Flip the top two stack elements
J       % Grab '>v<^' from clipboard J
m       % Create a logical matrix the size of the input where it is 1 where
        % the direction char is and 0 otherwise
)       % Use this to index into the output of the cumulative sum. If the 
        % direction char is below 'O' in a column, this will yield a 1 and 0 otherwise
        % Implicitly display the result
  • \$\begingroup\$ @LuisMendo Now to figure out how to get rid of J \$\endgroup\$
    – Suever
    Commented May 19, 2016 at 21:13
  • \$\begingroup\$ I don't know MATL, so this may be a silly question, but why is 0 prime? \$\endgroup\$
    – Neil
    Commented May 20, 2016 at 9:29
  • 3
    \$\begingroup\$ @Neil It's letter 'O', not number 0. ASCII code for letter 'O' is 79 \$\endgroup\$
    – Luis Mendo
    Commented May 20, 2016 at 10:05
  • \$\begingroup\$ Ugh, I guess I would have still been fooled if you'd been looking for odd numbers instead. \$\endgroup\$
    – Neil
    Commented May 20, 2016 at 11:35

CJam (no regex), 25

Earlier versions were wrong, this will have to do for now:


Try it online


q~         read and evaluate the input (given as an array of strings)
_z         copy and transpose
]          put the original grid and transposed grid in an array
{…}f%      map the block to each grid (applying it to each string in each grid)
  '.-      remove all dots (obtaining a string of 0 to 2 chars)
  Hb       convert to base H=17, e.g. ">O" -> 62*17+79=1133
  I%       calculate modulo I=18
[HF]       make an array [17 15]
.&         set-intersect the first array (mapped grid) with 17 and 2nd one with 15
~+         dump and concatenate the results
,          get the array length

I tried a few mathematical formulas for distinguishing between "good" and "bad" strings, and for each type of formula I tried plugging in various numbers. I ended up with the HbI% above.

"good" strings for the original grid are ">O" and "O<" and they give the result 17
"good" strings for the transposed grid are "vO" and "O^" and they give the result 15
"bad" strings for both grids are: ">", "<", "^", "v", "O", "", "O>", "Ov", "<O", "^O" and they give the results 8, 6, 4, 10, 7, 0, 1, 3, 1, 3


Python 3 (no regex), 184 bytes.

Hooray for eval hacking!

def f(a,o=0,d={},q=''):
 for r in a:
  for c in r:d[c]=o,i;i+=1;q=(c,q)[c in'O.']
 z,y=d['O'];e,j=d[q];return eval("z%se and y%sj"%(('><'[q<'v'],'=='),('==',q))[q in'><'])

TSQL (sqlserver 2012) (no regex), 358 bytes

DECLARE @ varchar(1000)=
'......'+ CHAR(13)+CHAR(10)+
'......'+ CHAR(13)+CHAR(10)+
'...0..'+ CHAR(13)+CHAR(10)+
'...^..'+ CHAR(13)+CHAR(10)

number n,SUBSTRING(@,number,1)a,1+min(IIF(SUBSTRING(@,number,1)=char(13),number,99))over()m
FROM master..spt_values
WHERE'P'=type and
IIF(c.n%c.m=d.n%c.m and c.a+d.a in('0^','v0')or
c.n/c.m=d.n/c.m and c.a+d.a in('>0','0<'),1,0)FROM c,c d
WHERE c.n<d.n and char(13)not in(c.a,d.a)

Had to use funky linechange in the declaration to force the online version to execute it(assigning values to input variables doesn't affect the length calculation anyway)

Try it online!


Pyth, 43 bytes

KxJ"^<v>"[email protected]}\Ohc_W>K1hf}@JKTCW!%K2.z@JK

Live demo.


JavaScript (ES6), 78 bytes


Regexp of course. Turned out to be similar in principle to the Ruby answer.


Ruby, 71 55 54 bytes

Regex solution, which means that it's probably going to be easily beaten by Retina or Perl.

Returns an index number (truthy) if there is a match.

Now with a similar trick to @Downgoat Retina answer, matching for down and up beams at the same time.


JavaScript (ES6) (no regex), 126 bytes


Where \n represents the literal newline character.


Clojure (no regex), 293 bytes

(defn z[f](let[v(sort(keep-indexed(fn[i v](if(some #{v}[\v\>\<\^\O])[(if(= v\O)\& v)i]))f))l(+(.indexOf f"\n")1)d((nth v 1)0)q((nth v 1)1)p((nth v 0)1)r(=(quot p l)(quot q l))i(> q p)](cond(= d\^)(and i(=(mod(- q p)l)0))(= d\v)(and(not i)(=(mod(- p q)l)0))(= d\>)(and(not i)r):else(and i r))))

Doesn't feel great. Straightforward solution, finding the index of corresponding characters and calculating if they're on the same line.

You can try it here https://ideone.com/m4f2ra


Python (no regex), 105 bytes

def f(s):t=s.strip('.\n');return not['\n'in t,len(t)%(s.find('\n')+1)!=1,1]['>O<vO^'.find(t[0]+t[-1])//3]

returns True or False

First, strips '.' and '\n' from the ends so that the characters of interest, '0<>v^', are the first and last characters.

'>O<vO^'.find(t[0]+t[-1])//3 - checks if the characters are a potentially valid arrangement. Evaluates to 0 for '>O' or 'O<', to 1 for 'vO' or 'O^', and to -1 for anything else.

'\n'in t - checks if the characters are in different rows,
len(t)%(s.find('\n')+1)!=1 - checks if they are in different columns, and
1 - is the default

The not inverts the result selected from the list, so the return expression is equivalent to:

t[0]+t[-1] in '>0<' and '\n' not in t or t[0]+t[-1] in 'vO^' and len(t)%(s.find('\n')+1)==1

Julia (no regex), 98


Function operating on an array of chars, normalizing by rotation, removing rows and columns containing only dots by range indexing and finally checking for location of 'O' taking into account if the remainder b is a column or row vector using matrix multiplication.

Try it online


Python 2 (no regex), 268 bytes

import numpy
def q(i):
 for n in s:
   if n.index(">")<a or n.index("<")>a:return 1
 for n in range(len(i)):
  c=[x[0] for x in s[:,n].tolist()]
   if c.index("v")<a or c.index("^")>a:return 1
 return 0

Truthy and Falsy values returned by the function are 1 and 0, respectively.

I haven't had a chance to golf yet. Honestly, I'm not too hopeful for this one...

Any suggestions would be greatly appreciated!


C# (No Regex), 282 bytes

bool F(char[,]b){int k=0,l=1,m=1,n=0,o=0;for(int x=0;x<b.GetLength(0);x++)for(int y=0;y<b.GetLength(1);y++){char i=b[x,y];if(i=='O'){k=x;l=y;}if(new[]{'<','>','^','v'}.Contains(i)){m=x;n=y;o=i;}}return(o==60&&k==m&&l<n)||(o==62&&k==m&&l>n)||(o==94&&l==n&&k<m)||(o==118&&l==n&&k>m);}

Works like the java version but transpiled and reduced

Expanded (Explanation included):

bool F(char[,] b)
    // declare variables for goal x, goal y, laser x, laser y, and laser direction respectively (laser direction is char code for directions)
    int k = 0, l = 0, m = 0, n = 0, o = 0;
    // go through each cell
    for (int x = 0; x < b.GetLength(0); x++)
        for (int y = 0; y < b.GetLength(1); y++)
            // get cell contents
            char i = b[x, y];
            // set goal position if goal
            if (i == 'O')
                k = x;
                l = y;
            // set laser position and direction if laser
            if (new[]{ '<', '>', '^', 'v' }.Contains(i))
                m = x;
                n = y;
                o = i;
    // check everything is on the same line and in right direction
    return (o == 60 && k == m && l < n) ||
           (o == 62 && k == m && l > n) ||
           (o == 94 && l == n && k < m) ||
           (o == 118 && l == n && k > m);

C (ANSI) (No regex), 237 bytes

#define l b[1][i]
main(i,b,c,d,x,y,z,p)char **b;{for(y=z=i=0,x=1;z<2&&(l==10?x=0,++y:1);i++,x++)if(l>46){if(l!=79)p=l;if(!z++)c=x,d=y;}i--;x--;z=(c==x)*((p=='v')*(l==79)+(p==94)*(l==p))+(d==y)*((p==60)*(l==p)+(p==62)*(l==79));return z;}


#define l b[1][i]
main(i,b,c,d,x,y,z,p)char **b;{
    return z;

I think I took a decently different approach here compared to the Java or C# implementations. I got coordinates of the 'O' and arrow ((c,d) and (x,y)) and then compared them to see if the arrow was pointing in the correct direction.

Returns 0 if false and 1 if true


Grime v0.1, 31 bytes


Not a very interesting solution. Prints 1 for truthy instances, and 0 for falsy ones. Try it online!


We simply search the input rectangle for a minimal-size (n×1 or 1×n) pattern that contains the laser and target in the correct order. The n` flag makes the interpreter print the number of matches, of which there will always be at most one. The rest of the line consists of four patterns separated by |-characters, which means a logical OR: a rectangle is matched if it matches one of the patterns. The patterns work as follows:

\>.*a    Literal ">", horizontal row of any chars, one alphabetic char
a.*\<    One alphabetic char, horizontal row of any chars, literal "<"
\v/./*/a Literal "v", on top of vertical column of any chars, on top of one alphabetic char
a/./*/\^ One alphabetic char, on top of vertical column of any chars, on top of literal "^"

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.