Código Golf: Láser


152

El reto

El código más corto por recuento de caracteres para ingresar una representación 2D de una placa y generar 'verdadero' o 'falso' de acuerdo con la entrada .

El tablero está hecho de 4 tipos de fichas:

 # - 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 un solo 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.

El rayo láser dispara y viaja desde su origen a la dirección en la que apunta. Si un rayo láser golpea la pared, se detiene. Si un rayo láser golpea un espejo, rebota 90 grados en la dirección que apunta el espejo. Los espejos tienen dos lados, lo que significa que ambos lados son 'reflectantes' y pueden hacer rebotar un rayo de dos maneras. Si un rayo láser golpea el láser ( ^v><), se trata como una pared (el rayo láser destruye el proyector y, por lo tanto, nunca golpea el objetivo).

Casos de prueba

Entrada:
    ##########
    # / \ #
    # #
    # \ X#
    #> / #
    ########## 
Salida:
    cierto

Entrada:
    ##########
    # vx #
    # / #
    # / #
    # \ #
    ##########
Salida:    
    falso

Entrada:
    ##############
    # # #
    #> # #
    # # #
    # # X #
    # # #
    ##############
Salida:
    falso

Entrada:
    ##########
    # / \ / \ / \ #
    # \\ // \\\ #
    # // \ / \ / \\ #
    # \ / \ / \ / x ^ #
    ##########
Salida:
    cierto

El recuento de código incluye entrada / salida (es decir, programa completo).


84
IMMA CHARGIN 'MAH LAZER!
Ólafur Waage

37
Esto es increíble .
GManNickG

33
NO
CRUZES

49
@ GameFreak: Eso se está volviendo muy viejo.
Artelius

24
¿Es ese '^' realmente un tiburón con un maldito lazer en la cabeza?
Nathan Feger

Respuestas:


78

Perl, 166 160 caracteres

Perl, 251 248 246 222 214 208 203 201 193 190 180 176 173 170 166 -> 160 caracteres.

Solution tuvo 166 golpes cuando terminó este concurso, pero A. Rex ha encontrado un par de maneras de eliminar 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 %tuna tabla del tablero donde $t{99*i+j}se encuentra el carácter en la fila i , columna j . Luego,

%d=split//,'>.^1<2v3' ; ($r)=grep{$d|=$d{$t{$_}}}%t

busca en los elementos %tun carácter que coincida con > ^ <o v, y simultáneamente establece $dun valor entre 0 y 3 que indica la dirección inicial del rayo láser.

Al comienzo de cada iteración en el bucle principal, actualizamos $dsi el haz está actualmente en un espejo. XOR'ing by 3 da el comportamiento correcto para un \espejo y XOR'ing by 1 da el comportamiento correcto para un /espejo.

$d^=3*/\\/+m</>

A continuación, la posición actual $rse actualiza según la dirección actual.

$r+=(1,-99,-1,99)[$d] ; $_ = $t{$r}

Asignamos el carácter en la posición actual $_para hacer un uso conveniente de los operadores de coincidencia.

/[\/\\ ]/ && redo

Continúe si estamos en un espacio en blanco o un personaje espejo. De lo contrario, terminamos truesi estamos en el destino ( $_ =~ /x/) y de lo falsecontrario.

Limitación: puede no funcionar en problemas con más de 99 columnas. Esta limitación podría eliminarse a expensas de 3 caracteres más,


De acuerdo, llegó a 323 caracteres. = D
extraño

55
Puedo cambiar 99 a 1E5 para hacerlo muy robusto a expensas de 3 caracteres.
mafia

2
Su mejor solución sería más notable en la parte superior de la publicación.
extraño

13
¿Pero usando expresiones regulares para rotar el tablero? Eso fue de mal gusto. Eso es como un bono automático de 20 golpes.
mafia

1
@mobrule: guarde seis trazos: reordene la primera línea como s!.!$t{$s++}=$&!ge,$s=$r+=99for<>;, cambie %d=split//,.." to % d = .. = ~ /./ g , and change grep {..}% t` agrep..,%t
A. Rex

75

