Sześciokątny czas labiryntu!


27

Czas na kolejne wyzwanie labiryntowe, ale nie takie, jakie znasz.

Zasady tego wyzwania są nieco inne niż większość wyzwań w labiryncie. Typy kafelków są zdefiniowane w następujący sposób:

  • S: Lokalizacja w labiryncie, od której zaczynasz
  • E: Lokalizacja, do której próbujesz się dostać
  • 0: Ściana, której nie można przekroczyć
  • +: Podłoga, którą można przejść

Możesz podróżować w jednym z sześciu kierunków: góra-lewo, góra-prawo, lewo, prawo, dół-lewo lub dół-prawo.

\ /
-S-
/ \

Labirynt się nie zawija. Celem jest znalezienie najkrótszego ciągu ścieżki, z którego można przejść Sdo E.

Wkład:

Dane wejściowe to oddzielone spacjami linie, takie jak pokazane labirynty. Żadne końcowe miejsce nie będzie podążać za linią.

Wydajność:

Łańcuch R, Li Fgdzie

  • R obraca cię w prawo (zgodnie z ruchem wskazówek zegara) o 60 stopni
  • L obraca w lewo (w lewo) o 60 stopni
  • F przesuwa cię o jedną spację w kierunku, który wskazujesz

Zaczynasz wskazywać left-up

Najkrótsza ścieżka jest liczona na podstawie długości wyprodukowanego ciągu, a nie liczby odwiedzonych pozycji. Twój program musi wydrukować najkrótszą ścieżkę jako rozwiązanie.

Jeśli labirynt jest nierozwiązywalny, powinieneś wyjść Invalid maze!.

( >>>to wynik)

     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

(Pamiętaj, że niektóre labirynty mają inne rozwiązania, które są tej samej długości, ale nie są tutaj wymienione)


27
Mając nadzieję na rozwiązanie sześciokątne ...
bkul

3
Przyznam nagrodę w wysokości 500 punktów za rozwiązanie sześciokątne.
lirtosiast

@ lirtosiast2 lata później, myślę, że heksagonia może być problemem dla tego problemu;)
J Atkin 1'18

Poczekajmy jeszcze kilka lat.
user202729,

Czy może być nowa linia?
user202729

Odpowiedzi:


17

Python 2, 291 bajtów

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

Funkcja, fprzyjmująca labirynt jako listę wierszy i zwracająca rozwiązanie, jeśli takie istnieje.

Wyjaśnienie

Wykonuje pierwsze wyszukiwanie na wykresie par pozycji / kierunku w celu znalezienia najkrótszej ścieżki od Sdo E.

Interesujące jest znalezienie zwartego sposobu reprezentowania pozycji i kierunków na sześciokątnej siatce, który pozwala na proste „kroczenie” (tj. Poruszanie się w określonym kierunku) i obrót. Kuszące jest tutaj stosowanie liczb zespolonych do reprezentowania współrzędnych na „prawdziwej” siatce heksagonalnej, ale nie jest to bardzo dobry pomysł z wielu powodów, z których najpoważniejszym jest fakt, że musimy podłączyć √3 gdzieś, żeby to zadziałało (sin 60 ° = √3 / 2), co przy użyciu liczb zmiennoprzecinkowych nie jest możliwe, jeśli potrzebujemy absolutnej precyzji (np. do śledzenia stanów, które już odwiedziliśmy;) możesz spróbować uruchomić konsolę JavaScript i pisać Math.sqrt(3)*Math.sqrt(3) == 3i przekonać się sam.

Ale możemy użyć małej sztuczki! Zamiast używać liczb zespolonych, zdefiniujmy liczby heksagonalne w podobnej żyle, jako parę liczb rzeczywistych a + bh , gdzie h odgrywa podobną rolę do urojonej liczby i w przypadku liczb zespolonych. Podobnie jak w przypadku liczb zespolonych, możemy powiązać parę ( a , b ) z punktem na płaszczyźnie, w którym oś rzeczywista jest skierowana w prawo, oś urojona jest skierowana w górę o 60 ° i oba przecinają jednostkowy sześciokąt, gdy rzeczywista i części urojone wynoszą odpowiednio 1. Mapowanie tego układu współrzędnych do komórek labiryntu jest banalne.

Rycina 1

