Fiesta de portal de espejo láser

27

Un tablero de 2D será contener los siguientes objetos:

  • ^, >, v, O <: un emisor láser hacia arriba, derecha, abajo, o hacia la izquierda, respectivamente. Puede haber más de uno. Los láseres viajarán en línea recta en el espacio vacío (el espacio vacío se representa con un punto .). Los láseres no pasan a través de los emisores.
  • *: Un objetivo. Los láseres atraviesan objetivos. Puede haber más de uno.

El tablero también puede contener los siguientes objetos:

  • @: Una pared sólida. El láser no pasará por aquí.
  • \: Un reflector inclinado a la izquierda . Cambia la dirección de los láseres según la siguiente tabla:

    Direction laser is travelling     Direction of laser after hitting reflector
    Up                                Left
    Right                             Down
    Down                              Right
    Left                              Up
    

    Debería ser bastante intuitivo en cuanto a cómo funcionan los reflectores. Imagínelos como un espejo real de dos lados y las instrucciones deben ser claras.

  • /: Un reflector inclinado a la derecha . Cambia la dirección de los láseres según la siguiente tabla:

    Direction laser is travelling     Direction of laser after hitting reflector
    Up                                Right
    Right                             Up
    Down                              Left
    Left                              Down
    
  • 1, 2, 3... 9: Un portal . El número indica el canal del portal: habrá exactamente dos portales del mismo canal (por ejemplo, no habrá tres 1). El portal cambia la posición de los láseres a la posición del otro portal del mismo canal. Por ejemplo:

    >     1     @     1     *
    

    El láser golpeará el objetivo porque cuando golpea el primero 1, se teletransporta al segundo 1en el otro lado. Los láseres retienen la misma dirección en la que estaban antes.

    Un portal no teletransportará el láser a un portal de un canal diferente (es decir 1, a no teletransportará el láser a un 9.

Su programa recibirá una representación 2D del tablero como entrada. El tablero siempre tendrá forma rectangular. La salida debería ser Truesi todos los objetivos tienen láser pasando a través de ellos, o de Falseotra manera.

Aquí hay algunos casos de prueba:

  1. Entrada

    >....\
    ..*...
    >./../
    ..*...
    

    Salida

    True
    
  2. Entrada

    >..........\
    1........../
    2..........1
    3..........2
    4..........3
    5..........4
    6..........5
    7..........6
    8..........7
    9..........8
    *..........9
    

    Salida

    True
    
  3. Entrada

    >.@............*
    >..@...........*
    >...@..........*
    >....@.........*
    >.....@........*
    >...*..@........
    >.......@......*
    

    Salida

    False
    
  4. Entrada

    ../\.
    >./**
    

    Salida

    False
    
  5. Entrada

    /.......*.......\/3.....
    @..............//\.\....
    *.............2\.1\/\...
    \..............///.....<
    .........*...//\\/.....\
    >.............\.1.///.4.
    4.......*/...\2\/3/\/..^
    

    Salida

    True
    
  6. Entrada

    vvvvvvvvvvvvvvvvv
    \\\\\\\\\\\\\\\\\
    /////////////////
    \\\\\\\\\\\\\\\\\
    /////////////////
    \\\\\\\\\\\\\\\\\
    /////////////////
    *****************
    

    Salida (observe el objetivo en el extremo derecho)

    False
    
Ajenjo
fuente
¿No tendría más sentido si un reflector inclinado a la derecha (/) cambiara la dirección de un rayo láser de izquierda (←) a abajo (↓)?
aprensivo ossifrage
@squeamish ossifrage Lo siento, no entiendo tu pregunta. ¿Qué regla de reflexión sobre la mesa reflector se inclina a la izquierda cree usted que es incorrecto?
absenta
Creo que te confundiste a la izquierda y a la derecha
aprensivo ossifrage
1
¿Qué sucede si el láser alcanza el límite de la cuadrícula?
DavidG
2
@DavidG Nada, o se recupera de la forma en que vino. (Estos son equivalentes en este caso). No se 'ajusta' como se puede ver en el ejemplo 6.
Dennis Jaheruddin

Respuestas:

8

Pitón, 310 302 287 278 277 260

No es radicalmente diferente a la publicación de Python existente, pero creo que tiene uno o dos trucos notables. También maneja la entrada "sin terminación", como 1>1. EDITAR : ¡Uy! Los emisores bloquean los láseres.

def t(b):
 w=len(b[0])+1;B=list('@'*w+'@'.join(b));i=l=len(B);C="<>^v@"
 while i:
    j=l-i;i-=1;d=C.find(B[j]);c='.'
    while c not in C:
     if'+'>c:B[j]='.'
     if'0'<c<C:j=(B*2).index(c,j+1)%l
     elif'.'<c:d^=2+(c<C)
     j-=[1,-1,w,-w,j][d];c=B[j%l]
 return'*'not in B

t toma una lista de cadenas (las líneas de entrada) y devuelve un resultado booleano.

Aquí hay un buen gif del código que se está jugando:

ingrese la descripción de la imagen aquí

EDITAR : impresionante gif cortesía de Will. Gracias Will!

Ana
fuente
La especificación especifica que "Los láseres no pasan a través de los emisores". así 1>1terminará. No he podido encontrar algo que no termine, aunque no he puesto mucho esfuerzo en ello y asumí que no sucede para mi implementación. Por supuesto, reconsideraré si alguien puede presentar uno.
VisualMelon
44
@VisualMelon: las reglas son simétricas en el tiempo, excepto en los lugares donde nacen o mueren los láseres, lo que significa que todo tiene que terminar (ya que siempre se puede rastrear de forma única hasta el punto donde nació, y los emisores no pueden ser parte de un bucle).
Micah
@Micah jeje, gracias por una explicación adecuada, como dije, fui con intuición y no me preocupé mucho por eso, gracias por poner otra herramienta en mi caja.
VisualMelon
Sí, lo leí mal.
Ell
Felicitaciones a Ell! Muy bien hecho. Creo que puede eliminar algunos bytes más utilizando el hecho de que .find(d)devuelve -1 si no se encuentra. Si elimina la if-1<d:declaración y en su lugar lo hace j+=[-1,1,w,-w,-i][d]en la parte superior del ciclo while, un -1 no encontrado se convertirá en la adición del último elemento de esa matriz j, lo que hará j0, que sabemos que es @...?
Will
7

Perl, 647

Este es mi primer intento en el código de golf, y estoy un poco avergonzado de que ni siquiera supere el puntaje de C #, pero pensé que sería interesante (o divertido, o simplemente masoquista) hacer todo esto como un serie de sustituciones de expresiones regulares. (También pensé que sería divertido repasar mi Perl, pero al final lamentaba profundamente no haberlo implementado en Ruby o Python).

No he hecho muchas pruebas, pero creo que debería manejar todos los casos.

La cuadrícula se ingresa a través de STDIN. Debe haber al menos una nueva línea en la entrada (es decir, una sola fila sin una nueva línea no funcionará).

%s=(d,'[|+#$vk%ZX]',u,'[|+#$^W%KX]',r,'[-G+#>k%KX]',l,'[-G+#<W%ZX]');%o=(d,'[-.*G/k\\\\Z',u,'[-.*G/W\\\\K',r,'[|.*$\\\\/kK',l,'[|.*$\\\\/ZW');for$d(d,u,r,l){$o{$d}.='123456789qwertyuio]'}%u=(d,'.|-+*$G#/Wk%\KZX',u,'.|-+*$G#/kW%\ZKX',r,'.-|+*G$#/Wk%\ZKX',l,'.-|+*G$#/kW%\KZX');@q=split//,"qwertyuio";local$/;$_=<STDIN>;for$i(1..9){$m{$i}=$q[$i-1];$m{$m{$i}}=$i;s/$i/$m{$i}/e}/.*?\n/;$l='.'x((length$&)-1);do{$c=0;for$d(d,u,r,l){%p=(d,"(?<=$s{d}$l)$o{d}",u,"$o{u}(?=$l$s{u})",r,"(?<=$s{r})$o{r}",l,"$o{l}(?=$s{l})");%h=split//,$u{$d};$c+=s!$p{$d}!$h{$&}||($v=$&,($o{$d}=~s/$v// && $s{$d}=~s/]/$m{$v}]/),$v)!es}}while($c);print/\*/?"False\n":"True\n"

Explicación: el código actualiza iterativamente la cadena de la cuadrícula a medida que los láseres pasan a través de ella. -representa un láser horizontal, |un láser vertical, láser +cruzado, Kun \espejo con un láser que rebota en la parte superior, kun /espejo con un láser que rebota en la parte inferior, Zun \espejo con un láser que rebota en la parte inferior y Wun /espejo con un láser que rebota la parte superior. %es un /espejo con láser en ambos lados, mientras que Xes un \espejo con láser en ambos lados. (Estos distinguen entre mayúsculas y minúsculas. Intenté elegir letras que parezcan algo apropiadas, por ejemplo, kyKson opciones algo obvias, pero desafortunadamente el efecto realmente no es tan útil. Realmente debería poner esta información en una tabla, pero estoy agotado en este momento).

El manejo de los portales de la misma manera (es decir, asignar a cada dígito un conjunto de caracteres adicionales en función de las posibles posiciones del láser de entrada / salida) requeriría 144 caracteres (incluido el 9 original), por lo tanto, cuando un láser golpea un portal de "entrada", Agrego el carácter del portal de "salida" al conjunto de caracteres que emiten un láser en la dirección correcta. (Esto requiere diferenciar entre los portales de entrada y salida; usé las letras qwertyuiopara esto).

Algo inexperto, con declaraciones impresas para que pueda ver las sustituciones que suceden (cada sustitución representa una "ronda" de progresión láser), y con la gbandera agregada al principal s///para que no tome tantas iteraciones:

# Throughout, d,u,r,l represents lasers going down, up, left, or right
# `sources` are the character classes representing laser "sources" (i.e. any
# character that can, on the next round, cause a laser to enter the space
# immediately adjacent to it in the proper direction)
%sources=(d,'[|+#$vk%ZX]',u,'[|+#$^W%KX]',r,'[-G+#>k%KX]',l,'[-G+#<W%ZX]');
# `open` characters will not block a laser
%open=(d,'[-.*G/k\\\\Z',u,'[-.*G/W\\\\K',r,'[|.*$\\\\/kK',l,'[|.*$\\\\/ZW');
# One of each portal is changed into the corresponding letter in `qwertyuio`.
# At the start, each portal is 'open' and none of them is a source.
for$d(d,u,r,l){$open{$d}.='123456789qwertyuio]'}
# A mapping of 'open' characters to the characters they become when a laser
# goes through them. (This is used like a hash of hashes; see the assignment
# of `%h` below.)
%update=(d,'.|-+*$G#/Wk%\KZX',
    u,'.|-+*$G#/kW%\ZKX',
    r,'.-|+*G$#/Wk%\ZKX',
    l,'.-|+*G$#/kW%\KZX');
@q=split//,"qwertyuio";
local$/;$_=<STDIN>;
for$i(1..9){
    $m{$i}=$q[$i-1];
    $m{$m{$i}}=$i;
    s/$i/$m{$i}/e}
print "After substituting portals:\n";
print;
print "\n";
# Find the number of characters in each line and create a string of `.`'s,
# which will be used to correlate characters above/below one another in the
# grid with each other.
/.*?\n/;
$l='.'x((length$&)-1);
do{
    $changes=0;
    for$d(d,u,r,l){
        # `patterns` is a mapping from each direction to the regex representing
        # an update that must occur (i.e. a place where a laser must progress).
        # Each pattern is either a lookahead or lookbehind plus the necessary
        # "open" character class.
        %patterns=(d,"(?<=$sources{d}$l)$open{d}",
            u,"$open{u}(?=$l$sources{u})",
            r,"(?<=$sources{r})$open{r}",
            l,"$open{l}(?=$sources{l})");
        %h=split//,$update{$d};
        # Match against the pattern for each direction. Note whether any
        # matches were found.
        $changes+=s!$patterns{$d}!
            # If the "open" character for a map is in the `update` map, return
            # the corresponding value. Otherwise, the "open" character is a
            # portal.
            $h{$&} || ($v=$&,
                        # For portals, remove the input portal from the
                        # proper "open" list and add the output portal to
                        # the proper "source" list.
                       ($open{$d}=~s/$v// && $sources{$d}=~s/]/$m{$v}]/),
                       $v)
                    # This whole substitution should allow `.` to match
                    # newlines (see the definition of `$l` above), and the
                    # replacement must be an expression rather than a string
                    # to facilitate the portal logic. The `g` allows multiple
                    # updates per "frame"; it is left out of the golfed code.
                    !egs
    }
    # Print the next "frame".
    print;
    print "\n";
# Continue updating until no "open" spaces are found.
}while($changes);
# Print whether `*` is still present in the input.
print/\*/?"False\n":"True\n"
Kyle Strand
fuente
Experimenté con este tipo de enfoque (usando matrices bool en lugar de expresiones regulares) en Python, pero no pude acercarme a este pequeño. ¡Creo que este es un enfoque realmente estimulante! Mis intentos fueron influenciados erróneamente por catpad.net/michael/apl con buen video youtube.com/watch?v=a9xAKttWgP4 y petercollingridge.co.uk/blog/python-game-of-life-in-one-line
Será el
1
@ ¡Gracias! Definitivamente me di cuenta de cuán similares eran mis esfuerzos para GoL en el momento en que resolví cuán factible sería usar un personaje diferente para cada combinación posible de láseres que entran y salen de un portal. Creo que podría afeitarme unos pocos personajes más, pero ... ¡claramente este no es el enfoque óptimo!
Kyle Strand
Además, si alguien sabe una mejor manera de manejar el triple escapado `` 's en las clases de personajes en las primeras líneas, eso sería precioso ...
Kyle Strand
6

Python 338 351

def t(b):
 L=len;w=L(b[0])+3;b=list("@"*w+"@@".join(b)+"@"*w);w-=1;I=b.index
 for i in range(L(b)):
  c=b[i];d={"^":-w,"<":-1,">":1,"v":w}.get(c)
  if d:
   while c!='@':
    i+=d;c=b[i]
    if c=='*':b[i]='.'
    elif c in '/\\':d={-w:-1,w:1,1:w,-1:-w}[d]*(-1 if c=='/' else 1)
    elif c>'0':i+=I(c)-i or I(c,i+1)-i
 return "*" not in b

Mi versión no minificada en realidad traza los caminos del láser en el tablero, lo cual es bonito:

>-+--\
..X..|
>-/--/
..X...

>----------\
1----------/
2----------1
3----------2
4----------3
5----------4
6----------5
7----------6
8----------7
9----------8
X----------9

>-@............*
>--@...........*
>---@..........*
>----@.........*
>-----@........*
>---X--@........
>-------@......*

/-------X+------\/3.....
@........|.....//\+\....
X........|....2\+1\/\...
\--------+----+///+++--<
.........X...//\\/+++--\
>--------+---+\+1+///-4|
4-------X/...\2\/3/\/..^

vvvvvvvvvvvvvvvvv
\\\\\\\\\\\\\\\\\
/////////////////
\\\\\\\\\\\\\\\\\
/////////////////
\\\\\\\\\\\\\\\\\
/////////////////
XXXXXXXXXXXXXXXX*

def debug(board,x,y):
    emit_dir = {
        "^":    ( 0, -1),
        "<":    (-1,  0),
        ">":    ( 1,  0),
        "v":    ( 0,  1),
    }
    class PortalException(Exception): pass
    xdir, ydir = emit_dir[board[y][x]]
    while True:
        # print "step (%d, %d) (%d, %d)" % (x, y, xdir, ydir)
        x += xdir
        y += ydir
        if y < 0 or y >= len(board) or x < 0 or x >= len(board[y]):
            return
        ch = board[y][x]
        if ch == '/':
            xdir, ydir = -ydir, -xdir
        elif ch == '\\':
            xdir, ydir = ydir, xdir
        elif ch in '@^><v':
            return
        elif ch == '*':
            board[y] = board[y][:x] + 'X' + board[y][x+1:]
        elif ch in '.-|':
            ch = ('-' if xdir else '|') if ch == '.' else '+'
            board[y] = board[y][:x] + ch + board[y][x+1:]
        elif ch in '123456789':
            try:
                for r in range(len(board)):
                    for c in range(len(board[r])):
                        if board[r][c] == ch and (r != y or c != x):
                            x, y = c, r
                            raise PortalException()
                raise Exception("could not find portal %s (%d,%d)" % (ch, x, y))
            except PortalException:
                pass
Será
fuente
5

C # - 515 414 400 bytes

Programa completo de C #, sin resultados agradables como los de Will. Funciona siguiendo el camino del láser para cada emisión individual y manteniendo una matriz de las células que hemos visitado, para que podamos verificar que hemos visitado todas las estrellas al final. Editar: separa una gran cantidad de bytes haciendo que todo sea 1D y usando un carácter en lugar de un int para almacenar el carácter actual

w0lf me recordó que tenía un bucle for infrautilizado justo en el medio de mi código, así que pensé que sería mejor hacer un último esfuerzo y ponerlo a trabajar, y ahora estoy en el número mínimo absoluto de rizado tirantes. No pretendo que me guste el colapso del segundo bucle for, el código ahora es terriblemente desordenado, pero ahorró algunos bytes. En el proceso reescribí el manejo del portal. También encontré un método más corto para realizar el "movimiento" con una operación condicional anidada en lugar de agregada.

Código de golf:

using C=System.Console;class P{static void Main(){var S=C.In.ReadToEnd().Replace("\r","");int W=S.IndexOf('\n')+1,l=S.Length,i=l,d,m,n;var M=new int[l];for(char c;i-->0;)for(d="^<v>".IndexOf(c=S[m=i]);c>14&d>-1;d=(m+=d==2?W:d>0?d-2:-W)>=0&m<l&&"@^<v>".IndexOf(c=S[m])<0?d:-1)for(d=c==47?3-d:c==92?d^1:d,M[n=m]=1;c%49<9&&(m=S.IndexOf(c,m+1))==n|m<0;);for(;l-->0;)W*=S[l]==42?M[l]:1;C.WriteLine(W>0);}}

Menos código de golf:

using C=System.Console;

class P
{
    static void Main()
    {
        var S=C.In.ReadToEnd().Replace("\r",""); // read the grid, remove pesky carriage returns
        int W=S.IndexOf('\n')+1,l=S.Length,i=l,d,m,n; // find "width"
        var M=new int[l]; // defaults to 0s

        for(char c;i-->0;) // for each cell

            for(d="^<v>".IndexOf(c=S[m=i]); // find initial direction, if any
                c>14&d>-1; // loop only if we have direction
                d=(m+=d==2?W:d>0?d-2:-W) // move (after iteration)
                >=0&m<l&&"@^<v>".IndexOf(c=S[m])<0?d:-1) // terminate if we hit something or go off edge

                for(d=c==47?3-d:c==92?d^1:d, // mirrors
                    M[n=m]=1; // we have seen this spot
                    c%49<9&&(m=S.IndexOf(c,m+1))==n|m<0;); // portals

        for(;l-->0;) // for each cell
            W*=S[l]==42?M[l]:1; // if *, then mul by whether seen

        C.WriteLine(W>0);
    }
}

El nuevo código de manejo del portal utiliza el hecho de que la función String.IndexOf devuelve felizmente -1 (es decir, char no encontrado) si le pide que comience a buscar 1 carácter más allá de la cadena (arroja una excepción si le pide que comience más allá). Esto fue nuevo para mí, pero fue muy conveniente en este caso.

VisualMelon
fuente
+1 ¡Golf increíble! Me acaba de ocurrir un truco: se puede tomar el m+=(d>0?d-2:0)+(d<3?d-1:0)*W;y empujarlo en el for, así: for(char c;i-->0;m+=(d>0?d-2:0)+(d<3?d-1:0)*W). De esta manera, ahorrará un carácter, porque perderá un punto y coma.
Cristian Lupascu
@ w0lf hizo un último esfuerzo y logró colapsar por completo los bucles for, gracias por el empujón;)
VisualMelon