Perl, 177 Personajes

El primer salto de línea se puede eliminar; 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 una viga que se mueve hacia la derecha se encuentra con un {espacio vacío, espejo en ángulo hacia arriba, espejo en ángulo hacia abajo} se convierte en una {viga en movimiento hacia la derecha, viga en movimiento hacia arriba, viga en movimiento hacia abajo}. Inicialice $/en el camino - afortunadamente "6" no es un carácter de entrada válido.

$_ = <>;

Lea la pizarra en $_.

$s="#";

$ses el símbolo de lo que sea que esté sentado el rayo encima. Dado que el emisor láser debe tratarse como una pared, para empezar, configúrelo como una pared.

if (tr/v<^/>v</) {
  my $o;
  $o .= "\n" while s/.$/$o .= $&, ""/meg;
  tr,/\\,\\/, for $o, $s;
  $_ = $o;
}

Si el rayo láser apunta hacia cualquier lado, excepto a la derecha, gire su símbolo y luego gire toda la placa en su lugar (también girando los símbolos de los espejos). Es una rotación a la izquierda de 90 grados, lograda efectivamente invirtiendo las filas mientras se transponen filas y columnas, en un poco diabólico s///econ efectos secundarios. En el código de golf, el tr está escrito en la forma y'''que me permite omitir una barra diagonal inversa.

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 el haz. En cualquier caso, vuelva a colocar el "símbolo guardado" en la ubicación de la viga anterior y coloque lo que acabamos de sobrescribir en el símbolo guardado.

redo;

Repita hasta la terminación. {...;redo}es dos caracteres menos que for(;;){...}y tres menos que while(1){...}.


44
Gire el tablero ... Loco. Regexp ... más loco. O_o
extraño

39
Tu ... tu monstruo!
LiraNuna

44
LiraNuna: Elijo tomar eso como un cumplido.
hobbs

21
El golf ha terminado. ¿Cómo puedes vencer la rotación de un tablero 2D con expresiones regulares?
Konrad Rudolph

13
wtf? los programadores de perl son magos.
Johannes Schaub - litb

39

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 comprende C. Solo una advertencia.

#define M(a,b)*p==*#a?m=b,*p=1,q=p:

Esta pequeña macro verifica si el carácter actual ( *p) es igual a lo que aesté en forma de carácter ( *#a). Si son iguales, establezca el vector de movimiento en b( m=b), marque este carácter como un muro ( *p=1) y establezca el punto de partida en la ubicación actual ( q=p). Esta macro incluye la parte "más".

*q,G[999],*p=G;
w;

Declarar algunas variables. * qes la ubicación actual de la luz. * Ges el tablero de juego como una matriz 1D. * pes la ubicación de lectura actual al rellenar G. * wes el ancho del tablero.

main(m){

Obvia main. mes una variable que almacena el vector de movimiento. (Es un parámetro maincomo una optimización).

    for(;(*++p=getchar())>0;)

Recorre todos los personajes, rellenando Gcon p. Saltar G[0]como una optimización (no es necesario desperdiciar un personaje escribiendo pnuevamente en la tercera parte de la for).

        M(<,-1)
        M(>,1)
        M(^,-w)
        M(v,w)

Utilice la macro antes mencionada para definir el lazer, si es posible. -1y 1corresponden a izquierda y derecha, respectivamente, -wy warriba y abajo.

        !w&*p<11
            ?w=p-G
            :0;

Si el carácter actual es un marcador de fin de línea (ASCII 10), establezca el ancho si aún no se ha establecido. El omitido G[0]nos permite escribir en w=p-Glugar de w=p-G+1. Además, esto termina la ?:cadena de la M's.

    for(;
        q+=m,

Mueve 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=0termina el ciclo). De lo contrario, no haga nada (noop; m=m)

    );
}

8
Ugh! Estaba trabajando en una solución C cuando sonó la alarma de incendio en mi complejo de apartamentos. Ahora me dieron una paliza. Buena solución sin embargo.
rlbond

Creo que usar una variable temporal para sus pasos de intercambio y negación le ahorraría un par de caracteres.
Artelius

