Tiempo de laberinto hexagonal!

27

Es hora de otro desafío de laberinto, pero no como lo sabes.

Las reglas para este desafío son un poco diferentes a la mayoría de los desafíos de laberintos. Los tipos de mosaico se definen de la siguiente manera:

  • S: La ubicación en el laberinto donde comienzas
  • E: La ubicación a la que intenta llegar
  • 0: Muro que no puedes cruzar
  • +: Piso que puedes cruzar

Puede viajar en una de las seis direcciones: arriba a la izquierda, arriba a la derecha, izquierda, derecha, abajo a la izquierda o abajo a la derecha.

\ /
-S-
/ \

El laberinto no se envuelve. El objetivo es encontrar la cadena de ruta más corta para llegar Sa E.

Entrada:

La entrada es líneas separadas por espacios como los laberintos mostrados. Ningún espacio final seguirá una línea.

Salida:

Una cadena de R, Ly Fdonde

  • R te gira a la derecha (en sentido horario) 60 grados
  • L gira a la izquierda (en sentido antihorario) 60 grados
  • F te mueve un espacio en la dirección que estás apuntando

Empiezas a señalar left-up

La ruta más corta se cuenta por la longitud de la cadena producida, no por el número de posiciones visitadas. Su programa debe imprimir la ruta más corta como la solución.

Si el laberinto no se puede resolver, debe salir Invalid maze!.

( >>>es la salida)

     0 0 0 0
    0 + 0 + 0
   0 0 0 + + 0
  0 + 0 + 0 + 0
 0 0 + + 0 0 + 0
0 0 + 0 + 0 0 + 0
 E 0 + 0 0 + + 0 
  + + 0 + 0 + 0
   0 0 0 0 0 +
    + 0 + + +
     0 S 0 0

>>>RFRFFLFLFRFFLFFFLFLFFRFLFLFRFRFRF

  + 0 0 0 0 0 0
 0 0 0 0 0 + + 0
0 0 E 0 + 0 0 + 0
 0 0 0 0 0 0 0 +
  0 + 0 0 + + +
   0 0 + + 0 0
    S + 0 0 0

>>>Invalid maze!

0 E S

>>>LF


 E + 0
0 + + +
 0 0 S
  + +

>>>FFLF

  E
 0 +
0 + +
 0 +
  S

>>>RFFLFF

 0 E + 0 0
0 + 0 0 + +
 + 0 + + + 0
  + 0 + 0 + 0
   + + + 0 S

>>>FFLFLFFRFRFFRFF

 E 0 + + 0
0 + 0 + + 0
 + + + 0 + 0
  + 0 0 0 0 0
   + + + + 0
    + 0 S 0

>>>FLFFRFFRFLF

(Tenga en cuenta que algunos de los laberintos tienen otras soluciones que tienen la misma longitud pero que no se enumeran aquí)

J Atkin
fuente
27
Esperando una solución de Hexagony ...
bkul
3
Otorgaré una recompensa de 500 puntos a una solución de Hexagony.
lirtosiast el
@ lirtosiast2 años después, creo que la hexagonía podría ser una exageración para este problema;)
J Atkin
Esperemos unos años más.
user202729
¿Puede haber una nueva línea final?
usuario202729

Respuestas:

17

Python 2, 291 bytes

def f(M):
 Y=map(max,M).index("S");X=M[Y].find("S");V={()};Q=[(0,0,0,1,"")]
 while Q:
    try:x,y,u,v,p=s=Q.pop(0);c=(Y>=y<=X-2*x)*ord(M[Y-y][X-2*x-y])
    except:c=0
    if c==69:return p
    if{c%2*s[:4]}-V:V|={s[:4]};Q+=(x+u,y+v,u,v,p+"F"),(x,y,-v,u+v,p+"R"),(x,y,u+v,-u,p+"L")
 return"Invalid maze!"

Una función, ftomar el laberinto como una lista de filas y devolver una solución, si existe.

Explicación

Realiza una búsqueda de amplitud en el gráfico de pares de posición / dirección para encontrar la ruta más corta de Sa E.

Lo interesante es encontrar una forma compacta de representar posiciones y direcciones en una cuadrícula hexagonal, que admita un simple "paso" (es decir, moverse en una determinada dirección) y rotación. Es tentador usar números complejos aquí, para representar coordenadas en una cuadrícula hexagonal "real", pero esta no es una muy buena idea por varias razones, la más grave de las cuales es el hecho de que necesitamos enchufar un √3 en algún lugar para que funcione (sen 60 ° = √3 / 2), que, cuando se usan números de punto flotante, no se recomienda si necesitamos precisión absoluta (por ejemplo, para hacer un seguimiento de los estados que ya hemos visitado;) puedes intentar encender tu consola JavaScript y escribir Math.sqrt(3)*Math.sqrt(3) == 3y ver por ti mismo.

¡Pero podemos usar un pequeño truco! En lugar de usar números complejos, definamos números hexagonales en una veta similar, como un par de números reales a + bh , donde h juega un papel similar al imaginario i cuando se trata de números complejos. Al igual que con los números complejos, podemos asociar el par ( a , b ) con un punto en el plano, donde el eje real apunta hacia la derecha, el eje imaginario apunta 60 ° hacia arriba, y ambos cruzan el hexágono regular de la unidad cuando el real y las partes imaginarias son iguales a 1, respectivamente. Mapear este sistema de coordenadas a las celdas del laberinto es trivial.

Figura 1

