Temps de labyrinthe hexagonal!

27

Il est temps pour un autre défi de labyrinthe, mais pas comme vous le savez.

Les règles de ce défi sont un peu différentes de la plupart des défis de labyrinthe. Les types de tuiles sont définis comme suit:

  • S: L'emplacement sur le labyrinthe où vous commencez
  • E: L'endroit où vous essayez de vous rendre
  • 0: Mur que vous ne pouvez pas traverser
  • +: Plancher que vous pouvez traverser

Vous pouvez voyager dans l'une des six directions: haut-gauche, haut-droite, gauche, droite, bas-gauche ou bas-droite.

\ /
-S-
/ \

Le labyrinthe ne s'enroule pas. Le but est de trouver la chaîne de chemin la plus courte pour aller de Sà E.

Contribution:

L'entrée est des lignes séparées par des espaces comme les labyrinthes montrés. Aucun espace de fin ne suivra une ligne.

Sortie:

Une chaîne de R, Let F

  • R vous fait pivoter à droite (dans le sens des aiguilles d'une montre) de 60 degrés
  • L vous fait tourner à gauche (dans le sens antihoraire) de 60 degrés
  • F vous déplace d'un espace dans la direction vers laquelle vous pointez

Vous commencez à pointer left-up

Le chemin le plus court est compté par la longueur de la chaîne produite et non par le nombre de positions visitées. Votre programme doit imprimer le chemin le plus court comme solution.

Si le labyrinthe est insoluble, vous devez sortir Invalid maze!.

( >>>est la sortie)

     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

(Notez que certains labyrinthes ont d'autres solutions qui sont de la même longueur mais ne sont pas répertoriées ici)

J Atkin
la source
27
En espérant une solution Hexagony ...
bkul
3
Je vais attribuer une prime de 500 points à une solution Hexagony.
lirtosiast
@ lirtosiast2 ans plus tard, je pense que l'hexagonie pourrait être un étirement pour ce problème;)
J Atkin
Attendons encore quelques années.
user202729
Peut-il y avoir une nouvelle ligne de fuite?
user202729

Réponses:

17

Python 2, 291 octets

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

Une fonction, fprenant le labyrinthe comme une liste de lignes et renvoyant une solution, si elle existe.

Explication

Effectue une recherche en premier sur le graphique des paires position / direction pour trouver le chemin le plus court de Sà E.

Ce qui est intéressant, c'est de trouver un moyen compact de représenter les positions et les directions sur une grille hexagonale, qui admet un simple «pas» (c'est-à-dire se déplacer dans une certaine direction) et une rotation. Il est tentant d'utiliser des nombres complexes ici, pour représenter des coordonnées sur une "vraie" grille hexagonale, mais ce n'est pas une très bonne idée pour un certain nombre de raisons, la plus sérieuse étant le fait que nous devons brancher un √3 quelque part pour le faire fonctionner (sin 60 ° = √3 / 2), ce qui, lorsque vous utilisez des nombres à virgule flottante, est un pas si nous avons besoin d'une précision absolue (par exemple, pour garder une trace des états que nous avons déjà visités;) vous pouvez essayer de lancer votre console JavaScript et de taper Math.sqrt(3)*Math.sqrt(3) == 3et voyez par vous-même.

Mais, nous pouvons utiliser une petite astuce! Au lieu d'utiliser des nombres complexes, définissons des nombres hexagonaux dans une veine similaire, comme une paire de nombres réels a + bh , où h joue un rôle similaire à l'imaginaire i lorsqu'il s'agit de nombres complexes. Tout comme avec les nombres complexes, nous pouvons associer la paire ( a , b ) à un point sur le plan, où l'axe réel pointe à droite, l'axe imaginaire pointe à 60 ° vers le haut, et ils croisent tous les deux l'hexagone régulier de l'unité lorsque le réel et le les parties imaginaires sont égales à 1, respectivement. Le mappage de ce système de coordonnées aux cellules du labyrinthe est trivial.

Figure 1

Contrairement à i , la constante h est définie par la relation h 2 = h - 1 (la résolution de h pourrait révéler certaines informations.) Et c'est tout! Les nombres hexagonaux peuvent être ajoutés et multipliés, en utilisant la relation ci-dessus, un peu comme les nombres complexes: ( a + bh ) + ( c + dh ) = ( a + c ) + ( b + d ) h ,
et ( a + bh ) · ( c + dh ) = ( ac - bd) + ( ad + bc + bd ) h . Ces opérations ont la même interprétation géométrique que leurs homologues complexes: l'addition est l'addition vectorielle, et la multiplication est l'échelle et la rotation. En particulier, pour faire pivoter un nombre hexagonal de 60 ° dans le sens antihoraire, on le multiplie par h :
( a + bh ) · h = - b + ( a + b ) h , et pour faire tourner le même nombre de 60 ° dans le sens horaire, on divise par h :
( a + bh ) / h = ( a +bh ) · (1 - h ) = (a + b) - ah . Par exemple, nous pouvons prendre le nombre hexagonal unitaire pointant vers la droite, 1 = (1, 0), un cercle complet, dans le sens antihoraire, en le multipliant par h six fois:
(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).

Le programme utilise des nombres hexagonaux de cette manière pour représenter la position actuelle dans le labyrinthe et la direction actuelle, pour avancer dans la direction spécifiée et pour faire pivoter la direction vers la gauche et vers la droite.

Aune
la source
31

Hexagonie , 2437 octets

Le programme tant attendu est ici:

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

Essayez-le en ligne!

Version "lisible":

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

Testé sur IDE ésotérique: TIO peut expirer sur certains des cas de test les plus importants mais tous vérifiés. Merci beaucoup à Timwi, cela n'aurait pas été possible sans l'IDE.

Il y a un peu d'espace vide, donc j'aurais peut-être pu l'ajuster sur un hexagone de côté 28 (au lieu de 29 de côté), mais ce sera une tâche énorme, donc je ne vais probablement pas essayer.

Explication de base

Cliquez sur les images pour une version plus grande et plus détaillée.

Les fonctions

Les fonctions
Remarque: Les divisions sont généralement correctes, mais peuvent parfois être approximatives.

Ce code est assez "fonctionnel" - autant que Hexagony le permet. Il y a huit fonctions principales dans ce code étiquetées dans le diagramme ci-dessus, nommées par les numéros avec lesquels elles sont appelées (donc leurs numéros de pointeur d'instructions sont ce numéro mod 6). Dans l'ordre (approximatif) d'appel, ils sont (les noms cités sont des emplacements en mémoire qui seront expliqués plus loin):

  • S: La fonction de démarrage - lit l'entrée et configure le "tableau de référence", puis démarre la "pile de chemins" avec les trois chemins F, Ret est Lprête pour le traitement principal. Le pointeur d'instruction 0 passe à la fonction 0 tandis que l'exécution passe à la fonction 1.
  • 1 (-11): La fonction principale - utilise 2 pour obtenir un chemin, 3 pour vérifier sa validité, et si valide va à la fonction -110 / -10 deux fois puis 4 trois fois pour copier les nouveaux chemins dans le "chemin pile ", terminant en revenant à lui-même. Peut appeler la fonction 5 si le chemin est à l'emplacement final.
  • 2: Obtient le chemin suivant de la "pile de chemins" prêt pour le traitement, appelle la fonction -1 s'il ne reste aucun chemin sur la pile. Retourne à la fonction 1.
  • 3: Prend une paire de valeurs ainsi que le numéro de déplacement et vérifie le "tableau de référence" pour voir si le chemin actuel s'est terminé à un emplacement valide. Un emplacement valide est soit le début dans les 3 premiers mouvements, soit tout +mouvement dans les 2 premiers mouvements. Retourne à la fonction 1.
  • -10 / -110: copie le chemin actuel. Retourne à la fonction 1.
  • 0: Aide la fonction 1 à gérer la direction du mouvement avec F. Retourne à la fonction 1.
  • 4: Prend une copie du chemin en cours et interconnecté avec la fonction 1 le change dans le même chemin avec soit F, Rsoit Lajouté. Retourne à la fonction 1.
  • 5: Prend le chemin et imprime le chemin correct (par exemple FFLF), puis termine le programme.
  • -1: Imprime Invalid maze!et se termine.
  • (Double flèches): En raison du manque d'espace, la fonction 1 / -11 a dû disparaître dans l'espace au-dessus de la fonction -1.

Mémoire

Disposition de la mémoire
Remarque: Merci encore à Esoteric IDE pour le diagramme

La mémoire se compose de trois parties principales:

  • Tableau de référence: la grille est stockée à 2 colonnes d'intervalle, avec une valeur à chaque étape:
    • 0 représente soit un lieu , 0soit un lieu valide auquel on a accédé il y a plus de coups qu'il ne faudrait pour quitter le lieu dans n'importe quelle direction.
    • 1 représente un +qui n'a pas encore été atteint.
    • (numéro plus élevé) représente le numéro de coup où il y aura eu suffisamment de coups pour quitter le lieu dans n'importe quelle direction.
    • 10 représente également une nouvelle ligne: ceux-ci ne sont jamais atteints en supposant qu'ils suivent immédiatement le dernier caractère non blanc.
  • Rail: se compose de -1s avec un seul -2sur la gauche, permet au pointeur de mémoire de revenir rapidement à la zone de traitement principale.
  • Pile de chemins: stocke chacun des chemins non testés dans l'ordre par ID de chemin (qui est directement lié au numéro de déplacement afin que les chemins les plus courts soient testés en premier). Le chemin est stocké comme suit:
    Disposition du chemin
    • Rot: la rotation à la fin de la trajectoire actuelle: 0 pour haut-gauche et augmentant dans le sens horaire à 5
    • Déplacer: le numéro de déplacement actuel (instructions - 1)
    • Path: le trajet de courant, stockée dans quaternaire avec F, R, Lcomme 1, 2, 3respectivement
    • x / y: coordonnées à la fin du trajet actuel: x + 1 -1s à droite puis y valeurs vers le haut (bien que y = 0 soit traité comme 1 de toute façon afin de séparer le rail des données de référence)

Autres emplacements de mémoire importants:

  1. Le x / y du Eest stocké ici.
  2. Cet espace est utilisé pour faire la transition des chemins dans et hors de la mémoire.
  3. Cet emplacement est le centre de stockage de chaque chemin pendant le traitement.
boboquack
la source
L'étape suivante consiste à exécuter votre programme dans votre programme pour trouver l'itinéraire de labyrinthe le plus court.
Veskah
Je sais que quelqu'un l'affichera. Enfin ... / J'ai également un plan différent, qui devrait probablement prendre moins de code que le vôtre. Jamais réellement le temps de le mettre en œuvre.
user202729
@ user202729 serait intéressant d'en entendre parler. Cette méthode peut probablement être utilisée au moins 2 tailles, je dirais, mais il y a presque certainement quelque chose de mieux là-bas.
boboquack
1
J'attends juste @lirtosiast.
J Atkin
1
Toutes mes excuses pour le retard.
lirtosiast
6

Python 3, 466 octets

Aurait probablement fini plus petit si j'avais utilisé la recherche en profondeur d'abord ou quelque chose comme ça. Cette monstruosité utilise Dijkstra et est assez rapide, mais très longue.

Le code définit une fonction Squi prend une chaîne multiligne avec le labyrinthe et renvoie le résultat.

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]

Voici un test du code.

Non 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
la source
Wow très agréable. Combien de temps cela vous a-t-il pris pour écrire?
J Atkin
1
@JAtkin Eh bien, le fichier a été créé il y a 1,5 heure, même si je ne sais pas combien de temps j'ai réellement passé à travailler sur le code. De plus, il est 3h du matin ici, donc ma productivité est évidemment à son maximum.
PurkkaKoodari
Bien, j'ai passé plus de 2 heures, et la plupart des miennes étaient déjà écrites pour un labyrinthe standard.
J Atkin
Avez-vous une version non golfée?
J Atkin
1
@JAtkin C'est nécessaire, car vous devrez peut-être faire demi-tour au début. Sans la position de départ, cela fonctionnerait L,,R.
PurkkaKoodari
3

Groovy, 624 octets. Fore!

Il est temps de faire rouler la balle avec une grosse. Prend la chaîne multiligne comme argument pourQ

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

Version non golfée:

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
la source
3

C #, 600 574 octets

Programme complet, accepte les entrées de STDIN, les sorties vers STDOUT.

Edit: il y avait un bug dans la gestion de l'habillage (ne s'est cassé sur aucun des cas de test donnés) qui aurait ajouté 1 octet, j'ai donc fait un peu plus de golf pour compenser.

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