@Artelius: Sí, me di cuenta de eso y de algunas otras cosas. Gracias.
extraño

1
En realidad, a TCC no le gustan las declaraciones y errores sin tipo con g.c:3: declaration expected:(
Mark Rushakoff

2
La eliminación putsde la declaración ayudó, pero no fue suficiente para reducirla a 170. Sin embargo, 209 es bastante bueno, así que creo que lo dejaré así. Gracias por su ayuda chicos. Realmente lo aprecio. =] (¡Cualquier cosa para destronar a esas brujas de Perl!)
extraño

36

Apuesto a que la gente ha estado esperando este por MUCHO tiempo. (¿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!

Pesa la friolera de 973 caracteres (o 688 si eres lo suficientemente caritativo como para ignorar los espacios en blanco, que solo se usa para formatear y no hace nada en el código real).

Advertencia : escribí mi propio intérprete Befunge-93 en Perl hace poco tiempo, y desafortunadamente esto es todo lo que realmente he tenido tiempo para probarlo. Tengo una confianza razonable en su corrección en general, pero podría tener una limitación extraña con respecto a EOF: dado que el <>operador de Perl devuelve undef al final del archivo, esto se procesa como un 0 en el contexto numérico. Para implementaciones basadas en C donde EOF tiene un valor diferente (digamos -1), 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 operación de Befunge, marque aquí .

Befunge es un lenguaje basado en la pila, pero hay comandos que le permiten a uno escribir caracteres en el código Befunge. Aprovecho eso en dos lugares. Primero, copié toda la entrada en la placa Befunge, pero ubiqué 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 central 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 las coordenadas x e y de la fuente del rayo láser cuando se encuentra; y la columna final (con el carácter 'a' en ella) finalmente se sobrescribe para contener la dirección del haz actual, que obviamente cambia a medida que se rastrea la trayectoria del haz.

El programa comienza colocando (0,27) como la posición inicial del cursor. Luego, la entrada se lee un carácter a la vez y se coloca en la posición del cursor; las nuevas líneas simplemente causan que la coordenada y aumente y la coordenada x regrese a 0, al igual que un retorno de carro real. Eventualmente, el intérprete lee undef y ese valor de 0 caracteres se usa para señalar el final de la entrada y pasar a los pasos de iteración del láser. Cuando se lee el carácter láser [<> ^ v], eso también se copia en el repositorio de memoria (sobre el carácter 'a') y sus coordenadas se copian en las columnas a la izquierda.

El resultado final de todo esto es que todo el archivo se copia básicamente en el código Befunge, un poco por debajo del código real atravesado.

Posteriormente, la ubicación del haz se copia nuevamente en las ubicaciones del cursor y se realiza la siguiente iteración:

  • Verifique la dirección del haz actual e incremente o disminuya las coordenadas del cursor apropiadamente. (Hago esto primero para evitar tener que lidiar con la caja de la esquina del rayo láser justo en el primer movimiento).
  • Lee el personaje en ese lugar.
  • Si el carácter es "#", ponga nueva línea y "falso" en la pila, imprima y finalice.
  • Compárelo con todos los caracteres de la viga [<> ^ v]; si hay una coincidencia, también imprima "falso \ n" y finalice.
  • Si el personaje es un espacio, vacía la pila y continúa.
  • Si el personaje es una barra diagonal, obtenga la dirección del haz en la pila y compárelo con cada uno de los personajes de la dirección. Cuando se encuentra uno, la nueva dirección se almacena en ese mismo lugar en el código y el ciclo se repite.
  • Si el carácter es una barra invertida, haga básicamente lo mismo que el anterior (excepto con la asignación adecuada para la barra invertida).
  • Si el personaje es 'x', hemos dado en el blanco. Imprima "verdadero \ n" y salga.
  • Si el carácter no es ninguno de estos, imprima "error \ n" y salga.

Si hay suficiente demanda, intentaré señalar exactamente en qué parte del código se logra todo esto.


14
+1: solo porque podría malinterpretarse como un EXE abierto en el bloc de notas.
Kyle Rosendo

1
Um ... santo **** Me he metido con Befunge, y esto es realmente impresionante.
Almo

¡Codifique el golf en idiomas ofuscados ... como la mantequilla de maní y la cayena!
wberry

29

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

54
¡REALMENTE PUEDO LEER ESTE! ¡MILAGROSO!
Jeff Atwood

17
El código Java / C # golf se cuenta por líneas, no por caracteres. Esa es la desventaja.
Nathan Feger

3
@strager no es deprimente en 3 años cuando te contratan para mantener el código y el desarrollador original hace mucho que se fue.
Nathan Feger

Esto falla al usar F # en Visual Studio 2010. Seq.to_list no existe (ok, lo cambió a toList), y luego la Línea 25, coincidencia de patrón incompleta.
Russell

2
Sí, cambie to_list a toList ahora. La advertencia de coincidencia incompleta está bien; es código de golf, así que no hice código como: | _ -> falla con "imposible"
Brian

29

Golfscript - 83 caracteres (mashup mío y 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 solo está ahí 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 resuelve la ubicación inicial y la dirección.
La segunda línea da un giro cuando el láser golpea un espejo.


18

353 caracteres en Ruby:

314 277 caracteres ahora!

OK, 256 caracteres en Ruby y ahora he terminado. Bonito 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

Pero ... ¡puede cambiar el nombre cha Ccualquier otra letra de char para guardar 2 caracteres!
LiraNuna

Ok ok, okay ... Realmente me di cuenta de que toda la variable es innecesaria ya que solo la uso una vez. Esto y un par de otras mejoras lo redujeron a 247 caracteres.
Jeremy Ruten

1
No i++(en lugar de i+=1)?
LiraNuna

66
No Puedes hacer ++ i, pero solo lo hace realmente tan positivo como solía ser.
DigitalRoss

Me gusta la versión comprimida: #; p
SeanJA

17

Pitón

294 277 253 240 232 caracteres incluyendo nuevas líneas:

(el primer carácter 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

Había olvidado que Python incluso tenía puntos y comas 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, aumentando 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 de modo que el índice de un carácter de dirección láser 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 la viga sale de diferentes mosaicos. 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 res el número de fila actual de la entrada, pes la posición actual del rayo láser.

while b[-1]: dejar de cargar datos de la placa cuando raw_input devuelve una cadena vacía

b+=[raw_input()];r+=1 agregue la siguiente línea de entrada al tablero e incremente el contador de filas

for g in l: adivina cada dirección láser a su vez

c=b[r].find(g) configure la columna en la ubicación del láser o -1 si no está en la línea (o apunta en una dirección diferente)

if-1<c:p=c+1j*r;d=gSi encontramos un láser, establezca la posición py dirección actuales d. des uno de los caracteres enl

Después de cargar la placa b, la posición py dirección actuales dse han establecido en las de la fuente láser.

while' '<d: el espacio tiene un valor ASCII más bajo que cualquiera de los símbolos de dirección, por lo que lo usamos como un indicador de detención.

z=l.find(d);índice de la dirección actual char en la lcadena. zse utiliza más adelante para determinar la nueva dirección del haz utilizando la xtabla y para incrementar la posición.

p+=1j**z;incrementar la posición usando una potencia de i. Por ejemplo, l.find('<')==2-> i ^ 2 = -1, que se movería a la izquierda una columna.

c=b[int(p.imag)][int(p.real)]; lee el personaje en la posición actual

d=x.get(c,' '*4)[z]Busque la nueva dirección de la viga en la tabla de transformación. Si el carácter actual no existe en la tabla, configúrelo den espacio.

print'#'<c imprima falso si nos detenemos en otra cosa que no sea el objetivo.


9
p+=1j**z: Eso es dulce.
dmckee --- ex-gatito moderador

16

Este es 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, me preguntaba cómo algunas de las construcciones de F # que utilizó podrían estar representadas 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 experimentar un poco, 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));

8
Dios mio. Qué buen ejemplo para demostrar cuán poderosos se han convertido linq y c #. 1+ porque soy un gran fan de C #. x)
Emiswelt

16

F #, 255 caracteres (¡y aún bastante legible!):

Ok, después de una noche de descanso, mejoré mucho esto:

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])

Hablemos de ello línea por línea.

Primero, sorba toda la entrada en una gran matriz unidimensional (las matrices 2D pueden ser malas para el golf de código; solo use una matriz 1D y sume / 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 función 'siguiente' '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 qué-nueva-dirección-si-golpeamos-una-barra 's' son calculados 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 oblicua, la nueva dirección es 2 (Derecha).

Ahora volvemos a la siguiente función 'n' con (1) el siguiente índice ("c + e" - actual más épsilon), 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 lookahead char es una barra oblicua, la nueva dirección es 's'. Si se trata de una barra diagonal inversa, la nueva dirección es de 3 s (nuestra elección de codificación 0123 hace que esto funcione). Si es un espacio, seguimos yendo en la misma dirección 'd'. Y si se trata de cualquier otro personaje 'c', entonces el juego termina, imprimiendo 'verdadero' si el carácter era 'x' y falso de lo contrario.

Para comenzar, llamamos a la función recursiva 'n' con la posición inicial 'c' y la dirección de inicio (que realiza la codificación inicial de la dirección en 0123).

Creo que probablemente todavía pueda eliminar algunos caracteres más, pero estoy bastante satisfecho con esto de esta manera (y 255 es un buen número).


11

Con un peso de 18203 caracteres, hay una solución de Python que puede:

  • hacer frente a los espejos fuera de la 'habitación'
  • calcule la trayectoria cuando no hay 'sala' sobre la base de las limitaciones 2D (la especificación dice mucho sobre lo que debe estar en la 'sala' pero no si la sala tiene que existir)
  • informar sobre errores

Todavía necesita ser arreglado un poco y no sé si la física 2D dicta que el rayo no puede cruzarse solo ...

#!/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()

Un script bash para mostrar el informe de errores de color:

#!/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#
#\\^/\\ /  #
##########"

Las pruebas unitarias utilizadas en el desarrollo:

#!/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

66
La física del láser dicta que el rayo puede cruzarse solo. El comentario anterior es una referencia cultural importante.
dmckee --- ex-gatito moderador

55
Tortuga y el enfoque de liebre para codificar golf. Entregue algo con obviamente demasiados caracteres (91 veces más que el ganador actual) pero preste atención a cada letra de la especificación. Sin embargo, lento y constante generalmente me da menos trabajo por contrato.
Metalshark

Parece que a tu unidad de prueba le falta alguna parte. Está cortado en "self.NOT_DIRECTIO"
BioGeek

@BioGeek: alcanza el límite de la duración de la publicación;). Además de las pruebas de estilo BASH muestran el resaltado de color.
Metalshark

11

Ruby, 176 caracteres

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}