W przeciwieństwie do i , stała h jest zdefiniowana przez relację h 2 = h - 1 (rozwiązanie dla h może ujawnić pewne spostrzeżenia.) I to wszystko! Numery sześciokątne mogą być dodawane i mnoży, wykorzystując powyższy związek, tak jak na liczbach zespolonych ( + bh ) + ( C + DH ) = ( + c ) + ( b + d ) H , oraz ( + bh ) · ( c + dh ) = ( ac - bd
) + ( ad + bc + bd ) h . Operacje te mają taką samą interpretację geometryczną jak ich złożone odpowiedniki: dodawanie to dodawanie wektora, a mnożenie to skalowanie i obrót. W szczególności, aby obrócić liczbę heksagonalną o 60 ° w kierunku przeciwnym do ruchu wskazówek zegara, mnożymy ją przez h :
( a + bh ) · h = - b + ( a + b ) h , a aby obrócić tę samą liczbę o 60 ° w prawo, dzielimy do godz . :
( a + bh ) / h = ( a +bh ) · (1 - h ) = (a + b) - ah . Na przykład, możemy wziąć jednostkową liczbę heksagonalną wskazującą w prawo, 1 = (1, 0), pełny okrąg, idąc w lewo, sześciokrotnie mnożąc go przez h :
(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).

Program używa w ten sposób liczb heksagonalnych, aby przedstawić bieżącą pozycję w labiryncie i bieżący kierunek, aby przejść w określonym kierunku i obrócić kierunek w lewo i w prawo.


31

Sześciokąt , 2437 bajtów

Długo oczekiwany program jest tutaj:

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

Wypróbuj online!

Wersja „czytelna”:

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

Testowane na Esoteric IDE: TIO może przekroczyć limit czasu w niektórych większych przypadkach testowych, ale wszystkie zostaną zweryfikowane. Wielkie dzięki Timwi, nie byłoby to możliwe bez IDE.

Jest trochę pustej przestrzeni, więc mogłem zmieścić to na sześciokącie o długości boku 28 (zamiast boku o długości 29), ale będzie to ogromne zadanie, więc prawdopodobnie nie zamierzam tego robić.

Podstawowe objaśnienie

Kliknij obrazy, aby uzyskać większą i bardziej szczegółową wersję.

Funkcje

Funkcje
Uwaga: Podziały są ogólnie poprawne, ale czasami mogą być trudne do odgadnięcia.

Ten kod jest dość „funkcjonalny” - na tyle na ile pozwala na to Hexagony. W tym kodzie jest osiem głównych funkcji oznaczonych na powyższym schemacie, nazwanych liczbami, z którymi są wywoływane (więc ich numery wskaźników instrukcji to ta liczba mod 6). W (przybliżonej) kolejności wywoływania są to (cytowane nazwy to miejsca w pamięci, które zostaną wyjaśnione później):

  • S: Funkcja wyjściowy - odczytuje dane wejściowe i ustawia „tablicy odniesienia”, wówczas rozpoczyna się „stos”, ścieżka z trzech ścieżek F, Ri Lgotowy do głównej obróbki. Wskaźnik instrukcji 0 przesuwa się do funkcji 0, a wykonanie przechodzi do funkcji 1.
  • 1 (-11): Główna funkcja - używa 2, aby uzyskać ścieżkę, 3, aby sprawdzić jej poprawność, a jeśli poprawna, przechodzi do funkcji -110 / -10 dwa razy, a następnie 4 trzy razy, aby skopiować nowe ścieżki na ścieżkę „ stos ”, kończąc powracając do siebie. Może wywoływać funkcję 5, jeśli ścieżka znajduje się w miejscu końcowym.
  • 2: Pobiera kolejną ścieżkę ze „stosu ścieżek” gotową do przetwarzania, wywołuje funkcję -1, jeśli na stosie nie ma ścieżek. Powrót do funkcji 1.
  • 3: Pobiera parę wartości, a także numer ruchu i sprawdza „tablicę odniesienia”, aby sprawdzić, czy bieżąca ścieżka zakończyła się w prawidłowej lokalizacji. Prawidłowa lokalizacja to albo początek w ciągu pierwszych 3 ruchów, albo dowolny +w ciągu 2 ruchów od pierwszego osiągnięcia. Powrót do funkcji 1.
  • -10 / -110: Kopiuje bieżącą ścieżkę. Powrót do funkcji 1.
  • 0: Pomaga funkcji 1 w zarządzaniu kierunkiem ruchu za pomocą F. Powrót do funkcji 1.
  • 4. Bierze kopię ścieżki prądowej i połączone z funkcją 1 zmienia go w tej samej ścieżce albo z F, Ralbo Ldodane. Powrót do funkcji 1.
  • 5: Pobiera ścieżkę i drukuje prawidłową ścieżkę (np. FFLF), A następnie kończy działanie programu.
  • -1: Drukuje Invalid maze!i kończy.
  • (Podwójne strzałki): Z powodu braku miejsca funkcja 1 / -11 musiała wyjść w przestrzeń nad funkcją -1.

