C89 (209 characters)
#define M(a,b)*p==*#a?m=b,*p=1,q=p:
*q,G[999],*p=G;w;main(m){for(;(*++p=getchar())>0;)M(<,-1)M
(>,1)M(^,-w)M(v,w)!w&*p<11?w=p-G:0;for(;q+=m,m=*q&4?(*q&1?
-1:1)*(m/w?m/w:m*w):*q&9?!puts(*q&1?"false":"true"):m;);}
Explanation
This monstrosity will probably be difficult to follow if you don't understand C. Just a forewarning.
#define M(a,b)*p==*#a?m=b,*p=1,q=p:
This little macro checks if the current character (*p
) is equal to whatever a
is in character form (*#a
). If they are equal, set the movement vector to b
(m=b
), mark this character as a wall (*p=1
), and set the starting point to the current location (q=p
). This macro includes the "else" portion.
*q,G[999],*p=G;
w;
Declare some variables.
* q
is the light's current location.
* G
is the game board as a 1D array.
* p
is the current read location when populating G
.
* w
is the board's width.
main(m){
Obvious main
. m
is a variable storing the movement vector. (It's a parameter to main
as an optimization.)
for(;(*++p=getchar())>0;)
Loop through all characters, populating G
using p
. Skip G[0]
as an optimization (no need to waste a character writing p
again in the third part of the for
).
M(<,-1)
M(>,1)
M(^,-w)
M(v,w)
Use the aforementioned macro to define the lazer, if possible. -1
and 1
correspond to left and right, respectively, and -w
and w
up and down.
!w&*p<11
?w=p-G
:0;
If the current character is an end-of-line marker (ASCII 10), set the width if it hasn't already been set. The skipped G[0]
allows us to write w=p-G
instead of w=p-G+1
. Also, this finishes off the ?:
chain from the M
's.
for(;
q+=m,
Move the light by the movement vector.
m=
*q&4
?(*q&1?-1:1)*(
m/w?m/w:m*w
)
Reflect the movement vector.
:*q&9
?!puts(*q&1?"false":"true")
:m
;
If this is a wall or x
, quit with the appropriate message (m=0
terminates the loop). Otherwise, do nothing (noop; m=m
)
);
}