Utilicé una máquina de estado simple (como la mayoría de los carteles), nada lujoso. Seguí reduciéndolo usando cada truco que se me ocurría. El XOR bit a bit utilizado para cambiar la dirección (almacenado como un entero en la variable c) fue una gran mejora con respecto a los condicionales que tenía en versiones anteriores.

Sospecho que el código se incrementa xy ypodría acortarse. Aquí está la sección del código que hace el incremento:

c<2&&y+=c*2-1;c>1&&x+=(c-2)*2-1

Editar : pude acortar un poco lo anterior:

c<2&&y+=c*2-1;c>1&&x+=2*c-5

La dirección actual del láser cse almacena de la siguiente manera:

0 => arriba
1 => abajo
2 => izquierda
3 => derecha

El código se basa en este hecho para aumentar xy yen la cantidad correcta (0, 1 o -1). Intenté reorganizar qué números se asignan a cada dirección, buscando un arreglo que me permitiera hacer una manipulación bit a bit para incrementar los valores, porque tengo la sensación de que sería más corto que la versión aritmética.


9

C # 3.0

259 caracteres

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;}}

Ligeramente más legible:

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;
    }
}

El principal desperdicio de caracteres parece estar en encontrar el ancho del mapa y la posición de la fuente láser. ¿Alguna idea de cómo acortar esto?