Pamięć

Układ pamięci
Uwaga: Jeszcze raz dzięki Esoteric IDE dla diagramu

Pamięć składa się z trzech głównych części:

  • Tablica odniesienia: Siatka jest przechowywana oddzielnie w kolumnach 2, z wartością na każdym kroku:
    • 0 oznacza albo , 0lub ważnego miejsca, które było dostępnego więcej ruchów temu niż byłoby wymagane, aby zakończyć się w dowolnym kierunku.
    • 1 oznacza, +że jeszcze nie został osiągnięty.
    • (wyższa liczba) reprezentuje liczbę ruchów, w której będzie wystarczająco dużo ruchów, aby wyjść z miejsca w dowolnym kierunku.
    • 10 również reprezentuje nowy wiersz: nigdy nie są osiągane, zakładając, że natychmiast następują po ostatnim znaku spacji.
  • Szyna: Składa się -1z pojedynczego -2po lewej, pozwala wskaźnikowi pamięci szybko wrócić do obszaru przetwarzania rdzenia.
  • Stos ścieżek: przechowuje każdą z nieprzetestowanych ścieżek w kolejności według identyfikatora ścieżki (która jest bezpośrednio związana z numerem ruchu, więc najpierw testowane są krótsze ścieżki). Ścieżka jest przechowywana w następujący sposób:
    Układ ścieżki
    • Rot: obrót na końcu bieżącej ścieżki: 0 dla góra-lewo i wzrastanie zgodnie z ruchem wskazówek zegara do 5
    • Move: aktualny numer ruchu (instrukcje - 1)
    • Ścieżka: aktualna ścieżka, przechowywane w czwartorzędowych z F, R, Ljak 1, 2, 3odpowiednio
    • x / y: współrzędne na końcu bieżącej ścieżki: x + 1 -1s w prawo, a następnie wartość y w górę (chociaż y = 0 i tak jest przetwarzane jako 1 w celu oddzielenia szyny od danych odniesienia)

Inne ważne lokalizacje pamięci:

  1. Tutaj znajduje się x / y E.
  2. Ta przestrzeń służy do przejścia ścieżek do i z pamięci.
  3. Ta lokalizacja jest centrum przechowywania każdej ścieżki podczas przetwarzania.

Następnym krokiem jest uruchomienie programu przez program, aby znaleźć najkrótszą trasę labiryntu.
Veskah

Wiem, że ktoś to opublikuje. Wreszcie ... / Mam też inny plan, który prawdopodobnie powinien zająć mniej kodu niż twój. Nigdy nie mam czasu na jego wdrożenie.
user202729,

@ user202729 byłby interesujący, aby o tym usłyszeć. Powiedziałbym, że tą metodą można grać w golfa przynajmniej o 2 rozmiary w dół, ale prawie na pewno jest coś lepszego.
Boboback

1
Tylko czekam na @lirtosiast.
J Atkin

1
Przeprosiny za opóźnienie.
lirtosiast

6

Python 3, 466 bajtów

Prawdopodobnie skończyłby się mniej, gdybym użył wyszukiwania w głębokości lub czegoś takiego. Ta potworność używa Dijkstry i jest dość szybka, ale bardzo długa.

Kod definiuje funkcję, Sktóra pobiera ciąg labiryntowy z labiryntu i zwraca wynik.

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]

Oto test kodu.

Nie golfił

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

Wow bardzo fajnie. Jak długo zajęło ci pisanie?
J Atkin

1
@JAtkin Cóż, plik został utworzony 1,5 godziny temu, chociaż nie jestem pewien, ile czasu faktycznie spędziłem na pracy nad kodem. Tutaj jest trzecia nad ranem, więc moja produktywność jest oczywiście na najwyższym poziomie.
PurkkaKoodari,