A diferencia de i , la constante h está definida por la relación h 2 = h - 1 (resolver para h podría revelar algunas ideas). ¡Y eso es todo! Los números hexagonales se pueden sumar y multiplicar, usando la relación anterior, al igual que los números complejos: ( a + bh ) + ( c + dh ) = ( a + c ) + ( b + d ) h ,
y ( a + bh ) · ( c + dh ) = ( ac - bd) + ( ad + bc + bd ) h . Estas operaciones tienen la misma interpretación geométrica que sus contrapartes complejas: la suma es la suma de vectores, y la multiplicación es escalado y rotación. En particular, para rotar un número hexagonal 60 ° en sentido antihorario, lo multiplicamos por h :
( a + bh ) · h = - b + ( a + b ) h , y para rotar el mismo número 60 ° en sentido horario, dividimos por h :
( a + bh ) / h = ( a +bh ) · (1 - h ) = (a + b) - ah . Por ejemplo, podemos tomar la unidad del número hexagonal apuntando a la derecha, 1 = (1, 0), un círculo completo, en sentido antihorario, multiplicándolo por h seis veces:
(1, 0) · h = (0, 1 ); (0, 1) · h = (-1, 1); (-1, 1) · h = (-1, 0); (-1, 0) · h = (0, -1); (0, -1) · h = (1, -1);
(1, -1) · h = (1, 0).

El programa utiliza números hexagonales de esta manera para representar la posición actual en el laberinto y la dirección actual, avanzar en la dirección especificada y rotar la dirección hacia la izquierda y hacia la derecha.

Ana
fuente
31

Hexagonía , 2437 bytes.

El programa tan esperado está aquí:

(.=$>({{{({}}{\>6'%={}{?4=$/./\_><./,{}}{<$<\?{&P'_("'"#<".>........_..\></</(\.|/<>}{0/'$.}_.....><>)<.$)).><$./$\))'"<$_.)><.>%'2{/_.>(/)|_>}{{}./..>#/|}.'/$|\})'%.<>{=\$_.\<$))<>(|\4?<.{.%.|/</{=....$/<>/...'..._.>'"'_/<}....({{>%'))}/.><.$./{}{\>$\|$(<><$?..\\<.}_>=<._..\(/.//..\}\.)))))<...2/|$\){}/{..><>).../_$..$_>{0#{{((((/|#.}><..>.<_.\(//$>))<(/.\.\})'"#4?#\_=_-..=.>(<...(..>(/\")<((.\=}}}\>{}{?<,|{>/...(...>($>{)<.>{=P&/(>//(_.)\}=#=\4#|)__.>"'()'\.'..".(\&P'&</'&\$_></}{)<\<0|\<.}.\"\.(.(.(/(\..{.>}=P/|><.(...(..."/<.{"_{{=..)..>})<|><$}}/\}}&P<\(/._...>\$'/.>}/{}}{)..|/(\'.<(\''"")$/{{}})<..'...}}>3#./\$<}|.}|..$.><${{}/>.}}{{<>(""''/..>){<}\?=}{\._=/$/=_>)\{_\._..>)</{\=._.....>(($>}}<.>5#.\/}>)<>-/(.....{\<>}}{{/)\$>=}}}))<...=...(\?{{{?<\<._...}.><..\}}/..>'P&//(\......(..\})"'/./&P'&P{}}&P'<{}\{{{({{{(.\&P=<.><$"&1}(./'"?&'&"\.|>}{?&"?&'P&/|{/&P''</(\..>P&{/&/}{}&'&},/"&P'&?<.|\}{&?"&P'&P'<._.>"&}\(>))<\=}{}<.{/}&?"&"&/"&"?&}\.|>?&"?&{{}}?&//x'&{((<._\($|(}.\/}{/>=&'P&"&/".{3?<.|\"&P'&P}{}&P'<.>&{}}?&"&'P&\=}}<.}/2?".?''5?"/?1{(}\."..../{},<../&//&"&P'&P'&"&"</{}}{{/>"?1''?.'({/}}{}<..>?&"?&}}$>)|P/<.>"&'P&'P&"&"&{/........._/"\$#1}/._.........|,($<'"}'?/_$P#"$0'${)$})$)|........(>/\.#1?<$<|.....>?&}$>=?&"?&/1$..>I;n;v;a;l;i;d;P0;m;a\|\"(}}({=/..$_...\"&P=},}}&P'<.|><....................;...>1"(}}){=/_....>'P&'P&}}_?&/#.>}?4'%\/<...@;1P;e;z<._><>"))'?=<.$$=..\&P}{&</\"><_'|/'&=}<.>{{.<.........|>(/>3")}}){=/=/_.>}P&"?/"<).}_.>?4{=:<.|_...........\$\2$'>4")}}({/."\{&P'&?/><.?|>P...."/=(>(/./(}{{\..>(<>(<>?5'"((..'/...#,</,}{{\.......;.F>..\(...}....._.._..._..._........__..'$......\.<R..$.>))<$}{{&P'&?}<.\$$.\...................$\.<>L\.\(('_"\>}P&"?&{/__/=(.(<.>_)..<...>....\..._.<.....&?=\}=&?"&<.."'>.\>))<.|>))\.|$.>&"?&{{}=P&}?&=}/{\.>&{{P/{">)<|\{<(|\(_(<>\_\?"&P'&P}{{{&<=_.>\&\?"&?<|'{/(/>{{/_>.{/=/\\.>'P&"?&"?&"?/._(\)\\>?&"/_|.>/.$/|$..\\><..\&?}{{}&P'&<}.._>{<|\{}<._$>-")<.>_)<|{)$|..>}=P&"?&"?/...{"./>'P&/=_\{?(/>(<>\(|)__.\&?}}{}&P<}.$.\&P'&P'&<\})))&=<\)<'.'_,><.>"?&'P&'/.|>?&{{}?&"?/>&"?&"?&}}<.".(\\\&?={&P<{..\"&?"&P'&<.?....|.$'\$/\"/.,.>{}{}=/..>&'P&}{{}P/\{}&P{(&?"&?"<'.(\&?"&<}..\?"&?"&<.>P&={}}?&}}P&'P&/.'.>&"?/..>P&}}{{P/\}&P'&?={&?<$}=\"."\P'<{..\'&P'&<....>'P&{}P&"?&{{<\\..>&/$.>}{?&"?/|'$&.P&$P\$'&P={(/..\P\\.\{&?"&?\...\?{{}=<$&P'&P<.,./<?\...{}=P\"&<.>=P&""'?&'P&'/$.#1.>{?1#=$\&'P/\}&P'&?={(,}<._?_&\&?{=&{*=}4<.>P&"?&"?&'P&/1_$>}?&}}=?&){?/\{}&P'&?={&?#<$

Pruébalo en línea!

Versión "legible":

                             ( . = $ > ( { { { ( { } } { \ > 6 ' % = { } { ? 4 = $ / .
                            / \ _ > < . / , { } } { < $ < \ ? { & P ' _ ( " ' " # < " .
                           > . . . . . . . . _ . . \ > < / < / ( \ . | / < > } { 0 / ' $
                          . } _ . . . . . > < > ) < . $ ) ) . > < $ . / $ \ ) ) ' " < $ _
                         . ) > < . > % ' 2 { / _ . > ( / ) | _ > } { { } . / . . > # / | }
                        . ' / $ | \ } ) ' % . < > { = \ $ _ . \ < $ ) ) < > ( | \ 4 ? < . {
                       . % . | / < / { = . . . . $ / < > / . . . ' . . . _ . > ' " ' _ / < }
                      . . . . ( { { > % ' ) ) } / . > < . $ . / { } { \ > $ \ | $ ( < > < $ ?
                     . . \ \ < . } _ > = < . _ . . \ ( / . / / . . \ } \ . ) ) ) ) ) < . . . 2
                    / | $ \ ) { } / { . . > < > ) . . . / _ $ . . $ _ > { 0 # { { ( ( ( ( / | #
                   . } > < . . > . < _ . \ ( / / $ > ) ) < ( / . \ . \ } ) ' " # 4 ? # \ _ = _ -
                  . . = . > ( < . . . ( . . > ( / \ " ) < ( ( . \ = } } } \ > { } { ? < , | { > /
                 . . . ( . . . > ( $ > { ) < . > { = P & / ( > / / ( _ . ) \ } = # = \ 4 # | ) _ _
                . > " ' ( ) ' \ . ' . . " . ( \ & P ' & < / ' & \ $ _ > < / } { ) < \ < 0 | \ < . }
               . \ " \ . ( . ( . ( / ( \ . . { . > } = P / | > < . ( . . . ( . . . " / < . { " _ { {
              = . . ) . . > } ) < | > < $ } } / \ } } & P < \ ( / . _ . . . > \ $ ' / . > } / { } } {
             ) . . | / ( \ ' . < ( \ ' ' " " ) $ / { { } } ) < . . ' . . . } } > 3 # . / \ $ < } | . }
            | . . $ . > < $ { { } / > . } } { { < > ( " " ' ' / . . > ) { < } \ ? = } { \ . _ = / $ / =
           _ > ) \ { _ \ . _ . . > ) < / { \ = . _ . . . . . > ( ( $ > } } < . > 5 # . \ / } > ) < > - /
          ( . . . . . { \ < > } } { { / ) \ $ > = } } } ) ) < . . . = . . . ( \ ? { { { ? < \ < . _ . . .
         } . > < . . \ } } / . . > ' P & / / ( \ . . . . . . ( . . \ } ) " ' / . / & P ' & P { } } & P ' <
        { } \ { { { ( { { { ( . \ & P = < . > < $ " & 1 } ( . / ' " ? & ' & " \ . | > } { ? & " ? & ' P & /
       | { / & P ' ' < / ( \ . . > P & { / & / } { } & ' & } , / " & P ' & ? < . | \ } { & ? " & P ' & P ' <
      . _ . > " & } \ ( > ) ) < \ = } { } < . { / } & ? " & " & / " & " ? & } \ . | > ? & " ? & { { } } ? & /
     / x ' & { ( ( < . _ \ ( $ | ( } . \ / } { / > = & ' P & " & / " . { 3 ? < . | \ " & P ' & P } { } & P ' <
    . > & { } } ? & " & ' P & \ = } } < . } / 2 ? " . ? ' ' 5 ? " / ? 1 { ( } \ . " . . . . / { } , < . . / & /
   / & " & P ' & P ' & " & " < / { } } { { / > " ? 1 ' ' ? . ' ( { / } } { } < . . > ? & " ? & } } $ > ) | P / <
  . > " & ' P & ' P & " & " & { / . . . . . . . . . _ / " \ $ # 1 } / . _ . . . . . . . . . | , ( $ < ' " } ' ? /
 _ $ P # " $ 0 ' $ { ) $ } ) $ ) | . . . . . . . . ( > / \ . # 1 ? < $ < | . . . . . > ? & } $ > = ? & " ? & / 1 $
  . . > I ; n ; v ; a ; l ; i ; d ; P 0 ; m ; a \ | \ " ( } } ( { = / . . $ _ . . . \ " & P = } , } } & P ' < . |
   > < . . . . . . . . . . . . . . . . . . . . ; . . . > 1 " ( } } ) { = / _ . . . . > ' P & ' P & } } _ ? & / #
    . > } ? 4 ' % \ / < . . . @ ; 1 P ; e ; z < . _ > < > " ) ) ' ? = < . $ $ = . . \ & P } { & < / \ " > < _ '
     | / ' & = } < . > { { . < . . . . . . . . . | > ( / > 3 " ) } } ) { = / = / _ . > } P & " ? / " < ) . } _
      . > ? 4 { = : < . | _ . . . . . . . . . . . \ $ \ 2 $ ' > 4 " ) } } ( { / . " \ { & P ' & ? / > < . ? |
       > P . . . . " / = ( > ( / . / ( } { { \ . . > ( < > ( < > ? 5 ' " ( ( . . ' / . . . # , < / , } { { \
        . . . . . . . ; . F > . . \ ( . . . } . . . . . _ . . _ . . . _ . . . _ . . . . . . . . _ _ . . ' $
         . . . . . . \ . < R . . $ . > ) ) < $ } { { & P ' & ? } < . \ $ $ . \ . . . . . . . . . . . . . .
          . . . . . $ \ . < > L \ . \ ( ( ' _ " \ > } P & " ? & { / _ _ / = ( . ( < . > _ ) . . < . . . >
           . . . . \ . . . _ . < . . . . . & ? = \ } = & ? " & < . . " ' > . \ > ) ) < . | > ) ) \ . | $
            . > & " ? & { { } = P & } ? & = } / { \ . > & { { P / { " > ) < | \ { < ( | \ ( _ ( < > \ _
             \ ? " & P ' & P } { { { & < = _ . > \ & \ ? " & ? < | ' { / ( / > { { / _ > . { / = / \ \
              . > ' P & " ? & " ? & " ? / . _ ( \ ) \ \ > ? & " / _ | . > / . $ / | $ . . \ \ > < . .
               \ & ? } { { } & P ' & < } . . _ > { < | \ { } < . _ $ > - " ) < . > _ ) < | { ) $ | .
                . > } = P & " ? & " ? / . . . { " . / > ' P & / = _ \ { ? ( / > ( < > \ ( | ) _ _ .
                 \ & ? } } { } & P < } . $ . \ & P ' & P ' & < \ } ) ) ) & = < \ ) < ' . ' _ , > <
                  . > " ? & ' P & ' / . | > ? & { { } ? & " ? / > & " ? & " ? & } } < . " . ( \ \
                   \ & ? = { & P < { . . \ " & ? " & P ' & < . ? . . . . | . $ ' \ $ / \ " / . ,
                    . > { } { } = / . . > & ' P & } { { } P / \ { } & P { ( & ? " & ? " < ' . (
                     \ & ? " & < } . . \ ? " & ? " & < . > P & = { } } ? & } } P & ' P & / . '
                      . > & " ? / . . > P & } } { { P / \ } & P ' & ? = { & ? < $ } = \ " . "
                       \ P ' < { . . \ ' & P ' & < . . . . > ' P & { } P & " ? & { { < \ \ .
                        . > & / $ . > } { ? & " ? / | ' $ & . P & $ P \ $ ' & P = { ( / . .
                         \ P \ \ . \ { & ? " & ? \ . . . \ ? { { } = < $ & P ' & P < . , .
                          / < ? \ . . . { } = P \ " & < . > = P & " " ' ? & ' P & ' / $ .
                           # 1 . > { ? 1 # = $ \ & ' P / \ } & P ' & ? = { ( , } < . _ ?
                            _ & \ & ? { = & { * = } 4 < . > P & " ? & " ? & ' P & / 1 _
                             $ > } ? & } } = ? & ) { ? / \ { } & P ' & ? = { & ? # < $

Probado en IDE esotérico: TIO podría agotar el tiempo en algunos de los casos de prueba más grandes, pero todos verificados. Muchas gracias a Timwi, esto no hubiera sido posible sin el IDE.

Hay un poco de espacio vacío, por lo que podría haber sido capaz de ajustar esto en un hexágono de longitud lateral 28 (en lugar de la longitud lateral 29), pero esa será una tarea enorme, por lo que probablemente no voy a intentarlo.

Explicación Básica

Haga clic en las imágenes para obtener una versión más grande y más detallada.

Las funciones

Las funciones
Nota: Las divisiones son generalmente correctas, pero en ocasiones pueden ser una suposición aproximada.

Este código es bastante "funcional", tanto como lo permite Hexagony. Hay ocho funciones principales en este código etiquetadas en el diagrama anterior, nombradas por los números con los que se llaman (por lo que sus números de puntero de instrucción son ese número mod 6). En orden (aproximado) de llamada, son (los nombres entre comillas son ubicaciones en la memoria que se explicarán más adelante):

  • S: La función de partida - lee la entrada y establece la "matriz de referencia", a continuación, inicia la "pila camino" con los tres caminos F, Ry Llistos para su procesamiento principal. El puntero de instrucción 0 se mueve a la función 0 mientras que la ejecución se mueve a la función 1.
  • 1 (-11): la función principal: utiliza 2 para obtener una ruta, 3 para verificar su validez y, si es válida, va a la función -110 / -10 dos veces y luego 4 tres veces para copiar las nuevas rutas en la "ruta" stack ", terminando volviendo a sí mismo. Puede llamar a la función 5 si la ruta está en la ubicación final.
  • 2: Obtiene la siguiente ruta de la "pila de rutas" lista para procesar, llama a la función -1 si no quedan rutas en la pila. Vuelve a la función 1.
  • 3: Toma un par de valores, así como el número de movimiento y verifica la "matriz de referencia" para ver si la ruta actual ha finalizado en una ubicación válida. Una ubicación válida es el inicio dentro de los primeros 3 movimientos, o cualquiera +dentro de los 2 movimientos desde que se alcanza por primera vez. Vuelve a la función 1.
  • -10 / -110: copia la ruta actual. Vuelve a la función 1.
  • 0: ayuda a la función 1 para gestionar la dirección del movimiento con F. Vuelve a la función 1.
  • 4: Toma una copia de la ruta actual e interrelacionada con la función 1 la cambia a la misma ruta con cualquiera de los dos F, Ro Lanexa. Vuelve a la función 1.
  • 5: Toma la ruta e imprime la ruta correcta (por ejemplo FFLF), luego termina el programa.
  • -1: Imprime Invalid maze!y termina.
  • (Flechas dobles): debido a la falta de espacio, la función 1 / -11 tuvo que irse al espacio arriba de la función -1.

Memoria

Diseño de la memoria
Nota: Gracias al IDE Esotérico nuevamente por el diagrama.

La memoria consta de tres partes principales:

  • Matriz de referencia: la cuadrícula se almacena separadas por columnas 2, con un valor en cada paso:
    • 0 representa a , 0o un lugar válido al que se accedió hace más movimientos de los que se necesitarían para salir del lugar en cualquier dirección.
    • 1 representa un +que aún no se ha alcanzado.
    • (número más alto) representa el número de movimiento donde habrá suficientes movimientos para salir del lugar en cualquier dirección.
    • 10 también representa una nueva línea: estos nunca se alcanzan suponiendo que siguen inmediatamente al último carácter que no es un espacio en blanco.
  • Riel: Consiste en -1s con un solo -2a la izquierda, permite que el puntero de memoria regrese rápidamente al área de procesamiento central.
  • Pila de rutas: almacena cada una de las rutas no probadas en orden por ID de ruta (que está directamente relacionada con el número de movimiento, por lo que primero se prueban las rutas más cortas). La ruta se almacena de la siguiente manera:
    Diseño del camino
    • Rot: la rotación al final de la ruta actual: 0 para arriba a la izquierda y aumentando en sentido horario a 5
    • Mover: el número de movimiento actual (instrucciones - 1)
    • Path: la ruta actual, almacenado en cuaternario con F, R, Lcomo 1, 2, 3respectivamente
    • x / y: coordenadas al final de la ruta actual: x + 1 -1s hacia la derecha y luego los valores hacia arriba (aunque y = 0 se procesa como 1 de todos modos con el fin de separar el riel de los datos de referencia)

Otras ubicaciones importantes de memoria:

  1. La x / y de la Ese almacena aquí.
  2. Este espacio se utiliza para hacer rutas de transición dentro y fuera de la memoria.
  3. Esta ubicación es el centro de donde se almacena cada ruta durante el procesamiento.
boboquack
fuente
El siguiente paso es ejecutar su programa a través de su programa para encontrar la ruta de laberinto más corta.
Veskah
Sé que alguien lo publicará. Finalmente ... / También tengo un plan diferente, que probablemente debería tomar menos código que el tuyo. Nunca tenga tiempo de implementarlo.
user202729
@ user202729 sería interesante saberlo. Este método probablemente se pueda jugar al menos 2 tamaños menos, diría, pero es casi seguro que hay algo mejor por ahí.
boboquack
1
Solo estoy esperando a @lirtosiast.
J Atkin
1
Perdón por el retraso.
lirtosiast
6

Python 3, 466 bytes

Probablemente habría terminado más pequeño si usara la búsqueda de profundidad primero o algo así. Esta monstruosidad usa Dijkstra y es bastante rápida, pero muy larga.

El código define una función Sque toma una cadena multilínea con el laberinto y devuelve el resultado.

def F(M,L,c):y=M[:M.index(c)].count("\n");return L[y].index(c),y
def S(M):
 L=M.split("\n");Q=[("",)+F(M,L,"S")+(0,)];D={};R=range;H=len;U=R(2**30)
 while Q:
  C,*Q=sorted(Q,key=H);w,x,y,d=C
  for e in R(H(L)>y>-1<x<H(L[y])>0<H(D.get(C[1:],U))>H(w)and(L[y][x]in"+SE")*6):D[C[1:]]=w;E=(d+e)%6;Q+=[(w+",R,RR,RRR,LL,L".split(",")[e]+"F",x+[-1,1,2,1,-1,-2][E],y+[-1,-1,0,1,1,0][E],E)]
 J=min([D.get(F(M,L,"E")+(d,),U)for d in R(6)],key=H);return[J,"Invalid maze!"][J==U]

Aquí hay una prueba del código.

Sin golf

def find_char(maze, lines, char):
    y = maze[:maze.index(char)].count("\n")
    return lines[y].index(char), y
def solve(maze):
    lines = maze.split("\n")
    x, y = find_char(maze, lines, "S")
    queue = [("", x, y, 0)]
    solutions = {}
    very_long = range(2**30)
    x_for_direction = [-1,1,2,1,-1,-2]
    y_for_direction = [-1,-1,0,1,1,0]
    rotations = ["","R","RR","RRR","LL","L"]
    while len(queue) > 0:
        queue = sorted(queue, key=len)
        current, *queue = queue
        route, x, y, direction = current
        if 0 <= y < len(lines) and 0 <= x < len(lines[y]) and lines[y][x] in "+SE" and len(solutions.get(current[1:], very_long)) > len(route):
            solutions[current[1:]] = route
            for change in range(6):
                changed = (direction + change) % 6
                queue += [(route + rotations[change] + "F", x + x_for_direction[changed], y + y_for_direction[changed], changed)]
    end_x, end_y = find_char(maze, lines, "E")
    solution = min([solutions.get((end_x, end_y, direction), very_long) for direction in range(6)], key=len)
    return "Invalid maze!" if solution == very_long else solution
PurkkaKoodari
fuente
Wow muy agradable. ¿Cuánto tiempo te llevó escribir esto?
J Atkin
1
@JAtkin Bueno, el archivo se creó hace 1,5 horas, aunque no estoy seguro de cuánto tiempo pasé trabajando en el código. Además, son las 3 de la mañana aquí, por lo que mi productividad obviamente está en su máximo.
PurkkaKoodari
Bien, pasé más de 2 horas, y la mayor parte de la mía ya estaba escrita para un laberinto estándar.
J Atkin
¿Tienes una versión sin golf?
J Atkin
1
@JAtkin Es necesario, ya que es posible que deba dar la vuelta al comienzo. Sin la posición inicial con la que funcionaría L,,R.
PurkkaKoodari
3

Groovy, 624 bytes. ¡Delantero!

Time time hace que la pelota ruede con una grande. Toma una cadena de varias líneas como arg paraQ

Q={a->d=[0]*4
a.eachWithIndex{x,y->f=x.indexOf('S');e=x.indexOf('E');
if(f!=-1){d[0]=f;d[1]=y}
if(e!=-1){d[2]=e;d[3]=y}}
g=[]
s={x,y,h,i,j->if(h.contains([x, y])|y>=a.size()||x>=a[y].size()|x<0|y<0)return;k = a[y][x]
def l=h+[[x, y]]
def m=j
def n=1
if(h){
o=h[-1]
p=[x,y]
q=[p[0]-o[0],p[1]-o[1]]
n=[[-2,0]:0,[-1,-1]:1,[1,-1]:2,[2,0]:3,[1,1]:4,[-1,1]:5][q]
r=n-i
m=j+((r==-5|r==5)?' LR'[(int)r/5]:['','R','RR','LL','L'][r])+'F'}
if(k=='E')g+=m
if(k=='+'|k=='S'){s(x-2,y,l,n,m)
s(x+2,y,l,n,m)
s(x+1,y+1,l,n,m)
s(x+1,y-1,l,n,m)
s(x-1,y+1,l,n,m)
s(x-1,y-1,l,n,m)}}
s(d[0],d[1],[],1,'')
print(g.min{it.size()}?:"Invalid maze!")}

Versión sin golf:

def map =
        """
  + 0 0 0 0 0 0
 0 0 0 0 0 + + 0
0 0 E 0 + 0 0 + 0
 0 0 0 0 0 0 0 +
  0 + 0 0 + + +
   0 0 + + 0 0
    S + 0 0 0""".split('\n').findAll()
//map =
//        """
// 0 + +
//E + 0 S 0
// 0 0 0 +
//  + + +""".split('\n').findAll()

//map = [""]// TODO remove this, this is type checking only
//map.remove(0)
//reader = System.in.newReader()
//line = reader.readLine()
//while (line != '') {
//    map << line
//    line = reader.readLine()
//}

startAndEnd = [0, 0, 0, 0]
map.eachWithIndex { it, idx ->
    s = it.indexOf('S'); e = it.indexOf('E');
    if (s != -1) {
        startAndEnd[0] = s; startAndEnd[1] = idx
    }
    if (e != -1) {
        startAndEnd[2] = e; startAndEnd[3] = idx
    }
}

def validPaths = []
testMove = { x, y, visited ->// visited is an array of x y pairs that we have already visited in this tree
    if (visited.contains([x, y]) || y >= map.size() || x >= map[y].size() || x < 0 || y < 0)
        return;


    def valueAtPos = map[y][x]
    def newPath = visited + [[x, y]]

    if (valueAtPos == 'E') validPaths += [newPath]
    if (valueAtPos == '+' || valueAtPos == 'S') {
        println "$x, $y passed $valueAtPos"
        testMove(x - 2, y, newPath)
        testMove(x + 2, y, newPath)

        testMove(x + 1, y + 1, newPath)
        testMove(x + 1, y - 1, newPath)

        testMove(x - 1, y + 1, newPath)
        testMove(x - 1, y - 1, newPath)
    }
}

//if (!validPath) invalid()
testMove(startAndEnd[0], startAndEnd[1], [])
println validPaths.join('\n')

//println validPath

def smallest = validPaths.collect {
    def path = ''
    def orintation = 1
    it.inject { old, goal ->
        def chr = map[goal[1]][goal[0]]
        def sub = [goal[0] - old[0], goal[1] - old[1]]
        def newOrin = [[-2, 0]: 0, [-1, -1]: 1, [1, -1]: 2, [2, 0]: 3, [1, 1]:4, [-1, 1]:5][sub]
        def diff = newOrin - orintation// 5L -5R
        def addedPath= ((diff==-5||diff==5)?' LR'[(int)diff/5]:['', 'R', 'RR', 'LL', 'L'][diff]) + 'F'//(diff == 0) ? '' : (diff > 0 ? 'R'*diff : 'L'*(-diff)) + 'F'
//        println "old:$old, goal:$goal chr $chr, orintation $orintation, sub:$sub newOrin $newOrin newPath $addedPath diff $diff"
        path += addedPath
        orintation = newOrin
        goal
    }
    path
}.min{it.size()}
//println "paths:\n${smallest.join('\n')}"
if (smallest)
    println "path $smallest"
else
    println "Invalid maze!"
J Atkin
fuente
3

C #, 600 574 bytes

Programa completo, acepta entrada de STDIN, salida a STDOUT.

Editar: hubo un error en el manejo de la envoltura (no se rompió en ninguno de los casos de prueba dados) que habría agregado 1 byte, por lo que hice un poco más de golf para compensar.

using Q=System.Console;struct P{int p,d;static void Main(){string D="",L;int w=0,W=0,o,n=1;for(;(L=Q.ReadLine())!=null;D+=L)w=(o=(L+="X").Length+1)>w?o:w;for(;W<D.Length;)D=D.Insert(W+1,"".PadLeft(D[W++]>87?w-W%w:0));P[]K=new P[W*6];var T=new string[W*6];P c=K[o=0]=new P{p=D.IndexOf('S')};for(System.Action A=()=>{if(c.p>=0&c.p<W&System.Array.IndexOf(K,c)<0&&D[c.p]%8>0){T[n]=T[o]+L;K[n]=c;n=D[c.p]==69?-n:n+1;}};o<n;o++){c=K[o];L="R";c.d=++c.d%6;A();L="L";c.d=(c.d+4)%6;A();L="F";c=K[o];c.p+=new[]{~w,1-w,2,1+w,w-1,-2}[c.d%6];A();}Q.WriteLine(n>0?"Invalid maze!":T[-n]);}}

Comienza leyendo en el mapa, agregando (a cada línea para que sepa dónde termina, y puede retroceder y agregar una carga de espacios para hacer que el mapa sea rectangular y con una fila de espacios a lo largo del lado derecho (esto ahorra haciendo cheques de envoltura como se explicará a continuación) Calcula el ancho del rectángulo en algún punto de esto y determina la longitud total del Mapa.

A continuación, inicializa todo para una búsqueda de Breadth-First. Se crean dos matrices grandes, una para almacenar todos los estados que necesitamos explorar en nuestra búsqueda, la otra para registrar la ruta que tomamos para llegar a cada estado. El estado inicial se agrega a la matriz debida, con los punteros de cabeza y cola preestablecidos en algún punto anterior. Todo está indexado en 1.

Luego iteramos hasta que la cola se estrella contra la cabeza, o al menos parece haberse estrellado contra la cabeza. Para cada estado que hemos visitado, intentamos agregar un nuevo estado en la misma posición en la que giramos hacia la izquierda o la derecha, y luego uno en el que hemos avanzado. Las direcciones están indexadas, con la dirección inicial (predeterminada 0) correspondiente a "arriba a la izquierda".

Cuando tratamos de poner en cola un estado, se verifica el límite, pero no se verifica el ajuste, debido a las columnas de espacios en el lado derecho, que se recoge con el "¿se nos permite estar aquí?" marca (no puedes estar en espacios). Si el estado está en cola, verificamos si está en la Ecelda, y si lo está, configuramos el encabezado de la cola para que sea menos, lo que hace que salga el bucle principal y le dice a la última línea del programa que se imprima fuera de la ruta correspondiente, en lugar del mensaje de falla (que muestra si nos quedamos sin estados para expandirnos (la cola se estrella en la cabeza)).

using Q=System.Console;

// mod 8 table (the block of zeros is what we are after - it's everywhere we /can't/ go)
//   0 (space)
// O 0
// X 0
// S 3
// + 3
// E 5

struct P
{
    int p,d;
    static void Main()
    {
        // it's probably a bad thing that I have my own standards for naming this stupid read sequence by now
        string D="", // map
        L; // line/path char

        int w=0, // width
        W=0, // full length
        o, // next state to expand
        n=1; // next state to fill

        for(;(L=Q.ReadLine())!=null;D+=L) // read in map
            w=(o=(L+="X").Length+1)>w?o:w; // assertain max length (and mark end, and remove any need for wrap checking)

        // now we need to add those trailing spaces...
        for(;W<D.Length;)
            D=D.Insert(W+1,"".PadLeft(D[W++]>87?w-W%w:0)); // inject a load of spaces if we hit an X

        P[]K=new P[W*6]; // create space for due states (can't be more states than 6*number of cells)
        var T=new string[W*6]; // create space for routes (never done it this way before, kind of exciting :D)
        P c=K[o=0]=new P{p=D.IndexOf('S')}; // set first state (assignment to c is just to make the lambda shut up about unassigned variables)

        // run bfs
        for(

            System.Action A=()=> // this adds c to the list of states to be expanded, if a whole load of checks pass
            {
                if(//n>0& // we havn't already finished - we don't need this, because we can't win on the first turn, so can't win unless we go forward, which we check last
                   c.p>=0&c.p<W& // c is within bounds
                   System.Array.IndexOf(K,c)<0&& // we havn't seen c yet (the && is to prevent the following lookup IOBing)
                   D[c.p]%8>0) // and we can move here (see table at top of code)
                {
                    T[n]=T[o]+L; // store route
                    K[n]=c; // store state
                    n=D[c.p]==69?-n:n+1; // check if we are at the end, if so, set n to be negative of itself so we know, and can look up the route (otherwise, increment n)
                }
            }

            ;o<n;o++) // o<n also catches n<0
        {
            c=K[o]; // take current
            L="R"; // say we are going right
            c.d=++c.d%6; // turn right
            A(); // go!

            L="L"; // say we are going left
            c.d=(c.d+4)%6; // turn left
            A(); // go!

            L="F"; // say we - you get the picture
            c=K[o];
            c.p+=new[]{~w,1-w,2,1+w,w-1,-2}[c.d%6]; // look up direction of travel (~w = -w-1)
            A();
        }

        // check if we visited the end
        Q.WriteLine(n>0?"Invalid maze!":T[-n]); // if n<0, then we found the end, so spit out the corresponding route, otherwise, the maze is invlida
    }
}

Como la mayoría de mis búsquedas de gráficos en este sitio, estoy haciendo un buen uso de las estructuras de C #, que por defecto se comparan por valor literal.

VisualMelon
fuente
2

Python 2, 703 bytes

No es tan bueno como las otras dos versiones, pero al menos funciona jaja. Establecer Men el laberinto.

Como no tengo experiencia en la resolución de laberintos, solo se aplica un enfoque de fuerza bruta, donde encontrará todas las soluciones que no implique cruzar de nuevo sobre sí mismo. Calcula los giros a partir de los más cortos y luego elige el resultado más corto.

z=zip;d=z((-1,1,-2,2,-1,1),(-1,-1,0,0,1,1));E=enumerate;D={};t=tuple;o=list;b=o.index
for y,i in E(M.split('\n')):
 for x,j in E(o(i)):
  c=(x,y);D[c]=j
  if j=='S':s=c
  if j=='E':e=c
def P(s,e,D,p):
 p=o(p);p.append(s);D=D.copy();D[s]=''
 for i in d:
  c=t(x+y for x,y in z(s,i))
  if c not in p and c in D:
   if D[c]=='E':L.append(p+[c])
   if D[c]=='+':P(c,e,D,p)
def R(p):
 a=[0,1,3,5,4,2];h=d[0];x=p[0];s=''
 for c in p[1:]:
  r=t(x-y for x,y in z(c,x));n=0
  while h!=r:n+=1;h=d[a[(b(a,b(d,h))+1)%6]]
  s+=['L'*(6-n),'R'*n][n<3]+'F';x=t(x+y for x,y in z(x,h))
 return s
L=[];P(s,e,D,[])
try:l=len(min(L))
except ValueError:print"Invalid maze!"
else:print min([R(i)for i in L if len(i)==l],key=len)

Versión desordenada y sin golf:

maze = """
     0 0 0 0
    0 + 0 + 0
   0 0 0 + + 0
  0 + 0 + 0 + 0
 0 0 + + 0 0 + 0
0 0 + 0 + 0 0 + 0
 E 0 + 0 0 + + 0 
  + + 0 + 0 + 0
   0 0 0 0 0 +
    + 0 + + +
     0 S 0 0
     """
directions = [(-1, -1), (1, -1),
              (-2, 0), (2, 0),
              (-1, 1), (1, 1)]


maze_dict = {}
maze_lines = maze.split('\n')
for y, row in enumerate(maze_lines):
    if row:
        for x, item in enumerate(list(row)):
            coordinates = (x, y)
            maze_dict[coordinates] = item
            if item == 'S':
                start = coordinates
            elif item == 'E':
                end = coordinates

list_of_paths = []


def find_path(start, end, maze_dict, current_path=None):
    if current_path is None:
        current_path = []
    current_path = list(current_path)
    current_path.append(start)
    current_dict = maze_dict.copy()
    current_dict[start] = '0'

    for direction in directions:
        new_coordinate = (start[0] + direction[0], start[1] + direction[1])

        if new_coordinate in current_path:
            pass

        elif new_coordinate in current_dict:
            if current_dict[new_coordinate] == 'E':
                list_of_paths.append(current_path + [new_coordinate])
                break
            elif current_dict[new_coordinate] == '+':
                find_path(new_coordinate, end, current_dict, current_path)


find_path(start, end, maze_dict)


def find_route(path):

    heading_R = [0, 1, 3, 5, 4, 2]
    heading = (-1, -1)
    current_pos = path[0]
    current_heading = directions.index(heading)
    output_string = []
    for coordinate in path[1:]:
        required_heading = (coordinate[0] - current_pos[0], coordinate[1] - current_pos[1])

        count_R = 0
        while heading != required_heading:
            count_R += 1
            heading_index = directions.index(heading)
            heading_order = (heading_R.index(heading_index) + 1) % len(heading_R)
            heading = directions[heading_R[heading_order]]

        if count_R:
            if count_R > 3:
                output_string += ['L'] * (6 - count_R)
            else:
                output_string += ['R'] * count_R

        output_string.append('F')
        current_pos = (current_pos[0] + heading[0], current_pos[1] + heading[1])
    return ''.join(output_string)


routes = []
try:
    min_len = len(min(list_of_paths))
except ValueError:
    print "Invalid maze!"
else:
    for i in list_of_paths:
        if len(i) == min_len:
            routes.append(find_route(i))

    print 'Shortest route to end: {}'.format(min(routes, key=len))
Peter
fuente
Puede reemplazar if heading != required_heading: while heading != required_heading: con solowhile heading != required_heading:
J Atkin
Sí, gracias, jaja, me di cuenta de algunas cosas, incluyendo que cuando hice la versión de golf, simplemente no actualicé el código original, lo haré ahora, ya que me las arreglé para eliminar algunos personajes más
Peter
¡Agradable! (llenando los 15 minutos de char)
J Atkin
<Esta es una etiqueta HTML no reconocida, por lo que SE no es similar.>
CalculatorFeline