No estoy seguro de si esto es más corto, pero es mi oportunidad de encontrar el láser y encontrar el ancho: usando L = List <string>; usando P = System.Drawing.Point; usando L = List <string>; L r = new L () {"v", "<", ">", "^"}; P p = new P (); r.ForEach (a => {int c = 0; v.ForEach (s => {c ++ ; if (s.IndexOf (a)! = - 1) {pX = s.IndexOf (a); pY = c;}});}); int l = v [0] .Length; v es una Lista <cadena> que contiene la tabla, y genera un Punto que representa la posición del láser + un ancho que representa int
RCIX

mejor: usando L = List <string>; L l = new L (4) {"v", "<", ">", "^"}; var point = new {x = 0, y = 0}; int c = 0; l.ForEach (a => {m.ForEach (s => {if (s.IndexOf (a)! = - 1) {point = new {x = s.IndexOf (a), y = c};}}); c ++;}); int w = m [0] .Longitud;
RCIX

44
Problemas requiere un programa completo, no una función.
extraño

¿ while(1)
Qué tal

9

C + ASCII, 197 caracteres:

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");}

Esta solución C asume un conjunto de caracteres ASCII, lo que nos permite utilizar el truco del espejo XOR. También es increíblemente frágil: todas las líneas de entrada deben tener la misma longitud, por ejemplo.