Fajnie, spędziłem ponad 2 godziny, a większość mojego była już napisana dla standardowego labiryntu.
J Atkin

Czy masz wersję bez golfa?
J Atkin,

1
@JAtkin Jest to potrzebne, ponieważ może być konieczne odwrócenie się na początku. Bez pozycji początkowej działałoby L,,R.
PurkkaKoodari,

3

Groovy, 624 bajtów. Dziobowy!

Czas czas, aby piłka toczyła się z dużą. Pobiera ciąg wieloliniowy jako argument doQ

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

Wersja bez golfa:

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

3

C #, 600 574 bajtów

Kompletny program, przyjmuje dane wejściowe z STDIN, dane wyjściowe do STDOUT.

Edycja: wystąpił błąd w obsłudze zawijania (nie zepsuł się w żadnym z podanych przypadków testowych), który dodałby 1 bajt, więc zrobiłem trochę golfa, aby to zrekompensować.

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

Zaczyna się od czytania na mapie, dołączania (do każdej linii, aby wiedział, gdzie się kończy, i może wrócić i dodać mnóstwo spacji, aby mapa była prostokątna, oraz z rzędem spacji po prawej stronie (oszczędza to wykonujemy kontrole pakowania, jak wyjaśnimy poniżej). W pewnym momencie oblicza szerokość prostokąta i określa całkowitą długość mapy.

Następnie inicjuje wszystko do pierwszego wyszukiwania szerokości. Tworzone są dwie duże tablice, jedna do przechowywania wszystkich stanów, które musimy zbadać w naszym wyszukiwaniu, druga do rejestrowania trasy, którą wybraliśmy się do każdego stanu. Stan początkowy jest dodawany do odpowiedniej tablicy, a wskaźniki głowy i ogona są wstępnie ustawione nieco powyżej. Wszystko ma indeks 1.

Następnie iterujemy, dopóki ogon nie uderzy w głowę, a przynajmniej wydaje się, że uderzył w głowę. Dla każdego stanu, który odwiedziliśmy, próbujemy dodać nowy stan w tym samym miejscu, w którym jesteśmy obracani w lewo lub w prawo, a następnie taki, w którym przesuwaliśmy się do przodu. Kierunki są indeksowane, a początkowy kierunek (domyślnie to 0) odpowiada „w górę w lewo”.

Gdy próbujemy stanić w kolejce stan, jest on sprawdzany, ale nie jest sprawdzany zawijaniem, ze względu na kolumny spacji po prawej stronie, które są odbierane przez „czy możemy tu być?” sprawdź (nie możesz przebywać na spacjach). Jeśli stan jest w kolejce, sprawdzamy, czy znajduje się on w Ekomórce, a jeśli tak, to ustawiamy, że głowa kolejki ma wartość minus sama, co powoduje zamknięcie głównej pętli i informuje ostatni wiersz programu, aby wydrukował poza odpowiednią trasę zamiast komunikatu o błędzie (który pokazuje, czy zabraknie stanów do rozwinięcia (ogon uderza w głowę).

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

Podobnie jak większość moich Wyszukiwań Grafów na tej stronie, dobrze wykorzystuję struktury C #, które domyślnie porównują dosłownie.


2

Python 2, 703 bajty

Nie tak dobre jak pozostałe dwie wersje, ale przynajmniej działa haha. Ustaw Msię w labiryncie.

Ponieważ nie mam doświadczenia w rozwiązywaniu labiryntów, stosuje podejście oparte na brutalnej sile, w którym znajdzie wszystkie możliwe rozwiązania, które nie wymagają przekraczania samego siebie. Oblicza zakręty od najkrótszych, a następnie wybiera z nich najkrótszy wynik.

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)

Brudna nie golfowa wersja:

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

Możesz zastąpić if heading != required_heading: while heading != required_heading: tylkowhile heading != required_heading:
J Atkin

Tak, dziękuję haha, zauważyłem kilka rzeczy, w tym, że podczas gry w golfa, po prostu nie zaktualizowałem oryginalnego kodu, zrobię to teraz, ponieważ udało mi się zgolić jeszcze kilka znaków
Peter

Miły! (wypełnienie 15 min min)
J Atkin

<To jest nierozpoznany tag HTML, więc SE nie lubię.>
CalculatorFeline
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.