language agnostic - Código Golf: Lasers
language-agnostic code-golf (28)
F #, 255 caracteres (¡y aún bastante legible!):
Ok, después de una noche de descanso, mejoré esto mucho:
let a=System.Console.In.ReadToEnd()
let w,c=a.IndexOf"/n"+1,a.IndexOfAny[|''^'';''<'';''>'';''v''|]
let rec n(c,d)=
let e,s=[|-w,2;-1,3;1,0;w,1|].[d]
n(c+e,match a.[c+e]with|''/''->s|''//'->3-s|'' ''->d|c->printfn"%A"(c=''x'');exit 0)
n(c,"^<>v".IndexOf a.[c])
Vamos a hablar a través de línea por línea.
Primero, sorbe toda la entrada en una gran matriz unidimensional (las matrices en 2D pueden ser malas para el golf de código, simplemente use una matriz 1D y agregue / reste el ancho de una línea al índice para subir / bajar una línea).
Luego calculamos ''w'', el ancho de una línea de entrada, y ''c'', la posición inicial, indexando en nuestra matriz.
Ahora definamos la ''siguiente'' función ''n'', que toma una posición actual ''c'' y una dirección ''d'' que es 0,1,2,3 para arriba, izquierda, derecha, abajo.
El índice-épsilon ''e'' y el que-nueva-dirección-si-golpeamos-una-barra ''s'' son computados por una tabla. Por ejemplo, si la dirección actual ''d'' es 0 (arriba), entonces el primer elemento de la tabla dice "-w, 2", lo que significa que disminuimos el índice por w, y si tocamos una barra la nueva dirección es 2 (derecho).
Ahora recurrimos a la siguiente función ''n'' con (1) el siguiente índice ("c + e" - current plus epsilon), y (2) la nueva dirección, que calculamos mirando hacia adelante para ver qué hay en la matriz en esa próxima celda. Si el carácter de búsqueda anticipada es una barra inclinada, la nueva dirección es ''s''. Si se trata de una barra diagonal inversa, la nueva dirección es 3-s (nuestra elección de la codificación 0123 hace que esto funcione). Si es un espacio, seguimos yendo en la misma dirección ''d''. Y si es cualquier otro personaje ''c'', entonces el juego termina, imprimiendo ''verdadero'' si el carácter fue ''x'' y falso de lo contrario.
Para empezar, llamamos a la función recursiva ''n'' con la posición inicial ''c'' y la dirección inicial (que hace la codificación inicial de dirección en 0123).
Creo que todavía puedo quitarle algunos personajes más, pero estoy bastante satisfecho con esto (y 255 es un buen número).
El reto
El código más corto por recuento de caracteres para ingresar una representación 2D de una placa, y la salida ''verdadero'' o ''falso'' de acuerdo con la entrada .
El tablero está hecho de 4 tipos de azulejos:
# - A solid wall
x - The target the laser has to hit
/ or / - Mirrors pointing to a direction (depends on laser direction)
v, ^, > or < - The laser pointing to a direction (down, up, right and left respectively)
Solo hay un láser y solo un objetivo . Las paredes deben formar un rectángulo sólido de cualquier tamaño, donde el láser y el objetivo se colocan dentro. Las paredes dentro de la "habitación" son posibles.
Disparos de rayos láser y viaja desde su origen a la dirección que apunta. Si un rayo láser golpea la pared, se detiene. Si un rayo láser choca contra un espejo, rebota 90 grados en la dirección en que apunta el espejo. Los espejos tienen dos lados, lo que significa que ambos lados son "reflectantes" y pueden rebotar un rayo de dos maneras. Si un rayo láser golpea el láser ( ^v><
) en sí mismo, se trata como una pared (el rayo láser destruye el proyector y por lo tanto nunca llegará al objetivo).
Casos de prueba
Input: ########## # / / # # # # / x# # > / # ########## Output: true Input: ########## # v x # # / # # /# # / # ########## Output: false Input: ############# # # # # > # # # # # # # x # # # # ############# Output: false Input: ########## #////// # #/////// # #////////# #//////x^# ########## Output: true
El recuento de códigos incluye entrada / salida (es decir, programa completo).
F #, 36 líneas, muy legible
Ok, solo para obtener una respuesta:
let ReadInput() =
let mutable line = System.Console.ReadLine()
let X = line.Length
let mutable lines = []
while line <> null do
lines <- Seq.to_list line :: lines
line <- System.Console.ReadLine()
lines <- List.rev lines
X, lines.Length, lines
let X,Y,a = ReadInput()
let mutable p = 0,0,''v''
for y in 0..Y-1 do
for x in 0..X-1 do
printf "%c" a.[y].[x]
match a.[y].[x] with
|''v''|''^''|''<''|''>'' -> p <- x,y,a.[y].[x]
|_ -> ()
printfn ""
let NEXT = dict [ ''>'', (1,0,''^'',''v'')
''v'', (0,1,''<'',''>'')
''<'', (-1,0,''v'',''^'')
''^'', (0,-1,''>'',''<'') ]
let next(x,y,d) =
let dx, dy, s, b = NEXT.[d]
x+dx,y+dy,(match a.[y+dy].[x+dx] with
| ''/'' -> s
| ''//'-> b
| ''#''|''v''|''^''|''>''|''<'' -> printfn "false"; exit 0
| ''x'' -> printfn "true"; exit 0
| '' '' -> d)
while true do
p <- next p
Muestras:
##########
# / / #
# #
# / x#
# > / #
##########
true
##########
# v x #
# / #
# /#
# / #
##########
false
#############
# # #
# > # #
# # #
# # x #
# # #
#############
false
##########
#////// #
#/////// #
#////////#
#//////x^#
##########
true
##########
# / / #
# #
#/ / x#
#/> / #
##########
false
##########
# / /#
# / / #
#/ / x#
#/^// / #
##########
false
PostScript , 359 bytes
First attempt, lots of room for improvement...
/a[{(%stdin)(r)file 99 string readline not{exit}if}loop]def a{{[(^)(>)(<)(v)]{2
copy search{stop}if pop pop}forall}forall}stopped/r count 7 sub def pop
length/c exch def[(>)0(^)1(<)2(v)3>>exch get/d exch def{/r r[0 -1 0 1]d get
add def/c c[1 0 -1 0]d get add def[32 0 47 1 92 3>>a r get c get .knownget
not{exit}if/d exch d xor def}loop a r get c get 120 eq =
Python - 152
Reads input from a file called "L"
A=open("L").read()
W=A.find(''/n'')+1
D=P=-1
while P<0:D+=1;P=A.find(">^<v"[D])
while D<4:P+=[1,-W,-1,W][D];D=[D,D^3,D^1,4,5]['' //x''.find(A[P])]
print D<5
To read from stdin replace the first line with this
import os;A=os.read(0,1e9)
If you need lowercase true/false change the last line to
print`D<5`.lower()
Ruby, 176 characters
x=!0;y=0;e="^v<>#x";b=readlines;b.map{|l|(x||=l=~/[v^<>]/)||y+=1};c=e.index(b[y][x])
loop{c<2&&y+=c*2-1;c>1&&x+=2*c-5;e.index(n=b[y][x])&&(p n==?x;exit);c^='' //''.index(n)||0}
I used a simple state machine (like most posters), nothing fancy. I just kept whittling it down using every trick I could think of. The bitwise XOR used to change direction (stored as an integer in the variable c
) was a big improvement over the conditionals I had in earlier versions.
I have a suspicion that the code that increments x
and y
could be made shorter. Here is the section of the code that does the incrementing:
c<2&&y+=c*2-1;c>1&&x+=(c-2)*2-1
Edit : I was able to shorten the above slightly:
c<2&&y+=c*2-1;c>1&&x+=2*c-5
The current direction of the laser c
is stored as follows:
0 => up 1 => down 2 => left 3 => right
The code relies on this fact to increment x
and y
by the correct amount (0, 1, or -1). I tried rearranging which numbers map to each direction, looking for an arrangement that would let me do some bitwise manipulation to increment the values, because I have a nagging feeling that it would be shorter than the arithmetic version.
C89 (209 caracteres)
#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;);}
Explicación
Esta monstruosidad probablemente será difícil de seguir si no entiendes C. Solo un aviso.
#define M(a,b)*p==*#a?m=b,*p=1,q=p:
Esta pequeña macro comprueba si el carácter actual ( *p
) es igual a lo que sea a
en forma de carácter ( *#a
). Si son iguales, establezca el vector de movimiento en b
( m=b
), marque este carácter como una pared ( *p=1
) y establezca el punto de partida en la ubicación actual ( q=p
). Esta macro incluye la parte "else".
*q,G[999],*p=G;
w;
Declara algunas variables. * q
es la ubicación actual de la luz. * G
es el tablero de juego como una matriz 1D. * p
es la ubicación de lectura actual al completar G
* w
es el ancho del tablero.
main(m){
Obvio main
. m
es una variable que almacena el vector de movimiento. (Es un parámetro main
como optimización).
for(;(*++p=getchar())>0;)
Pasa por todos los caracteres, completa G
usando p
. Omita G[0]
como optimización (no es necesario perder un carácter escribiendo p
nuevamente en la tercera parte de for
).
M(<,-1)
M(>,1)
M(^,-w)
M(v,w)
Utilice la macro antes mencionada para definir el lazer, si es posible. -1
y 1
corresponden a izquierda y derecha, respectivamente, y -w
y w
arriba y abajo.
!w&*p<11
?w=p-G
:0;
Si el carácter actual es un marcador de final de línea (ASCII 10), establezca el ancho si aún no se ha establecido. El G[0]
omitido nos permite escribir w=pG
lugar de w=p-G+1
. Además, esto finaliza la cadena ?:
De los M
''s.
for(;
q+=m,
Mueva la luz por el vector de movimiento.
m=
*q&4
?(*q&1?-1:1)*(
m/w?m/w:m*w
)
Refleja el vector de movimiento.
:*q&9
?!puts(*q&1?"false":"true")
:m
;
Si esto es un muro o x
, salga con el mensaje apropiado ( m=0
termina el ciclo). De lo contrario, no hacer nada (noop; m=m
)
);
}
Golfscript - 83 caracteres (mashup mío y más extraño)
La nueva línea está aquí para envolver
:|''v^><''.{|?}%{)}?:$@=?{.[10|?).~)1-1]=$+
:$|='' //x''?/[./2^.1^''true''''false'']=.4/!}do
Golfscript - 107 caracteres
La nueva línea está allí para mayor claridad
10/:@?):&4:$;{0''>^<v''$(:$=@?:*>}do;
{[1 0&--1&]$=*+:*;[{$}{3$^}{1$^}{"true "}{"false"}]@*='' //x''?=~5/:$>}do$
Cómo funciona.
La primera línea determina la ubicación y dirección iniciales.
La segunda línea pasa por el giro cada vez que el láser golpea un espejo.
Perl, 177 caracteres
El primer salto de línea puede eliminarse; los otros dos son obligatorios
$/=%d=split//,'' >/^/v'';$_=<>;$s=''#'';{
y/v<^/>v</?do{my$o;$o.="
"while s/.$/$o.=$&,""/meg;y''///'//''for$o,$s;$_=$o}:/>x/?die"true
":/>#/?die"false
":s/>(.)/$s$d{$1}/?$s=$1:1;redo}
Explicación:
$/ = %d = ('' '' => ''>'', ''/'' => ''^'', ''//' => ''v'');
Si un rayo que se mueve a la derecha se encuentra con un {espacio vacío, un espejo en ángulo arriba, un espejo en ángulo hacia abajo} se convierte en una {viga que se mueve a la derecha, viga que se mueve hacia arriba, viga que se mueve hacia abajo}. Inicialice $/
en el camino - afortunadamente "6" no es un char de entrada válido.
$_ = <>;
Lea la pizarra en $_
.
$s="#";
$s
es el símbolo de lo que sea que el haz esté sobre el ahora. Dado que el emisor láser debe tratarse como una pared, para empezar, establezca que se trata de una pared.
if (tr/v<^/>v</) {
my $o;
$o .= "/n" while s/.$/$o .= $&, ""/meg;
tr,///,///, for $o, $s;
$_ = $o;
}
Si el rayo láser está apuntando de alguna manera, excepto a la derecha, gire su símbolo y luego gire la placa completa en su lugar (también rotando los símbolos para los espejos). Se trata de una rotación de 90 grados a la izquierda, lograda de manera efectiva invirtiendo las filas al transponer filas y columnas, de una manera ligeramente diabólica con efectos secundarios. En el código golfed, el tr está escrito en la forma y''''''
que me permite omitir el backslash de una barra invertida.
die "true/n" if />x/; die "false/n" if />#/;
Termine con el mensaje correcto si golpeamos el objetivo o una pared.
$s = $1 if s/>(.)/$s$d{$1}/;
Si hay un espacio vacío frente al láser, avance. Si hay un espejo frente al láser, avance y gire la viga. En cualquier caso, vuelva a poner el "símbolo guardado" en la ubicación del rayo antiguo y coloque la cosa que acaba de sobreescribir en el símbolo guardado.
redo;
Repita hasta la terminación. {...;redo}
tiene dos caracteres menos que for(;;){...}
y tres menos que while(1){...}
.
Perl, 166 160 caracteres
Perl, 251 248 246 222 214 208 203 201 193 190 180 176 173 170 166 -> 160 caracteres.
La solución tuvo 166 golpes cuando finalizó el concurso, pero A. Rex ha encontrado un par de maneras de afeitar a 6 personajes más:
s!.!$t{$s++}=$&!ge,$s=$r+=99for<>;%d=''>.^1<2v3''=~/./g;($r)=grep$d|=$d{$t{$_}},%t;
{$_=$t{$r+=(1,-99,-1,99)[$d^=3*////+m</>]};/[//// ]/&&redo}die/x/?true:false,$/
La primera línea carga la entrada en %t
, una tabla del tablero donde $t{99*i+j}
retiene al personaje en la fila i , columna j . Entonces,
%d=split//,''>.^1<2v3'' ; ($r)=grep{$d|=$d{$t{$_}}}%t
busca en los elementos de %t
un carácter que coincida con > ^ <
o v
, y simultáneamente establece $d
en un valor entre 0 y 3 que indica la dirección inicial del rayo láser.
Al comienzo de cada iteración en el ciclo principal, actualizamos $d
si el haz se encuentra actualmente en un espejo. XOR''ing por 3 da el comportamiento correcto para un /
mirror y XOR''ing por 1 da el comportamiento correcto para un /
mirror.
$d^=3*////+m</>
A continuación, la posición actual $r
se actualiza de acuerdo con la dirección actual.
$r+=(1,-99,-1,99)[$d] ; $_ = $t{$r}
Asignamos al personaje en la posición actual a $_
para hacer un uso conveniente de los operadores de coincidencia.
/[//// ]/ && redo
Continúa si estamos en un espacio en blanco o un personaje espejo. De lo contrario, terminaremos en true
si estamos en el objetivo ( $_ =~ /x/
) y en caso contrario lo haremos.
Limitación: puede no funcionar en problemas con más de 99 columnas. Esta limitación podría eliminarse a expensas de 3 personajes más,
Pitón
294 277 253 240 232 caracteres incluyendo líneas nuevas:
(el primer personaje en las líneas 4 y 5 es una pestaña, no espacios)
l=''>v<^'';x={''/'':''^<v>'',''//':''v>^<'','' '':l};b=[1];r=p=0
while b[-1]:
b+=[raw_input()];r+=1
for g in l:
c=b[r].find(g)
if-1<c:p=c+1j*r;d=g
while'' ''<d:z=l.find(d);p+=1j**z;c=b[int(p.imag)][int(p.real)];d=x.get(c,'' ''*4)[z]
print''#''<c
Olvidé que Python incluso tenía punto y coma opcionales.
Cómo funciona
La idea clave detrás de este código es usar números complejos para representar posiciones y direcciones. Las filas son el eje imaginario, que aumenta hacia abajo. Las columnas son el eje real, aumentando a la derecha.
l=''>v<^'';
una lista de los símbolos láser. El orden se elige para que el índice de un carácter de dirección del láser se corresponda con una potencia de sqrt (-1)
x={''/'':''^<v>'',''//':''v>^<'','' '':l};
una tabla de transformación que determina cómo cambia la dirección cuando el haz deja diferentes fichas. El mosaico es la clave, y las nuevas direcciones son los valores.
b=[1];
sostiene el tablero. El primer elemento es 1 (se evalúa como verdadero) para que el ciclo while se ejecute al menos una vez.
r=p=0
r
es el número de fila actual de la entrada, p
es la posición actual del rayo láser.
while b[-1]:
deja de cargar los datos de la placa cuando raw_input devuelve una cadena vacía
b+=[raw_input()];r+=1
anexa la siguiente línea de entrada al tablero e incrementa el contador de filas
for g in l:
adivinar cada dirección de láser a su vez
c=b[r].find(g)
establece la columna en la ubicación del láser o -1 si no está en la línea (o está apuntando en una dirección diferente)
if-1<c:p=c+1j*r;d=g
si encontramos un láser, entonces configure la posición actual p
y la dirección d
. d
es uno de los caracteres en l
Después de cargar la placa en b
, la posición actual p
y la dirección d
se han ajustado a las de la fuente del láser.
while'' ''<d:
espacio tiene un valor ASCII menor que cualquiera de los símbolos de dirección, por lo que lo usamos como indicador de detención.
z=l.find(d);
índice de la dirección actual char en l
cadena. z
se usa más tarde para determinar la dirección del nuevo haz utilizando la tabla x
, y para incrementar la posición.
p+=1j**z;
incrementa la posición usando una potencia de i. Por ejemplo, l.find(''<'')==2
-> i ^ 2 = -1, que se movería a la columna de la izquierda.
c=b[int(p.imag)][int(p.real)];
lea el char en la posición actual
d=x.get(c,'' ''*4)[z]
busca la nueva dirección para el haz en la tabla de transformación. Si el char actual no existe en la tabla, entonces configure d
al espacio.
print''#''<c
imprime falso si nos detuvimos en otra cosa que no sea el objetivo.
C# 3.0
259 chars
bool S(char[]m){var w=Array.FindIndex(m,x=>x<11)+1;var s=Array.FindIndex(m,x=>x>50&x!=92&x<119);var t=m[s];var d=t<61?-1:t<63?1:t<95?-w:w;var u=0;while(0<1){s+=d;u=m[s];if(u>119)return 0<1;if(u==47|u==92)d+=d>0?-w-1:w+1;else if(u!=32)return 0>1;d=u>47?-d:d;}}
Slightly more readable:
bool Simulate(char[] m)
{
var w = Array.FindIndex(m, x => x < 11) + 1;
var s = Array.FindIndex(m, x => x > 50 & x != 92 & x < 119);
var t = m[s];
var d = t < 61 ? -1 : t < 63 ? 1 : t < 95 ? -w : w;
var u = 0;
while (0 < 1)
{
s += d;
u = m[s];
if (u > 119)
return 0 < 1;
if (u == 47 | u == 92)
d += d > 0 ? -w - 1 : w + 1;
else if (u != 32)
return 0 > 1;
d = u > 47 ? -d : d;
}
}
The main waste of chars seems to be in finding the width of the map and the position of the laser source. Any ideas how to shorten this?
F# - 454 (or thereabouts)
Bit late to the game, but can''t resist posting my 2d attempt.
Update modified slightly. Now stops correctly if transmitter is hit. Pinched Brian''s idea for IndexOfAny (shame that line is so verbose). I haven''t actually managed to work out how to get ReadToEnd to return from the Console, so I''m taking that bit on trust...
I''m pleased with this answer, as though it is quite short, it is still fairly readable.
let s=System.Console.In.ReadToEnd() //(Not sure how to get this to work!)
let w=s.IndexOf(''/n'')+1 //width
let h=(s.Length+1)/w //height
//wodge into a 2d array
let a=Microsoft.FSharp.Collections.Array2D.init h (w-1)(fun y x -> s.[y*w+x])
let p=s.IndexOfAny[|''^'';''<'';''>'';''v''|] //get start pos
let (dx,dy)= //get initial direction
match "^<>v".IndexOf(s.[p]) with
|0->(0,-1)
|1->(-1,0)
|2->(1,0)
|_->(0,1)
let mutable(x,y)=(p%w,p/w) //translate into x,y coords
let rec f(dx,dy)=
x<-x+dx;y<-y+dy //mutate coords on each call
match a.[y,x] with
|'' ''->f(dx,dy) //keep going same direction
|''/''->f(-dy,-dx) //switch dx/dy and change sign
|''//'->f(dy,dx) //switch dx/dy and keep sign
|''x''->"true"
|_->"false"
System.Console.Write(f(dx,dy))
Golfscript (83 characters)
Hello, gnibbler!
:/'><v^''.{/?}%{)}?:P@=?{:O[1-1/10?).~)]=P+
:P/='' //x''?[O.2^.1^''true''''false'']=.4/!}do
Ruby - 146 Chars
A=$<.read
W=A.index(''
'')+1
until
q=A.index(">^<v"[d=d ?d+1:0])
end
while d<4
d=[d,d^3,d^1,4,5][('' //x''.index(A[q+=[1,-W,-1,W][d]])or 4)]
end
p 5>d
c (K&R) 339 necessary characters after more suggestions from strager.
The physicist in me noted that the propagation and reflection operations are time-reversal invariant, so this version, throws rays from the target and checks to see if the arrive at the laser emitter.
The rest of the implementation is very straight forward and is taken more or less exactly from my earlier, forward going effort.
Compressed:
#define R return
#define C case
#define Z x,y
int c,i,j,m[99][99],Z;s(d,e,Z){for(;;)switch(m[x+=d][y+=e]){C''^'':R 1==e;
C''>'':R-1==d;C''v'':R-1==e;C''<'':R 1==d;C''#'':C''x'':R 0;C 92:e=-e;d=-d;C''/'':c=d;
d=-e;e=-c;}}main(){while((c=getchar())>0)c==10?i=0,j++:(c==120?x=i,y=j:
i,m[i++][j]=c);puts(s(1,0,Z)|s(0,1,Z)|s(-1,0,Z)|s(0,-1,Z)?"true":"false");}
Uncompressed(ish):
#define R return
#define C case
#define Z x,y
int c,i,j,m[99][99],Z;
s(d,e,Z)
{
for(;;)
switch(m[x+=d][y+=e]){
C''^'':
R 1==e;
C''>'':
R-1==d;
C''v'':
R-1==e;
C''<'':
R 1==d;
C''#'':
C''x'':
R 0;
C 92:
e=-e;
d=-d;
C''/'':
c=d;
d=-e;
e=-c;
}
}
main(){
while((c=getchar())>0)
c==10?i=0,j++:
(c==120?x=i,y=j:i,m[i++][j]=c);
puts(s(1,0,Z)|s(0,1,Z)|s(-1,0,Z)|s(0,-1,Z)?"true":"false");
}
There is no input validation, and bad input can send it into an infinite loop. Works properly with input no larger than 99 by 99. Requires a compiler that will link the standard library without including any of the headers. And I think I''m done, strager has me beat by a considerable stretch, even with his help.
I''m rather hoping someone will demonstrate a more subtle way to accomplish the task. There s nothing wrong with this, but it is not deep magic.
Apuesto a que la gente ha estado esperando esta por más de LOOOOONG. (¿Qué quieres decir con que el desafío ha terminado y a nadie le importa más?)
He aquí ... Aquí presento una solución en
Befunge-93!
Tiene un peso de 973 charaters (o 688 si tienes suficiente caridad como para ignorar el espacio en blanco, que solo se usa para formatear y no hace nada en el código real).
Advertencia : hace poco escribí mi propio intérprete de Befunge-93 en Perl, y desafortunadamente esto es todo lo que realmente he tenido tiempo para probarlo. Estoy razonablemente seguro de su corrección en general, pero podría tener una limitación extraña con respecto a EOF: dado que el operador <>
Perl devuelve undef al final del archivo, esto se procesa como un 0 en el contexto numérico. Para las implementaciones basadas en C donde EOF tiene un valor diferente (-1 decir), este código podría no funcionar.
003pv >~v> #v_"a"43g-!#v_23g03p33v>v
>39#<*v :: >:52*-!v >"rorrE",vg2*
######1 >^vp31+1g31$_03g13gp vv,,<15,
a#3 >0v vp30+1g30<>,,#3^@
######p $ 0vg34"a"< > >vp
^<v> > ^ p3<>-#v_:05g-!|>:15g-!| $
> v^ < < < >^v-g52:< $
v _ >52*"eslaf",,vv|-g53:_ v
: ^-"#">#:< #@,,,,<<>:43p0 v0 p34<
>">"-!vgv< ^0p33g31p32-1g3<
^ <#g1|-g34_v#-g34_v#-g34"><v^"<<<<
v!<^<33>13g1v>03g1-v>03g1+03p$v $$
>^ _#-v 1>g1-1v>+13pv >03p v pp
^_:"^"^#|^g30 <3# $< $<>^33
^!-"<":<>"v"v^># p#$<> $^44
^ >#^#_ :" "-#v_ ^ > ^gg
v g34$< ^!<v"/":< >$3p$^>05g43p$ ^55
>,@ |!-"/" :_$43g:">"-!|> ^$32
*v"x":< >-^ ^4g52<>:"^" -#v_^
5>-!#v_"ror"vv$p34g51:<>#| !-"<":<#|
^2,,, ,,"er"<>v #^^#<>05g43p$$^>^
>52*"eurt",,,,,@>15g4 3p$$$$ ^#
>:"v"/:"<"/: "^" -!#^_-!#^_-! ^
> ^
Explicación
Si no está familiarizado con la sintaxis y el funcionamiento de Befunge, consulte here .
Befunge es un lenguaje basado en pila, pero hay comandos que permiten escribir caracteres en el código de Befunge. Aprovecho eso en dos lugares. Primero, copio toda la entrada en la placa de Befunge, pero localicé un par de líneas debajo del código escrito real. (Por supuesto, esto nunca es realmente visible cuando se ejecuta el código).
El otro lugar está cerca de la esquina superior izquierda:
######
a#
######
En este caso, el área que he resaltado arriba es donde guardo un par de coordenadas. La primera columna en la fila del medio es donde guardo la coordenada x para la "posición del cursor" actual; la segunda columna es donde guardo la coordenada y; las siguientes dos columnas son para almacenar la coordenada xey de la fuente del rayo láser cuando se encuentra; y la columna final (con el carácter ''a'' en ella) se sobrescribe finalmente para contener la dirección actual del haz, que obviamente cambia a medida que se traza la trayectoria del haz.
El programa comienza colocando (0,27) como la posición inicial del cursor. Luego se lee la entrada de un personaje a la vez y se coloca en la posición del cursor; los saltos de línea simplemente hacen que la coordenada y aumente y la coordenada x regrese a 0, al igual que un retorno de carro real. Eventualmente undef es leído por el intérprete y ese valor de 0 caracteres se usa para señalar el final de la entrada y avanzar a los pasos de iteración del láser. Cuando se lee el carácter láser [<> ^ v], también se copia en el depósito de memoria (sobre el carácter ''a'') y sus coordenadas se copian a las columnas justo a la izquierda.
El resultado final de todo esto es que todo el archivo se copia básicamente en el código de Befunge, un poco por debajo del código real atravesado.
Después, la ubicación del haz se copia nuevamente en las ubicaciones del cursor, y se realiza la siguiente iteración:
- Verifique la dirección actual del rayo e incremente o disminuya las coordenadas del cursor de forma apropiada. (Hago esto primero para evitar tener que lidiar con el caso de la esquina del rayo láser justo en el primer movimiento.)
- Lee el personaje en esa ubicación.
- Si el caracter es "#", ponga newline y "false" en la pila, imprima y termine.
- Compárelo con todos los caracteres del haz [<> ^ v]; si hay una coincidencia, también imprime "falso / n" y finaliza.
- Si el personaje es un espacio, vacía la pila y continúa.
- Si el personaje es una barra inclinada, lleva la dirección del haz a la pila y compárala con cada uno de los caracteres de dirección por turno. Cuando se encuentra uno, la nueva dirección se almacena en el mismo lugar del código y el ciclo se repite.
- Si el caracter es una barra diagonal inversa, haga básicamente lo mismo que el anterior (excepto con la correlación adecuada para la barra inclinada invertida).
- Si el personaje es ''x'', hemos alcanzado el objetivo. Imprime "true / n" y sal.
- Si el caracter no es ninguno de estos, imprime "error / n" y sal.
Si hay suficiente demanda para ello, trataré de señalar exactamente en qué parte del código se logra todo esto.
Este fue un puerto directo de la solución de Brian para C # 3, menos las interacciones de la consola. Esta no es una entrada en el desafío, ya que no es un programa completo, solo me preguntaba cómo algunos de los constructos F # que utilizó podrían representarse en C #.
bool Run(string input) {
var a = input.Split(new[] {Environment.NewLine}, StringSplitOptions.None);
var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
.First(x => new[] {''v'', ''^'', ''<'', ''>''}.Contains(x.d));
var NEXT = new[] {
new {d = ''>'', dx = 1, dy = 0, s = ''^'', b = ''v''},
new {d = ''v'', dx = 0, dy = 1, s = ''<'', b = ''>''},
new {d = ''<'', dx = -1, dy = 0, s = ''v'', b = ''^''},
new {d = ''^'', dx = 0, dy = -1, s = ''>'', b = ''<''}
}.ToDictionary(x => x.d);
while (true) {
var n = NEXT[p.d];
int x = p.x + n.dx,
y = p.y + n.dy;
var d = a[y][x];
switch (d) {
case ''/'': d = n.s; break;
case ''//': d = n.b; break;
case '' '': d = p.d; break;
default: return d == ''x'';
}
p = new {x, y, d};
}
}
Editar: después de un poco de experimentación, el siguiente código de búsqueda bastante detallado:
int X = a[0].Length, Y = a.Length;
var p = new {x = 0, y = 0, d = ''v''};
for (var y = 0; y < Y; y++) {
for (var x = 0; x < X; x++) {
var d = a[y][x];
switch (d) {
case ''v'': case ''^'': case ''<'': case ''>'':
p = new {x, y, d}; break;
}
}
}
ha sido reemplazado por un código LINQ to Objects mucho más compacto:
var p = a.SelectMany((line, y) => line.Select((d, x) => new {x, y, d}))
.First(x => new[] {''v'', ''^'', ''<'', ''>''}.Contains(x.d));
Un peso de 18203 caracteres es una solución de Python que puede:
- hacer frente a los espejos fuera de la "habitación"
- calcule la trayectoria cuando no hay ''habitación'' en función de las limitaciones 2D (la especificación dice mucho sobre lo que debe estar en la ''habitación'' pero no si la habitación debe existir)
- informar sobre errores
Todavía necesita arreglarse un poco y no sé si la física en 2D dicta que el rayo no puede cruzarse ...
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
The shortest code by character count to input a 2D representation of a board,
and output ''true'' or ''false'' according to the input.
The board is made out of 4 types of tiles:
# - A solid wall
x - The target the laser has to hit
/ or / - Mirrors pointing to a direction (depends on laser direction)
v, ^, > or < - The laser pointing to a direction (down, up, right and left
respectively)
There is only one laser and only one target. Walls must form a solid rectangle
of any size, where the laser and target are placed inside. Walls inside the
''room'' are possible.
Laser ray shots and travels from it''s origin to the direction it''s pointing. If
a laser ray hits the wall, it stops. If a laser ray hits a mirror, it is bounces
90 degrees to the direction the mirror points to. Mirrors are two sided, meaning
both sides are ''reflective'' and may bounce a ray in two ways. If a laser ray
hits the laser (^v><) itself, it is treated as a wall (laser beam destroys the
beamer and so it''ll never hit the target).
"""
SOLID_WALL, TARGET, MIRROR_NE_SW, MIRROR_NW_SE, LASER_DOWN, LASER_UP, /
LASER_RIGHT, LASER_LEFT = range(8)
MIRRORS = (MIRROR_NE_SW, MIRROR_NW_SE)
LASERS = (LASER_DOWN, LASER_UP, LASER_RIGHT, LASER_LEFT)
DOWN, UP, RIGHT, LEFT = range(4)
LASER_DIRECTIONS = {
LASER_DOWN : DOWN,
LASER_UP : UP,
LASER_RIGHT: RIGHT,
LASER_LEFT : LEFT
}
ROW, COLUMN = range(2)
RELATIVE_POSITIONS = {
DOWN : (ROW, 1),
UP : (ROW, -1),
RIGHT: (COLUMN, 1),
LEFT : (COLUMN, -1)
}
TILES = {"#" : SOLID_WALL,
"x" : TARGET,
"/" : MIRROR_NE_SW,
"//": MIRROR_NW_SE,
"v" : LASER_DOWN,
"^" : LASER_UP,
">" : LASER_RIGHT,
"<" : LASER_LEFT}
REFLECTIONS = {MIRROR_NE_SW: {DOWN : LEFT,
UP : RIGHT,
RIGHT: UP,
LEFT : DOWN},
MIRROR_NW_SE: {DOWN : RIGHT,
UP : LEFT,
RIGHT: DOWN,
LEFT : UP}}
def does_laser_hit_target(tiles):
"""
Follows a lasers trajectory around a grid of tiles determining if it
will reach the target.
Keyword arguments:
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
"""
#Obtain the position of the laser
laser_pos = get_laser_pos(tiles)
#Retrieve the laser''s tile
laser = get_tile(tiles, laser_pos)
#Create an editable starting point for the beam
beam_pos = list(laser_pos)
#Create an editable direction for the beam
beam_dir = LASER_DIRECTIONS[laser]
#Cache the number of rows
number_of_rows = len(tiles)
#Keep on looping until an ultimate conclusion
while True:
#Discover the axis and offset the beam is travelling to
axis, offset = RELATIVE_POSITIONS[beam_dir]
#Modify the beam''s position
beam_pos[axis] += offset
#Allow for a wrap around in this 2D scenario
try:
#Get the beam''s new tile
tile = get_tile(tiles, beam_pos)
#Perform wrapping
except IndexError:
#Obtain the row position
row_pos = beam_pos[ROW]
#Handle vertical wrapping
if axis == ROW:
#Handle going off the top
if row_pos == -1:
#Move beam to the bottom
beam_pos[ROW] = number_of_rows - 1
#Handle going off the bottom
elif row_pos == number_of_rows:
#Move beam to the top
beam_pos[ROW] = 0
#Handle horizontal wrapping
elif axis == COLUMN:
#Obtain the row
row = tiles[row_pos]
#Calculate the number of columns
number_of_cols = len(row)
#Obtain the column position
col_pos = beam_pos[COLUMN]
#Handle going off the left hand side
if col_pos == -1:
#Move beam to the right hand side
beam_pos[COLUMN] = number_of_cols - 1
#Handle going off the right hand side
elif col_pos == number_of_cols:
#Move beam to the left hand side
beam_pos[COLUMN] = 0
#Get the beam''s new tile
tile = get_tile(tiles, beam_pos)
#Handle hitting a wall or the laser
if tile in LASERS /
or tile == SOLID_WALL:
return False
#Handle hitting the target
if tile == TARGET:
return True
#Handle hitting a mirror
if tile in MIRRORS:
beam_dir = reflect(tile, beam_dir)
def get_laser_pos(tiles):
"""
Returns the current laser position or an exception.
Keyword arguments:
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
"""
#Calculate the number of rows
number_of_rows = len(tiles)
#Loop through each row by index
for row_pos in range(number_of_rows):
#Obtain the current row
row = tiles[row_pos]
#Calculate the number of columns
number_of_cols = len(row)
#Loop through each column by index
for col_pos in range(number_of_cols):
#Obtain the current column
tile = row[col_pos]
#Handle finding a laser
if tile in LASERS:
#Return the laser''s position
return row_pos, col_pos
def get_tile(tiles, pos):
"""
Retrieves a tile at the position specified.
Keyword arguments:
pos --- a row/column position of the tile
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
"""
#Obtain the row position
row_pos = pos[ROW]
#Obtain the column position
col_pos = pos[COLUMN]
#Obtain the row
row = tiles[row_pos]
#Obtain the tile
tile = row[col_pos]
#Return the tile
return tile
def get_wall_pos(tiles, reverse=False):
"""
Keyword arguments:
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
reverse --- whether to search in reverse order or not (defaults to no)
"""
number_of_rows = len(tiles)
row_iter = range(number_of_rows)
if reverse:
row_iter = reversed(row_iter)
for row_pos in row_iter:
row = tiles[row_pos]
number_of_cols = len(row)
col_iter = range(number_of_cols)
if reverse:
col_iter = reversed(col_iter)
for col_pos in col_iter:
tile = row[col_pos]
if tile == SOLID_WALL:
pos = row_pos, col_pos
if reverse:
offset = -1
else:
offset = 1
for axis in ROW, COLUMN:
next_pos = list(pos)
next_pos[axis] += offset
try:
next_tile = get_tile(tiles, next_pos)
except IndexError:
next_tile = None
if next_tile != SOLID_WALL:
raise WallOutsideRoomError(row_pos, col_pos)
return pos
def identify_tile(tile):
"""
Returns a symbolic value for every identified tile or None.
Keyword arguments:
tile --- the tile to identify
"""
#Safely lookup the tile
try:
#Return known tiles
return TILES[tile]
#Handle unknown tiles
except KeyError:
#Return a default value
return
def main():
"""
Takes a board from STDIN and either returns a result to STDOUT or an
error to STDERR.
Called when this file is run on the command line.
"""
#As this function is the only one to use this module, and it can only be
#called once in this configuration, it makes sense to only import it here.
import sys
#Reads the board from standard input.
board = sys.stdin.read()
#Safely handles outside input
try:
#Calculates the result of shooting the laser
result = shoot_laser(board)
#Handles multiple item errors
except (MultipleLaserError, MultipleTargetError) as error:
#Display the error
sys.stderr.write("%s/n" % str(error))
#Loop through all the duplicated item symbols
for symbol in error.symbols:
#Highlight each symbol in green
board = board.replace(symbol, "/033[01;31m%s/033[m" % symbol)
#Display the board
sys.stderr.write("%s/n" % board)
#Exit with an error signal
sys.exit(1)
#Handles item missing errors
except (NoLaserError, NoTargetError) as error:
#Display the error
sys.stderr.write("%s/n" % str(error))
#Display the board
sys.stderr.write("%s/n" % board)
#Exit with an error signal
sys.exit(1)
#Handles errors caused by symbols
except (OutsideRoomError, WallNotRectangleError) as error:
#Displays the error
sys.stderr.write("%s/n" % str(error))
lines = board.split("/n")
line = lines[error.row_pos]
before = line[:error.col_pos]
after = line[error.col_pos + 1:]
symbol = line[error.col_pos]
line = "%s/033[01;31m%s/033[m%s" % (before, symbol, after)
lines[error.row_pos] = line
board = "/n".join(lines)
#Display the board
sys.stderr.write("%s/n" % board)
#Exit with an error signal
sys.exit(1)
#Handles errors caused by non-solid walls
except WallNotSolidError as error:
#Displays the error
sys.stderr.write("%s/n" % str(error))
lines = board.split("/n")
line = lines[error.row_pos]
before = line[:error.col_pos]
after = line[error.col_pos + 1:]
symbol = line[error.col_pos]
line = "%s/033[01;5;31m#/033[m%s" % (before, after)
lines[error.row_pos] = line
board = "/n".join(lines)
#Display the board
sys.stderr.write("%s/n" % board)
#Exit with an error signal
sys.exit(1)
#If a result was returned
else:
#Converts the result into a string
result_str = str(result)
#Makes the string lowercase
lower_result = result_str.lower()
#Returns the result
sys.stdout.write("%s/n" % lower_result)
def parse_board(board):
"""
Interprets the raw board syntax and returns a grid of tiles.
Keyword arguments:
board --- the board containing the tiles (walls, laser, target, etc)
"""
#Create a container for all the lines
tiles = list()
#Loop through all the lines of the board
for line in board.split("/n"):
#Identify all the tiles on the line
row = [identify_tile(tile) for tile in line]
#Add the row to the container
tiles.append(row)
#Return the container
return tiles
def reflect(mirror, direction):
"""
Returns an updated laser direction after it has been reflected on a
mirror.
Keyword arguments:
mirror --- the mirror to reflect the laser from
direction --- the direction the laser is travelling in
"""
try:
direction_lookup = REFLECTIONS[mirror]
except KeyError:
raise TypeError("%s is not a mirror.", mirror)
try:
return direction_lookup[direction]
except KeyError:
raise TypeError("%s is not a direction.", direction)
def shoot_laser(board):
"""
Shoots the boards laser and returns whether it will hit the target.
Keyword arguments:
board --- the board containing the tiles (walls, laser, target, etc)
"""
tiles = parse_board(board)
validate_board(tiles)
return does_laser_hit_target(tiles)
def validate_board(tiles):
"""
Checks an board to see if it is valid and raises an exception if not.
Keyword arguments:
tiles --- row/column based version of a board containing symbolic
versions of the tiles (walls, laser, target, etc)
"""
found_laser = False
found_target = False
try:
n_wall, w_wall = get_wall_pos(tiles)
s_wall, e_wall = get_wall_pos(tiles, reverse=True)
except TypeError:
n_wall = e_wall = s_wall = w_wall = None
number_of_rows = len(tiles)
for row_pos in range(number_of_rows):
row = tiles[row_pos]
number_of_cols = len(row)
for col_pos in range(number_of_cols):
tile = row[col_pos]
if ((row_pos in (n_wall, s_wall) and
col_pos in range(w_wall, e_wall))
or
(col_pos in (e_wall, w_wall) and
row_pos in range(n_wall, s_wall))):
if tile != SOLID_WALL:
raise WallNotSolidError(row_pos, col_pos)
elif (n_wall != None and
(row_pos < n_wall or
col_pos > e_wall or
row_pos > s_wall or
col_pos < w_wall)):
if tile in LASERS:
raise LaserOutsideRoomError(row_pos, col_pos)
elif tile == TARGET:
raise TargetOutsideRoomError(row_pos, col_pos)
elif tile == SOLID_WALL:
if not (row_pos >= n_wall and
col_pos <= e_wall and
row_pos <= s_wall and
col_pos >= w_wall):
raise WallOutsideRoomError(row_pos, col_pos)
else:
if tile in LASERS:
if not found_laser:
found_laser = True
else:
raise MultipleLaserError(row_pos, col_pos)
elif tile == TARGET:
if not found_target:
found_target = True
else:
raise MultipleTargetError(row_pos, col_pos)
if not found_laser:
raise NoLaserError(tiles)
if not found_target:
raise NoTargetError(tiles)
class LasersError(Exception):
"""Parent Error Class for all errors raised."""
pass
class NoLaserError(LasersError):
"""Indicates that there are no lasers on the board."""
symbols = "^v><"
def __str__ (self):
return "No laser (%s) to fire." % ", ".join(self.symbols)
class NoTargetError(LasersError):
"""Indicates that there are no targets on the board."""
symbols = "x"
def __str__ (self):
return "No target (%s) to hit." % ", ".join(self.symbols)
class MultipleLaserError(LasersError):
"""Indicates that there is more than one laser on the board."""
symbols = "^v><"
def __str__ (self):
return "Too many lasers (%s) to fire, only one is allowed." % /
", ".join(self.symbols)
class MultipleTargetError(LasersError):
"""Indicates that there is more than one target on the board."""
symbols = "x"
def __str__ (self):
return "Too many targets (%s) to hit, only one is allowed." % /
", ".join(self.symbols)
class WallNotSolidError(LasersError):
"""Indicates that the perimeter wall is not solid."""
__slots__ = ("__row_pos", "__col_pos", "n_wall", "s_wall", "e_wall",
"w_wall")
def __init__(self, row_pos, col_pos):
self.__row_pos = row_pos
self.__col_pos = col_pos
def __str__ (self):
return "Walls must form a solid rectangle."
def __get_row_pos(self):
return self.__row_pos
def __get_col_pos(self):
return self.__col_pos
row_pos = property(__get_row_pos)
col_pos = property(__get_col_pos)
class WallNotRectangleError(LasersError):
"""Indicates that the perimeter wall is not a rectangle."""
__slots__ = ("__row_pos", "__col_pos")
def __init__(self, row_pos, col_pos):
self.__row_pos = row_pos
self.__col_pos = col_pos
def __str__ (self):
return "Walls must form a rectangle."
def __get_row_pos(self):
return self.__row_pos
def __get_col_pos(self):
return self.__col_pos
row_pos = property(__get_row_pos)
col_pos = property(__get_col_pos)
class OutsideRoomError(LasersError):
"""Indicates an item is outside of the perimeter wall."""
__slots__ = ("__row_pos", "__col_pos", "__name")
def __init__(self, row_pos, col_pos, name):
self.__row_pos = row_pos
self.__col_pos = col_pos
self.__name = name
def __str__ (self):
return "A %s was found outside of a ''room''." % self.__name
def __get_row_pos(self):
return self.__row_pos
def __get_col_pos(self):
return self.__col_pos
row_pos = property(__get_row_pos)
col_pos = property(__get_col_pos)
class LaserOutsideRoomError(OutsideRoomError):
"""Indicates the laser is outside of the perimeter wall."""
def __init__ (self, row_pos, col_pos):
OutsideRoomError.__init__(self, row_pos, col_pos, "laser")
class TargetOutsideRoomError(OutsideRoomError):
"""Indicates the target is outside of the perimeter wall."""
def __init__ (self, row_pos, col_pos):
OutsideRoomError.__init__(self, row_pos, col_pos, "target")
class WallOutsideRoomError(OutsideRoomError):
"""Indicates that there is a wall outside of the perimeter wall."""
def __init__ (self, row_pos, col_pos):
OutsideRoomError.__init__(self, row_pos, col_pos, "wall")
if __name__ == "__main__":
main()
A bash script to show off the colour error reporting:
#!/bin/bash
declare -a TESTS
test() {
echo -e "/033[1m$1/033[0m"
tput sgr0
echo "$2" | ./lasers.py
echo
}
test /
"no laser" /
" ##########
# x #
# / #
# /#
# // #
##########"
test /
"multiple lasers" /
" ##########
# v x #
# / #
# /#
# // ^ #
##########"
test /
"no target" /
" ##########
# v #
# / #
# /#
# // #
##########"
test /
"multiple targets" /
" ##########
# v x #
# / #
# /#
# // x #
##########"
test /
"wall not solid" /
" ##### ####
# v x #
# / #
# /#
# // #
##########"
test /
"laser_outside_room" /
" ##########
> # x #
# / #
# /#
# // #
##########"
test /
"laser before room" /
" > ##########
# x #
# / #
# /#
# // #
##########"
test /
"laser row before room" /
" >
##########
# x #
# / #
# /#
# // #
##########"
test /
"laser after room" /
" ##########
# x #
# / #
# /#
# // #
########## >"
test /
"laser row after room" /
" ##########
# x #
# / #
# /#
# // #
##########
> "
test /
"target outside room" /
" ##########
x # v #
# / #
# /#
# // #
##########"
test /
"target before room" /
" x ##########
# v #
# / #
# /#
# // #
##########"
test /
"target row before room" /
" x
##########
# v #
# / #
# /#
# // #
##########"
test /
"target after room" /
" ##########
# v #
# / #
# /#
# // #
########## x"
test /
"target row after room" /
" ##########
# v #
# / #
# /#
# // #
##########
x "
test /
"wall outside room" /
" ##########
# # v #
# / #
# /#
# // x #
##########"
test /
"wall before room" /
" # ##########
# v #
# / #
# /#
# // x #
##########"
test /
"wall row before room" /
" #
##########
# v #
# / #
# /#
# // x #
##########"
test /
"wall after room" /
" ##########
# v #
# / #
# /#
# // x #
########## #"
test /
"wall row after room" /
" ##########
# v #
# / #
# /#
# // x #
##########
#"
test /
"mirror outside room positive" /
" ##########
/ # / // #
# #
# // x#
# > / #
########## "
test /
"mirrors outside room negative" /
" ##########
// # v x #
# / #
# /#
# // #
##########"
test /
"mirror before room positive" /
" // ##########
# / // #
# #
# // x#
# > / #
########## "
test /
"mirrors before room negative" /
" / ##########
# v x #
# / #
# /#
# // #
##########"
test /
"mirror row before room positive" /
" //
##########
# / // #
# #
# // x#
# > / #
########## "
test /
"mirrors row before room negative" /
" //
##########
# v x #
# / #
# /#
# // #
##########"
test /
"mirror after row positive" /
" ##########
# / // #
# #
# // x#
# > / #
########## / "
test /
"mirrors after row negative" /
" ##########
# v x #
# / #
# /#
# // #
########## / "
test /
"mirror row after row positive" /
" ##########
# / // #
# #
# // x#
# > / #
##########
/ "
test /
"mirrors row after row negative" /
" ##########
# v x #
# / #
# /#
# // #
##########
/ "
test /
"laser hitting laser" /
" ##########
# v //#
# #
# #
#x // /#
##########"
test /
"mirrors positive" /
" ##########
# / // #
# #
# // x#
# > / #
########## "
test /
"mirrors negative" /
" ##########
# v x #
# / #
# /#
# // #
##########"
test /
"wall collision" /
" #############
# # #
# > # #
# # #
# # x #
# # #
#############"
test /
"extreme example" /
" ##########
#///////// #
#//////////// #
#////////////#
#/////////x^#
##########"
test /
"brian example 1" /
"##########
# / // #
# #
#/ // x#
#//> / #
##########"
test /
"brian example 2" /
"##########
# / //#
# / // #
#/ // x#
#//^/// / #
##########"
The unittests used in development:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import unittest
from lasers import *
class TestTileRecognition(unittest.TestCase):
def test_solid_wall(self):
self.assertEqual(SOLID_WALL, identify_tile("#"))
def test_target(self):
self.assertEqual(TARGET, identify_tile("x"))
def test_mirror_ne_sw(self):
self.assertEqual(MIRROR_NE_SW, identify_tile("/"))
def test_mirror_nw_se(self):
self.assertEqual(MIRROR_NW_SE, identify_tile("//"))
def test_laser_down(self):
self.assertEqual(LASER_DOWN, identify_tile("v"))
def test_laser_up(self):
self.assertEqual(LASER_UP, identify_tile("^"))
def test_laser_right(self):
self.assertEqual(LASER_RIGHT, identify_tile(">"))
def test_laser_left(self):
self.assertEqual(LASER_LEFT, identify_tile("<"))
def test_other(self):
self.assertEqual(None, identify_tile(" "))
class TestReflection(unittest.TestCase):
def setUp(self):
self.DIRECTION = LEFT
self.NOT_DIRECTIO
353 caracteres en Ruby:
¡314 277 caracteres ahora!
OK, 256 caracteres en Ruby y ahora estoy listo. Buen número redondo para detenerse. :)
247 caracteres No puedo parar
223 203 201 caracteres en Ruby
d=x=y=-1;b=readlines.each{|l|d<0&&(d="^>v<".index l[x]if x=l.index(/[>^v<]/)
y+=1)};loop{c=b[y+=[-1,0,1,0][d]][x+=[0,1,0,-1][d]]
c==47?d=[1,0,3,2][d]:c==92?d=3-d:c==35?(p !1;exit):c<?x?0:(p !!1;exit)}
Con espacios en blanco:
d = x = y = -1
b = readlines.each { |l|
d < 0 && (d = "^>v<".index l[x] if x = l.index(/[>^v<]/); y += 1)
}
loop {
c = b[y += [-1, 0, 1, 0][d]][x += [0, 1, 0, -1][d]]
c == 47 ? d = [1, 0, 3, 2][d] :
c == 92 ? d = 3 - d :
c == 35 ? (p !1; exit) :
c < ?x ? 0 : (p !!1; exit)
}
Ligeramente refactorizado:
board = readlines
direction = x = y = -1
board.each do |line|
if direction < 0
x = line.index(/[>^v<]/)
if x
direction = "^>v<".index line[x]
end
y += 1
end
end
loop do
x += [0, 1, 0, -1][direction]
y += [-1, 0, 1, 0][direction]
ch = board[y][x].chr
case ch
when "/"
direction = [1, 0, 3, 2][direction]
when "//"
direction = 3 - direction
when "x"
puts "true"
exit
when "#"
puts "false"
exit
end
end
C + ASCII, 197 characters:
G[999],*p=G,w,z,t,*b;main(){for(;(*p++=t=getchar()^32)>=0;w=w|t-42?w:p-G)z=t^86?t^126?t^28?t^30?z:55:68:56:75,b=z?b:p;for(;t=z^55?z^68?z^56?z^75?0:w:-w:-1:1;z^=*b)b+=t;puts(*b^88?"false":"true");}
This C solution assumes an ASCII character set, allowing us to use the XOR mirror trick. It''s also incredibly fragile - all the input lines must be the same length, for example.
It breaks under the 200 character mark - but dang it, still haven''t beaten those Perl solutions!
DO#
1020 characters.
1088 characters (added input from console).
925 characters (refactored variables).
875 characters (removed redundant Dictionary initializer; changed to Binary & operators)
Made a Point not to look at anyone else''s before posting. I''m sure it could be LINQ''d up a bit. And the whole FindLaser method in the readable version seems awfully fishy to me. But, it works and it''s late :)
Note the readable class includes an additional method that prints out the current Arena as the laser moves around.
class L{static void Main(){
A=new Dictionary<Point,string>();
var l=Console.ReadLine();int y=0;
while(l!=""){var a=l.ToCharArray();
for(int x=0;x<a.Count();x++)
A.Add(new Point(x,y),l[x].ToString());
y++;l=Console.ReadLine();}new L();}
static Dictionary<Point,string>A;Point P,O,N,S,W,E;
public L(){N=S=W=E=new Point(0,-1);S.Offset(0,2);
W.Offset(-1,1);E.Offset(1,1);D();
Console.WriteLine(F());}bool F(){
var l=A[P];int m=O.X,n=O.Y,o=P.X,p=P.Y;
bool x=o==m,y=p==n,a=x&p<n,b=x&p>n,c=y&o>m,d=y&o<m;
if(l=="//"){if(a)T(W);if(b)T(E);if(c)T(S);
if(d)T(N);if(F())return true;}
if(l=="/"){if(a)T(E);if(b)T(W);if(c)T(N);
if(d)T(S);if(F())return true;}return l=="x";}
void T(Point p){O=P;do P.Offset(p);
while(!("//,/,#,x".Split('','')).Contains(A[P]));}
void D(){P=A.Where(x=>("^,v,>,<".Split('','')).
Contains(x.Value)).First().Key;var c=A[P];
if(c=="^")T(N);if(c=="v")T(S);if(c=="<")T(W);
if(c==">")T(E);}}
Readable Version (not quite the final golf version, but same premise):
class Laser
{
private Dictionary<Point, string> Arena;
private readonly List<string> LaserChars;
private readonly List<string> OtherChars;
private Point Position;
private Point OldPosition;
private readonly Point North;
private readonly Point South;
private readonly Point West;
private readonly Point East;
public Laser( List<string> arena )
{
SplitArena( arena );
LaserChars = new List<string> { "^", "v", ">", "<" };
OtherChars = new List<string> { "//", "/", "#", "x" };
North = new Point( 0, -1 );
South = new Point( 0, 1 );
West = new Point( -1, 0 );
East = new Point( 1, 0 );
FindLaser();
Console.WriteLine( FindTarget() );
}
private void SplitArena( List<string> arena )
{
Arena = new Dictionary<Point, string>();
int y = 0;
foreach( string str in arena )
{
var line = str.ToCharArray();
for( int x = 0; x < line.Count(); x++ )
{
Arena.Add( new Point( x, y ), line[x].ToString() );
}
y++;
}
}
private void DrawArena()
{
Console.Clear();
var d = new Dictionary<Point, string>( Arena );
d[Position] = "*";
foreach( KeyValuePair<Point, string> p in d )
{
if( p.Key.X == 0 )
Console.WriteLine();
Console.Write( p.Value );
}
System.Threading.Thread.Sleep( 400 );
}
private bool FindTarget()
{
DrawArena();
string chr = Arena[Position];
switch( chr )
{
case "//":
if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
{
OffSet( West );
}
else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
{
OffSet( East );
}
else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
{
OffSet( South );
}
else
{
OffSet( North );
}
if( FindTarget() )
{
return true;
}
break;
case "/":
if( ( Position.X == Position.X ) && ( Position.Y < OldPosition.Y ) )
{
OffSet( East );
}
else if( ( Position.X == Position.X ) && ( Position.Y > OldPosition.Y ) )
{
OffSet( West );
}
else if( ( Position.Y == Position.Y ) && ( Position.X > OldPosition.X ) )
{
OffSet( North );
}
else
{
OffSet( South );
}
if( FindTarget() )
{
return true;
}
break;
case "x":
return true;
case "#":
return false;
}
return false;
}
private void OffSet( Point p )
{
OldPosition = Position;
do
{
Position.Offset( p );
} while( !OtherChars.Contains( Arena[Position] ) );
}
private void FindLaser()
{
Position = Arena.Where( x => LaserChars.Contains( x.Value ) ).First().Key;
switch( Arena[Position] )
{
case "^":
OffSet( North );
break;
case "v":
OffSet( South );
break;
case "<":
OffSet( West );
break;
case ">":
OffSet( East );
break;
}
}
}
Groovy @ 279 characers
m=/[<>^v]/
i={''><v^''.indexOf(it)}
n=[''<'':{y--},''>'':{y++},''^'':{x--},''v'':{x++}]
a=[''x'':{1},''//':{''v^><''[i(d)]},''/'':{''^v<>''[i(d)]},''#'':{},'' '':{d}]
b=[]
System.in.eachLine {b<<it.inject([]) {r,c->if(c==~m){x=b.size;y=r.size;d=c};r<<c}}
while(d==~m){n[d]();d=a[b[x][y]]()}
println !!d
House of Mirrors
Not an actual entry to the challenge, but I wrote a game based on this concept (not too long back).
It''s written in Scala, open-source and available here :
It does a little bit more; deals with colors and various types of mirrors and devices, but version 0.00001 did exactly what this challenge asks. I have lost that version though and it was never optimised for character count anyway.
JavaScript - 265 Characters
Update IV - Odds are this will be the last round of updates, managed to save a couple more characters by switching to a do-while loop and rewriting the movement equation.
Update III - Thanks to the suggestion by strager in regards to removing Math.abs() and putting the variables in the global name space, that coupled with some rearranging of the variable assignments got the code down to 282 characters.
Update II - Some more updates to the code to remove the use of != -1 as well as some better use of variables for longer operations.
Update - When through and made some changes by creating a reference to the indexOf function (thanks LiraNuna!) and removing parenthesis that were not needed.
This is my first time doing a code golf so I''m not sure how much better this could be, any feed back is appreciated.
Fully minimized version:
a;b;c;d;e;function f(g){a=function(a){return g.indexOf(a)};b=a("/n")+1;a=g[c=e=a("v")>0?e:e=a("^")>0?e:e=a("<")>0?e:a(">")];d=a=="<"?-1:a==">"?1:a=="^"?-b:b;do{e=d==-1|d==1;a=g[c+=d=a=="//"?e?b*d:d>0?1:-1:a=="/"?e?-b*d:d>0?1:-1:d];e=a=="x"}while(a!="#"^e);return e}
Original version with comments:
character; length; loc; movement; temp;
function checkMaze(maze) {
// Use a shorter indexOf function
character = function(string) { return maze.indexOf(string); }
// Get the length of the maze
length = character("/n") + 1;
// Get the location of the laser in the string
character = maze[loc = temp = character("v") > 0 ? temp :
temp = character("^") > 0 ? temp :
temp = character("<") > 0 ? temp : character(">")];
// Get the intial direction that we should travel
movement = character == "<" ? -1 :
character == ">" ? 1 :
character == "^" ? -length : length;
// Move along until we reach the end
do {
// Get the current character
temp = movement == -1 | movement == 1;
character = maze[loc += movement = character == "//" ? temp ? length * movement : movement > 0 ? 1 : -1 :
character == "/" ? temp ? -length * movement : movement > 0 ? 1 : -1 : movement];
// Have we hit a target?
temp = character == "x";
// Have we hit a wall?
} while (character != "#" ^ temp);
// temp will be false if we hit the target
return temp;
}
Web page to test with:
<html>
<head>
<title>Code Golf - Lasers</title>
<script type="text/javascript">
a;b;c;d;e;function f(g){a=function(a){return g.indexOf(a)};b=a("/n")+1;a=g[c=e=a("v")>0?e:e=a("^")>0?e:e=a("<")>0?e:a(">")];d=a=="<"?-1:a==">"?1:a=="^"?-b:b;do{e=d==-1|d==1;a=g[c+=d=a=="//"?e?b*d:d>0?1:-1:a=="/"?e?-b*d:d>0?1:-1:d];e=a=="x"}while(a!="#"^e);return e}
</script>
</head>
<body>
<textarea id="maze" rows="10" cols="10"></textarea>
<button id="checkMaze" onclick="alert(f(document.getElementById(''maze'').value))">Maze</button>
</body>
</html>
Perl 219
My perl version is 392 342 characters long (I had to handle the case of the beam hitting the laser):
Update , thanks Hobbs for reminding me of tr//
, it''s now 250 characters:
Update , removing the m
in m//
, changing the two while
loops brought a few savings; there''s now only one space required.
( L:it;goto L
is the same length as do{it;redo}
):
@b=map{($y,$x,$s)=($a,$-[0],$&)if/[<>^v]/;$a++;[split//]}<>;L:$_=$s;$x++if/>/;
$x--if/</;$y++if/v/;$y--if//^/;$_=$b[$y][$x];die"true/n"if/x/;die"false/n"if
/[<>^v#]/;$s=~tr/<>^v/^v<>/if////;$s=~tr/<>^v/v^></if////;goto L
I shaved some, but it barely just competes with some of these, albeit late.
It looks a little better as:
#!/usr/bin/perl
@b = map {
($y, $x, $s) = ($a, $-[0], $&) if /[<>^v]/;
$a++;
[split//]
} <>;
L:
$_ = $s;
$x++ if />/;
$x-- if /</;
$y++ if /v/;
$y-- if //^/;
$_ = $b[$y][$x];
die "true/n" if /x/;
die "false/n" if /[<>^v#]/;
$s =~ tr/<>^v/^v<>/ if ////;
$s =~ tr/<>^v/v^></ if ////;
goto L
Well... Honestly this should be self explanatory if you understand that the @b
is an array arrays of characters in each line, and can read the simple regexp and tr
statements.
C++: 388 characters
#include<iostream>
#include<string>
#include<deque>
#include<cstring>
#define w v[y][x]
using namespace std;size_t y,x,*z[]={&y,&x};int main(){string p="^v<>",s;deque<string>v;
while(getline(cin,s))v.push_back(s);while(x=v[++y].find_first_of(p),!(x+1));int
i=p.find(w),d=i%2*2-1,r=i/2;do while(*z[r]+=d,w==''/''?d=-d,0:w=='' '');while(r=!r,
!strchr("#x<^v>",w));cout<<(w==''x''?"true":"false");}
( 318 without headers)
Cómo funciona:
First, all lines are read in, then, the laser is found. The following will evaluate to 0
as long as no laser arrow was found yet, and the same time assign to x
the horizontal position.
x=v[++y].find_first_of(p),!(x+1)
Then we look what direction we found and store it in i
. Even values of i
are top/left ("decreasing") and odd values are bottom/right ("increasing"). According to that notion, d
("direction") and r
("orientation") are set. We index pointer array z
with orientation and add the direction to the integer we get. The direction changes only if we hit a slash, while it remains the same when we hit a back-slash. Of course, when we hit a mirror, then we always change orientation ( r = !r
).
Haskell, 395 391 383 361 339 characters (optimized)
Still uses a generic state machine, rather than anything clever:
k="<>^v"
o(Just x)=x
s y(h:t)=case b of{[]->s(y+1)t;(c:_)->(c,length a,y)}where(a,b)=break(flip elem k)h
r a = f$s 0 a where f(c,x,y)=case i(a!!v!!u)"x ///"["true",g k,g"v^><",g"^v<>"]of{Just r->r;_->"false"}where{i x y=lookup x.zip y;j=o.i c k;u=j[x-1,x+1,x,x];v=j[y,y,y-1,y+1];g t=f(j t,u,v)}
main=do{z<-getContents;putStrLn$r$lines z}
A readable version:
k="<>^v" -- "key" for direction
o(Just x)=x -- "only" handle successful search
s y(h:t)=case b of -- find "start" state
[]->s(y+1)t
(c:_)->(c,length a,y)
where (a,b)=break(flip elem k)h
r a = f$s 0 a where -- "run" the state machine (iterate with f)
f(c,x,y)=case i(a!!v!!u)"x ///"["true",g k,g"v^><",g"^v<>"] of
Just r->r
_->"false"
where
i x y=lookup x.zip y -- "index" with x using y as key
j=o.i c k -- use c as index k as key; assume success
u=j[x-1,x+1,x,x] -- new x coord
v=j[y,y,y-1,y+1] -- new y coord
g t=f(j t,u,v) -- recurse; use t for new direction
main=do
z<-getContents
putStrLn$r$lines z
I believe in Code Reuse, I''d use one of your code as an API :).
puts Board.new.validate(input)
32 characters /o/... wohoooo