Se rompe por debajo de la marca de 200 caracteres, pero ¡dang, todavía no ha superado esas soluciones Perl!


= O! +1! Me molesta golpearme. =]
extraño

2
La mayoría de las buenas soluciones aquí hacen la suposición de "todas las líneas tienen la misma longitud". Todo es justo en el golf y la guerra.
hobbs

Si fuera necesario, las líneas no tenían la misma longitud, agregaría un caso de prueba para ello. pero claramente dije que fue intencional :)
LiraNuna

9

Golfscript (83 caracteres)

Hola gnibbler!

:\'><v^'.{\?}%{)}?:P@=?{:O[1-1\10?).~)]=P+
:P\=' \/x'?[O.2^.1^'true''false']=.4/!}do

3
golfscript: perl ~ = 1: 1.7
John La Rooy

9

Python - 152

Lee la entrada de un archivo llamado "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

Para leer de stdin, reemplace la primera línea con esto

import os;A=os.read(0,1e9)

Si necesita minúsculas verdadero / falso, cambie la última línea a

print`D<5`.lower()

Cuántos caracteres se tarda en cambiar Truea truey Falsea false? ;-)
mob

¿No podría eliminar 1 carácter cambiando "imprimir D<5" a "imprimir D <5"? ¿O hay algo que me falta?
Ponkadoodle

@wallacoloo, seguro que sí. Solo es necesario para las minúsculas verdadero / falso
John La Rooy

7

JavaScript: 265 caracteres

Actualización IV : las probabilidades son que esta será la última ronda de actualizaciones, lograron salvar un par de caracteres más al cambiar a un bucle do-while y reescribir la ecuación de movimiento.

Actualización III : gracias a la sugerencia de Strager con respecto a eliminar Math.abs () y colocar las variables en el espacio de nombre global, que junto con una cierta reorganización de las asignaciones de variables redujo el código a 282 caracteres.

Actualización II : algunas actualizaciones más del código para eliminar el uso de! = -1, así como un mejor uso de variables para operaciones más largas.

Actualización : cuando terminó e hizo algunos cambios al crear una referencia a la función indexOf (¡gracias LiraNuna!) Y eliminar paréntesis que no eran necesarios.

Esta es la primera vez que hago un código de golf, así que no estoy seguro de cuánto mejor podría ser, cualquier comentario es apreciado.

Versión completamente minimizada:

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}

Versión original con comentarios:

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;
    }

Página web para probar con:

<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>

¿Cómo toma entrada? Quiero probar y verificar esto. Además, puede guardar muchos caracteres si guarda una referencia en a.indexOf
LiraNuna

