Tempo de labirinto hexagonal!

27

Hora de outro desafio de labirinto, mas não como você o conhece.

As regras para esse desafio são um pouco diferentes da maioria dos desafios do labirinto. Os tipos de bloco são definidos da seguinte maneira:

  • S: A localização no labirinto em que você começa
  • E: O local que você está tentando acessar
  • 0: Muro que você não pode atravessar
  • +: Piso que você pode atravessar

Você pode viajar em uma das seis direções: cima-esquerda, cima-direita, esquerda, direita, baixo-esquerda ou baixo-direita.

\ /
-S-
/ \

O labirinto não quebra. O objetivo é encontrar a seqüência de caminho mais curto para chegar a partir Sde E.

Entrada:

A entrada é linhas separadas por espaço, como os labirintos mostrados. Nenhum espaço à direita seguirá uma linha.

Saída:

Uma série de R, Le Fonde

  • R gira você para a direita (sentido horário) 60 graus
  • L gira você para a esquerda (sentido anti-horário) 60 graus
  • F move um espaço na direção que você está apontando

Você começa a apontar left-up

O caminho mais curto é contado pelo comprimento da sequência produzida, não pelo número de posições visitadas. Seu programa deve imprimir o caminho mais curto como a solução.

Se o labirinto é insolúvel, você deve produzir Invalid maze!.

( >>>é a saída)

     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

(Observe que alguns dos labirintos têm outras soluções que têm o mesmo comprimento, mas não estão listadas aqui)

J Atkin
fonte
27
Esperando por uma solução Hexagony ...
bkul
3
Atribuirei uma recompensa de 500 pontos a uma solução Hexagony.
precisa saber é o seguinte
@ anos lirtosiast2 depois, acho hexagony pode ser um estiramento para este problema;)
J Atkin
Vamos esperar mais alguns anos.
User202729
Pode haver uma nova linha à direita?
user202729

Respostas:

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

Uma função, ftomando o labirinto como uma lista de linhas e retornando uma solução, se houver.

Explicação

Executa uma pesquisa de amplitude no gráfico dos pares de posição / direção para encontrar o caminho mais curto de SatéE .

O interessante é encontrar uma maneira compacta de representar posições e direções em uma grade hexagonal, que admita "passos" simples (isto é, movendo-se em uma determinada direção) e rotação. É tentador usar números complexos aqui, para representar coordenadas em uma grade hexagonal "real", mas essa não é uma idéia muito boa por vários motivos, o mais sério dos quais é o fato de que precisamos conectar um √3 um lugar para fazê-lo funcionar (sen 60 ° = √3 / 2), que, ao usar números de ponto flutuante, não é possível se precisarmos de precisão absoluta (por exemplo, para acompanhar os estados que já visitamos;) você pode tentar inicializar seu console JavaScript, digitar Math.sqrt(3)*Math.sqrt(3) == 3e ver por si mesmo.

Mas, podemos usar um pequeno truque! Em vez de usar números complexos, vamos definir números hexagonais de maneira similar, como um par de números reais a + bh , em que h desempenha um papel semelhante ao imaginário i ao lidar com números complexos. Assim como em números complexos, podemos associar o par ( a , b ) a um ponto no plano, onde o eixo real aponta para a direita, o eixo imaginário aponta 60 ° para cima e ambos cruzam o hexágono regular da unidade quando o real e as partes imaginárias são iguais a 1, respectivamente. Mapear esse sistema de coordenadas para as células do labirinto é trivial.

figura 1

Diferentemente de i , a constante h é definida pela relação h 2 = h - 1 (a solução de h pode revelar algumas idéias.) E é isso! Número hexagonal pode ser adicionado e multiplicado, usando a relação acima, bem como os números complexos: ( a + BH ) + ( C + DH ) = ( a + c ) + ( b + d ) H ,
e ( a + bh ) · ( c + dh ) = ( ac - bd) + ( +ad + bc + bd ) h . Essas operações têm a mesma interpretação geométrica de suas contrapartes complexas: adição é adição vetorial e multiplicação é dimensionamento e rotação. Em particular, para girar um número hexagonal 60 ° no sentido anti-horário, multiplicamos por h :
( a + bh ) · h = - b + ( a + b ) h , e para rodar o mesmo número 60 ° no sentido horário, dividimos por h :
( a + bh ) / h = ( a bh) · (1 - h ) = (a + b) - ah . Por exemplo, podemos pegar o número hexagonal da unidade apontando para a direita, 1 = (1, 0), um círculo completo, no sentido anti-horário, multiplicando-o por h seis vezes:
(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).

O programa usa números hexagonais dessa maneira para representar a posição atual no labirinto e a direção atual, para avançar na direção especificada e girar a direção para a esquerda e para a direita.

Ell
fonte
31

Hexagonia , 2437 bytes

O tão esperado programa está aqui:

(.=$>({{{({}}{\>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'&?={&?#<$

Experimente online!

Versão "legível":

                             ( . = $ > ( { { { ( { } } { \ > 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 ' & ? = { & ? # < $

Testado no IDE esotérico: o TIO pode atingir o tempo limite em alguns dos casos de teste maiores, mas todos verificados. Muito obrigado a Timwi, isso não teria sido possível sem o IDE.

Há um pouco de espaço vazio, então eu poderia ser capaz de encaixar isso em um hexágono 28 de comprimento lateral (em vez de 29), mas essa será uma tarefa enorme, por isso provavelmente não vou tentar.

Explicação básica

Clique nas imagens para obter uma versão maior e mais detalhada.

Funções

Funções
Nota: As divisões geralmente estão corretas, mas ocasionalmente podem ser um palpite aproximado.

Este código é bastante "funcional" - tanto quanto o Hexagony permite. Existem oito funções principais nesse código, rotuladas no diagrama acima, nomeadas pelos números com os quais são chamados (portanto, os números dos ponteiros de instruções são o número mod 6). Em ordem (aproximada) de chamada, eles são (nomes citados são locais na memória que serão explicados mais adiante):

  • S: A função de partida - lê a entrada e define-se a "sequcia de refercia", em seguida, começa a "pilha caminho" com os três caminhos F, Re Lpronto para o processamento principal. O ponteiro de instrução 0 passa para a função 0 enquanto a execução passa para a função 1.
  • 1 (-11): A função principal - usa 2 para obter um caminho, 3 para verificar sua validade e, se válido, passa para a função -110 / -10 duas vezes e depois 4 três vezes para copiar os novos caminhos no caminho " pilha ", terminando retornando a si mesma. Pode chamar a função 5 se o caminho estiver no local final.
  • 2: Obtém o próximo caminho da "pilha de caminhos" pronto para processamento, chama a função -1 se não houver nenhum caminho na pilha. Retorna à função 1.
  • 3: pega um par de valores, bem como o número da movimentação e verifica a "matriz de referência" para ver se o caminho atual terminou em um local válido. Um local válido é o início dentro dos 3 primeiros movimentos, ou qualquer outro +movimento dentro de 2 movimentos após o primeiro alcance. Retorna à função 1.
  • -10 / -110: copia o caminho atual. Retorna à função 1.
  • 0: Ajuda a função 1 a gerenciar a direção do movimento F. Retorna à função 1.
  • 4: Toma uma cópia da passagem de corrente e interligados com a função 1 altera-lo para o mesmo caminho com qualquer um F, Rou Lanexado. Retorna à função 1.
  • 5: Pega o caminho e imprime o caminho correto (por exemplo FFLF), depois finaliza o programa.
  • -1: imprime Invalid maze!e termina.
  • (Setas duplas): devido à falta de espaço, a função 1 / -11 teve que sair para o espaço acima da função -1.

Memória

Layout de memória
Nota: Obrigado novamente ao IDE Esotérico pelo diagrama

A memória consiste em três partes principais:

  • Matriz de referência: a grade é armazenada nas colunas 2, com um valor em cada etapa:
    • 0 representa ou um , 0ou um lugar válido que foi acessado mais jogadas atrás do que seria necessário para sair do lugar em qualquer direção.
    • 1 representa um +que ainda não foi alcançado.
    • (número mais alto) representa o número da jogada em que haverá movimentos suficientes para sair do local em qualquer direção.
    • 10 também representa uma nova linha: nunca são alcançadas, desde que sigam imediatamente o último caractere que não é de espaço em branco.
  • Trilho: Consiste em -1s com um único -2à esquerda, permite que o ponteiro de memória retorne rapidamente à área de processamento do núcleo.
  • Pilha de caminhos: armazena cada um dos caminhos não testados em ordem pelo ID do caminho (diretamente relacionado ao número da movimentação, para que os caminhos mais curtos sejam testados primeiro). O caminho é armazenado da seguinte maneira:
    Layout do caminho
    • Rot: a rotação no final do caminho atual: 0 para a esquerda e aumentando no sentido horário para 5
    • Mover: o número do movimento atual (instruções - 1)
    • Caminho: o caminho atual, armazenada em quaternário com F, R, Lcomo 1, 2, 3respectivamente
    • x / y: coordenadas no final do caminho atual: x + 1 -1s à direita e valores de y acima (embora y = 0 seja processado como 1 de qualquer maneira para fins de separar o trilho dos dados de referência)

Outros locais importantes de memória:

  1. O x / y do Eé armazenado aqui.
  2. Esse espaço é usado para fazer a transição de caminhos para dentro e para fora da memória.
  3. Este local é o centro de onde cada caminho é armazenado durante o processamento.
boboquack
fonte
O próximo passo é executar o programa através do programa para encontrar a rota de labirinto mais curta.
Veskah
Eu sei que alguém vai postar. Finalmente ... / Eu também tenho um plano diferente, que provavelmente deve levar menos código que o seu. Na verdade, nunca tem tempo para implementá-lo.
user202729
@ user202729 seria interessante ouvir sobre isso. Esse método provavelmente pode ser jogado em pelo menos 2 tamanhos, eu diria, mas há quase certamente algo melhor por aí.
boboquack
1
Apenas esperando por @lirtosiast.
J Atkins
1
Desculpe pelo atraso.
lirtosiast 15/09/18
6

Python 3, 466 bytes

Provavelmente teria ficado menor se eu usasse uma pesquisa profunda ou algo assim. Essa monstruosidade usa Dijkstra e é bastante rápida, mas muito longa.

O código define uma função Sque pega uma cadeia de linhas múltiplas com o labirinto e retorna o 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]

Aqui está um teste do código.

Ungolfed

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
fonte
Wow muito legal. Quanto tempo você levou para escrever?
precisa
1
@JAtkin Bem, o arquivo foi criado 1,5 horas atrás, embora eu não tenha certeza de quanto tempo gastei trabalhando no código. Além disso, são 3 da manhã aqui, então minha produtividade está obviamente no máximo.
PurkkaKoodari
Bom, passei mais de 2 horas, e a maioria dos meus já foi escrita para um labirinto padrão.
precisa
Você tem uma versão ungolfed?
precisa
1
@JAtkin É necessário, porque você pode precisar se virar no início. Sem a posição inicial, ele funcionaria L,,R.
PurkkaKoodari
3

Groovy, 624 bytes. Fore!

Com o tempo, a bola rola com uma grande. Leva uma sequência de linhas múltiplas 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!")}

Versão não destruída:

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

C #, 600 574 bytes

Programa completo, aceita entrada de STDIN, saída para STDOUT.

Edit: houve um bug no manuseio do envoltório (não quebrou em nenhum dos casos de teste) que teria adicionado 1 byte, então eu fiz um pouco mais de golfe 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]);}}

Começa lendo no mapa, anexando (a cada linha para saber onde termina e pode voltar e adicionar uma carga de espaços para tornar o mapa retangular e com uma linha de espaços ao lado direito (isso economiza fazendo verificações de embrulho, como será explicado abaixo). Ele calcula a largura do retângulo em algum momento e determina o comprimento total do mapa.

Em seguida, inicializa tudo para uma pesquisa de largura em primeiro lugar. Duas matrizes grandes são criadas, uma para armazenar todos os estados que precisamos explorar em nossa pesquisa e a outra para registrar a rota que seguimos para chegar a cada estado. O estado inicial é adicionado à matriz devido, com os ponteiros de cabeça e cauda predefinidos de alguma maneira acima. Tudo é indexado em 1.

Nós então iteramos até que a cauda colide com a cabeça, ou pelo menos pareça ter colidido com a cabeça. Para cada estado que visitamos, tentamos adicionar um novo estado na mesma posição em que somos rotacionados para a esquerda ou direita e depois para onde avançamos. As direções são indexadas, com a direção inicial (padrão 0) correspondente a "cima-esquerda".

Quando tentamos enfileirar um estado, ele é marcado como checado, mas não checado, por causa das colunas de espaços no lado direito, que são captadas pelo "nós podemos estar aqui?" cheque (você não pode estar em espaços). Se o estado estiver na fila, verificaremos se está na Ecélula e, se estiver, definiremos o início da fila como menos ele mesmo, o que faz com que o loop principal saia e informa a última linha do programa para imprimir a rota correspondente, em vez da mensagem de falha (que mostra se ficarmos sem estados para expandir (a cauda bate na cabeça)).

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 a maioria das minhas pesquisas de gráficos neste site, estou fazendo bom uso de estruturas em C #, que são padrão para comparar por valor literal.

VisualMelon
fonte
2

Python 2, 703 bytes

Não é tão bom quanto as outras duas versões, mas pelo menos funciona haha. Defina Mpara o labirinto.

Como não tenho experiência em resolver labirintos, ele segue uma abordagem de força bruta, onde encontra todas as soluções possíveis que não envolvem a passagem de si mesmas. Calcula as voltas a partir das mais curtas e depois escolhe o menor resultado.

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)

Versão desarrumada desarrumada:

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))
Pedro
fonte
Você pode substituir if heading != required_heading: while heading != required_heading: por apenaswhile heading != required_heading:
J Atkin
Sim, obrigado haha, eu tinha notado algumas coisas, incluindo a versão golfada, mas não atualizei o código original, vou fazer isso agora, já que acabei de raspar mais alguns personagens
Peter
Agradável! (15 enchendo o carvão animal min)
J Atkins
<Esta é uma tag HTML não reconhecida, portanto, não fique à vontade.>
CalculatorFeline