Il commence par lire la carte, en ajoutant (à chaque ligne pour qu'il sache où elle se termine, et peut revenir en arrière et ajouter une charge d'espaces pour rendre la carte rectangulaire, et avec une rangée d'espaces sur le côté droit (cela économise nous effectuons des contrôles d'emballage comme expliqué ci-dessous). Il calcule la largeur du rectangle à un moment donné et détermine la longueur totale de la carte.

Ensuite, il initialise tout pour une recherche en largeur. Deux tableaux biggish sont créés, l'un pour stocker tous les états que nous devons explorer dans notre recherche, l'autre pour enregistrer l'itinéraire que nous avons emprunté pour arriver à chaque état. L'état initial est ajouté au tableau dû, avec les pointeurs de tête et de queue prédéfinis quelque part au-dessus. Tout est indexé 1.

Nous répétons ensuite jusqu'à ce que la queue s'écrase dans la tête, ou du moins, elle semble s'être écrasée dans la tête. Pour chaque état que nous avons visité, nous essayons d'ajouter un nouvel état à la même position où nous sommes tournés à gauche ou à droite, puis un où nous avons avancé. Les directions sont indexées, la direction initiale (par défaut à 0) correspondant à "haut-gauche".

Lorsque nous essayons de mettre en file d'attente un état, il est vérifié, mais pas encapsulé, en raison des colonnes d'espaces sur le côté droit, qui sont reprises par le "sommes-nous autorisés à être ici?" vérifier (vous n'êtes pas autorisé à être sur les espaces). Si l'état est mis en file d'attente, nous vérifions ensuite s'il se trouve dans la Ecellule, et si c'est le cas, nous définissons la tête de la file d'attente comme étant elle-même moins, ce qui provoque la sortie de la boucle principale et indique à la dernière ligne du programme d'imprimer sur l'itinéraire correspondant, plutôt que sur le message d'échec (qui montre si nous manquons d'états pour nous développer (la queue s'écrase dans la tête)).

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

Comme la plupart de mes recherches de graphiques sur ce site, je fais bon usage des structures C #, qui par défaut sont comparées par valeur littérale.

VisualMelon
la source
2

Python 2, 703 octets

Pas aussi bon que les deux autres versions, mais au moins ça marche haha. Mis Mau labyrinthe.

Comme je n'ai aucune expérience dans la résolution de labyrinthes, cela passe simplement par une approche par force brute, où il trouvera toutes les solutions possibles sans impliquer un retour en arrière sur lui-même. Il calcule les virages à partir des plus courts, puis choisit le résultat le plus court à partir de cela.

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)

Version désordonnée désordonnée:

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
la source
Vous pouvez remplacer if heading != required_heading: while heading != required_heading: par justewhile heading != required_heading:
J Atkin
Ouais merci haha, j'avais remarqué quelques choses, y compris que lorsque je faisais la version golf, je n'ai pas mis à jour le code original, je vais le faire maintenant car je viens de raser quelques personnages de plus
Peter
Agréable! (remplissant les 15 caractères min)
J Atkin
<Ceci est une balise HTML non reconnue, donc SE no likey.>
CalculatorFeline