Reemplazar index != -1con index > 0por favor! (Esperemos que nadie ponga el lazer en la esquina superior izquierda para 0que no se devuelva. =]) Puede encadenar las vardeclaraciones o deshacerse de ellas por completo (colocando las variables en el espacio de nombres global). Creo que Math.abs(m)==1puede ser reemplazado por m==-1|m==1. Puede movement = ...; location += movementser optimizado para location += movement =?
extraño

@ strager: acabo de ver tu comentario, parece que lo publicaste mientras estaba actualizando el código, hasta 300 caracteres. Veré qué puedo hacer con la eliminación de Math.abs ().
rjzii

function(a){return g.indexOf(a)}se puede reemplazar con function(a)g.indexOf(a)versiones recientes de JavaScript.
user1686

6

Casa de los espejos

No es una entrada real al desafío, pero escribí un juego basado en este concepto (no hace mucho tiempo).

Está escrito en Scala, de código abierto y disponible aquí :

Hace un poquito más; trata de colores y varios tipos de espejos y dispositivos, pero la versión 0.00001 hizo exactamente lo que este desafío pide. Sin embargo, he perdido esa versión y nunca fue optimizada para el recuento de caracteres de todos modos.


¿Le sería posible cargar una versión compilada que funciona en Windows sin tener que instalar scala?
Milán

Hay una versión con bibliotecas Scala incluidas. Mira la lista de descargas. Pero de todos modos, si ya ha instalado Scala, me alegra haberlo hecho :)
HRJ

6

c (K&R) 339 caracteres necesarios después de más sugerencias de un extraño.

El físico en mí notó que las operaciones de propagación y reflexión son invariantes de inversión de tiempo, por lo que esta versión arroja rayos desde el objetivo y comprueba si llegan al emisor láser.

El resto de la implementación es muy sencillo y se toma más o menos exactamente de mi esfuerzo anterior y avanzado.

Comprimido:

#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");}

Sin comprimir (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");
}

No hay validación de entrada, y una entrada incorrecta puede enviarlo a un bucle infinito. Funciona correctamente con una entrada no mayor de 99 por 99. Requiere un compilador que vinculará la biblioteca estándar sin incluir ninguno de los encabezados. Y creo que ya terminé , extraño me ha superado considerablemente, incluso con su ayuda.

Espero que alguien demuestre una forma más sutil de realizar la tarea. No hay nada de malo en esto, pero no es magia profunda.


No es necesario =0en los globales, ya que se inicializan a 0 de forma predeterminada. Reemplace las constantes de caracteres con su equivalente en decimal. Use en >0lugar de !=EOFpara verificar contra EOF (y \0). Probablemente pueda #defineeliminar parte del código casecomo hice con if's. No es necesario el extra \nen el putscomo putsdebe imprimir una nueva línea de todos modos. for(;;)es más corto que while(1). Espero que esto ayude. =]
extraño

@strager: Gracias. Siempre me vienen a éstos de forma iterativa, porque no pienso de esa manera ...
dmckee --- ex-moderador gatito

2
"There is no input validation"- No debería haber ninguno. Para facilitar a los golfistas, se supone que la entrada siempre está "limpia" a menos que se especifique lo contrario.
LiraNuna

@dmckee, no te preocupes, los profesionales de Code Golf también trabajamos iterativamente. Sin embargo, generalmente usamos algunos trucos desde el principio (como la mitad de los que mencioné), pero eso viene con la experiencia. =]
extraño

A menos que cuente mal, el programa tiene 390 caracteres, no 380.
extraño

6

Ruby - 146 caracteres

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

5

PostScript , 359 bytes

Primer intento, mucho margen de mejora ...

/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 =

4

Haskell, 395 391 383 361 339 caracteres (optimizado)

Todavía usa una máquina de estado genérica, en lugar de algo inteligente:

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}

Una versión legible:

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


3

C ++: 388 caracteres

#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 sin encabezados)


Cómo funciona:

Primero, se leen todas las líneas, luego se encuentra el láser. Lo siguiente evaluará 0mientras no se haya encontrado ninguna flecha láser todavía, y al mismo tiempo asigne a xla posición horizontal.

x=v[++y].find_first_of(p),!(x+1)

Luego miramos en qué dirección encontramos y lo almacenamos i. Los valores pares de ison superior / izquierda ("decreciente") y los valores impares son inferior / derecha ("creciente"). Según esa noción, se establecen d("dirección") y r("orientación"). Indexamos la matriz de punteros zcon orientación y agregamos la dirección al entero que obtenemos. La dirección cambia solo si golpeamos una barra diagonal, mientras que permanece igual cuando golpeamos una barra diagonal inversa. Por supuesto, cuando golpeamos un espejo, siempre cambiamos de orientación ( r = !r).


Me estás haciendo hacer mi propia solución C ++. =]
extraño

2
@strager, esto se está volviendo aburrido. Hagamos una solución que muestre "verdadero" o "falso" en tiempo de compilación xD
Johannes Schaub - litb

explicación adicional ya que creo que lo mantendré en esto :)
Johannes Schaub - litb

2

Groovy @ 279 personajes

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

2

C#

1020 caracteres.
1088 caracteres (entrada agregada desde la consola).
925 caracteres (variables refactorizadas).
875 caracteres (se eliminó el inicializador de diccionario redundante; se cambió a Binario y operadores)

Hizo un punto para no mirar a nadie más antes de publicar. Estoy seguro de que podría ser LINQ'd un poco. Y todo el método FindLaser en la versión legible me parece muy sospechoso. Pero funciona y es tarde :)

Tenga en cuenta que la clase legible incluye un método adicional que imprime la Arena actual a medida que el láser se mueve.

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);}}

Versión legible (no es la versión final de golf, pero la misma premisa):

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;
        }
    }
}

2
El programa debe tomar entrada. Más comúnmente de stdin.
LiraNuna

0

Perl 219
Mi versión Perl es 392 342 caracteres de longitud (que tenía que manejar el caso del haz de golpear el láser):
Actualización , gracias Hobbs por recordarme tr//, ahora es 250 caracteres:
Actualización , eliminación de la men m//, el cambio de los dos whilebucles traídas algunos ahorros; ahora solo se requiere un espacio.
( L:it;goto Ltiene la misma longitud que 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

Me afeité un poco, pero apenas compite con algunos de estos, aunque tarde.
Se ve un poco mejor como:

#!/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

Bueno ... Sinceramente, esto debería explicarse por sí mismo si comprende que @bhay una matriz de matrices de caracteres en cada línea, y puede leer la trexpresión regular simple y las declaraciones.


Consejo: puedes acortar tu código espejo hacia arriba. $_=$s;tr/^v<>/<>^v/y $_=$s;tr/v^<>/<>^v/respectivamente. Además, no es necesario el men m//.
hobbs

Lo sentimos, haz el segundo$_=$s;tr/v^></<>^v/;
hobbs

Todavía tiene varios if m/.../que podrían estar if/.../guardando dos caracteres por pop.
hobbs

Puede usar en y///lugar de tr///guardar dos caracteres.
Platinum Azure

0

F # - 454 (o por ahí)

Llegué un poco tarde al juego, pero no puedo resistirme a publicar mi segundo intento.

Actualizar modificada ligeramente. Ahora se detiene correctamente si se golpea el transmisor. Pellizcó la idea de Brian para IndexOfAny (lástima que la línea sea tan detallada). En realidad no he logrado resolver cómo hacer que ReadToEnd regrese de la consola, así que estoy confiando un poco en eso ...

Estoy satisfecho con esta respuesta, como si fuera bastante corta, todavía es bastante legible.

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))

Son decoracion. Mira mis otros desafíos, es solo una cuestión de formato.
LiraNuna

@LiraNuna, bien, resulta que esta iteración los come de todos modos :)
Benjol

Sería bueno compararlo con una implementación 1-d. Simplemente suma / resta 1 para izquierda y derecha y suma / resta w para arriba y abajo. Espero que ahorres bastantes caracteres
John La Rooy

@gnibbler, Brian ya lo hizo, no estoy seguro de poder vencerlo, pero podría intentarlo.
Benjol
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.