Шестикутний час лабіринту!


27

Час ще одного виклику лабіринту, але не так, як ви це знаєте.

Правила цього виклику дещо інші, ніж більшість викликів лабіринту. Види плитки визначаються наступним чином:

  • S: Місце розташування на лабіринті, з якого ви починаєте
  • E: Місце, до якого ви намагаєтеся дістатися
  • 0: Стіна, яку ви не можете перетнути
  • +: Підлога, яку можна перетнути

Ви можете подорожувати в одному з шести напрямків: вліво, вліво, вправо, вліво, вправо, вниз-вліво або вниз-вправо.

\ /
-S-
/ \

Лабіринт не загортається. Мета полягає в тому, щоб знайти найкоротший шлях рядок , щоб отримати від Sдо E.

Вхід:

Вхід - це розділені пробілом лінії, як показані лабіринти. Жоден пробіл не буде слідувати лінії.

Вихід:

Рядок R, Lі Fде

  • R обертає вас вправо (за годинниковою стрілкою) на 60 градусів
  • L обертає ліворуч (проти годинникової стрілки) на 60 градусів
  • F переміщує вас на один пробіл у напрямку, який ви вказуєте

Ви починаєте вказувати left-up

Найкоротший шлях обчислюється довжиною виробленої рядки, а не кількістю відвідуваних позицій. Ваша програма повинна роздрукувати найкоротший шлях як рішення.

Якщо лабіринт нерозв’язний, вам слід вивести Invalid 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

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

(Зверніть увагу, що деякі лабіринти мають інші рішення, що мають однакову довжину, але тут не вказані)


27
Сподіваючись на рішення шестикутника ...
bkul

3
Я присуджую виграш у розмірі 500 балів за рішення шестикутника.
ліртосіаст

@ lirtosiast2 років потому, я думаю, що шестикутники можуть стати розтяжкою для цієї проблеми;)
J Atkin,

Зачекаємо ще кілька років.
користувач202729

Чи може бути зворотний новий рядок?
користувач202729

Відповіді:


17

Python 2, 291 байт

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

Функція, fприймаючи лабіринт як список рядків і повертаючи рішення, якщо таке існує.

Пояснення

Здійснює пошук по ширині на графіку пар положень / напрямків, щоб знайти найкоротший шлях від Sдо E.

Цікавим є пошук компактного способу представлення позицій та напрямків на шестикутній сітці, який допускає просте «крокування» (тобто рух у певному напрямку) та обертання. Тут заманливо використовувати складні числа, щоб представити координати на "справжній" шестикутній сітці, але це не дуже гарна ідея з ряду причин, найсерйознішою з яких є той факт, що нам потрібно підключити √3 десь, щоб змусити його працювати (sin 60 ° = √3 / 2), що при використанні чисел з плаваючою комою не є ходом, якщо нам потрібна абсолютна точність (наприклад, для відстеження стану, які ми вже відвідали;) ви можете спробувати запустити консоль JavaScript і ввести текст Math.sqrt(3)*Math.sqrt(3) == 3і переконатися в цьому.

Але, ми можемо скористатися невеликою хитрістю! Замість того, щоб використовувати складні числа, давайте визначимо гексагональні числа у подібному ключі, як пара дійсних чисел a + bh , де h відіграє аналогічну роль уявних i при роботі зі складними числами. Так само, як і при складних числах, ми можемо пов'язати пару ( a , b ) з точкою на площині, де реальна вісь вказує вправо, уявна вісь вказує на 60 ° вгору, і вони обидва перетинають одиничний правильний шестикутник, коли дійсний і уявні частини рівні 1 відповідно. Зіставлення цієї системи координат до осередків лабіринту тривіально.

Фігура 1

На відміну від i , константа h визначається відношенням h 2 = h - 1 (рішення для h може виявити певну думку). І це все! Шестикутні числа можна додати та помножити, використовуючи вищевказане відношення, подібно до складних чисел: ( a + bh ) + ( c + dh ) = ( a + c ) + ( b + д ) ч , і ( + BH ) · ( c + dh ) = ( ac - bd ) + ( ad + bc + bd ) h
. Ці операції мають таку ж геометричну інтерпретацію, як і їх складні аналоги: додавання - це векторне додавання, а множення - масштабування та обертання. Зокрема, щоб обертати шестикутне число на 60 ° проти годинникової стрілки, множимо його на h :
( a + bh ) · h = - b + ( a + b ) h , а для обертання того ж числа на 60 ° за годинниковою стрілкою ділимо по h :
( a + bh ) / h = ( a +)bh ) · (1 - h ) = (a + b) - ах . Наприклад, ми можемо взяти одиничне шестикутне число, яке вказує праворуч, 1 = (1, 0), повне коло, що йде проти годинникової стрілки, помноживши його на 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).

Програма таким чином використовує гексагональні числа для представлення поточної позиції в лабіринті та поточному напрямку, для просування в зазначеному напрямку та для обертання напрямку вліво і вправо.


31

Шестикутник , 2437 байт

Довгоочікувана програма тут:

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

Спробуйте в Інтернеті!

"Читана" версія:

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

Тестовано на Ezoteric IDE: TIO може затримати час у деяких великих тестових випадках, але всі перевірені. Дякую Тімві, без IDE це було б неможливо.

Тут є досить багато порожнього простору, тому я, можливо, міг би помістити це на шестикутник довжиною 28 сторін (замість довжини борта 29), але це буде великим завданням, тому я, мабуть, не збираюся його намагатися.

Основне пояснення

Клацніть зображення, щоб отримати більшу та детальну версію.

Функції

Функції
Примітка. Підрозділи, як правило, правильні, але іноді можуть бути грубими здогадами.

Цей код досить "функціональний" - стільки, скільки дозволяє Гексагоні. У цьому коді є вісім основних функцій, позначених на наведеній вище діаграмі, іменованих номерами, з якими вони викликаються (тому їхні вказівні номери інструкцій - це число mod 6). У (грубому) порядку виклику вони є (цитовані назви - це місця в пам'яті, які будуть пояснені пізніше):

  • S: починаючи функція - читає вхідний і встановлює «опорний» масив, а потім запускає «стек» шляху з трьома шляхами F, Rі Lготові до основного обробітку. Покажчик інструкції 0 переходить до функції 0, а виконання переходить до функції 1.
  • 1 (-11): Основна функція - використовує 2 для отримання шляху, 3 для перевірки його дійсності, а якщо дійсний переходить до функції -110 / -10 двічі, а потім 4 три рази для копіювання нових шляхів на "шлях" стека », закінчуючи поверненням до себе. Може викликати функцію 5, якщо шлях знаходиться в кінцевому місці.
  • 2: Отримує наступний шлях із "стека шляху", готового до обробки, функція викликів -1, якщо в стеку не залишилося жодного шляху. Повертається до функції 1.
  • 3: Бере пару значень, а також номер переміщення та перевіряє "опорний масив", щоб побачити, чи закінчився поточний шлях у дійсному місці. Дійсне місце розташування - це або початок у межах перших трьох ходів, або будь-який +протягом 2-х ходів від його першого досягнення. Повертається до функції 1.
  • -10 / -110: Копіює поточний шлях. Повертається до функції 1.
  • 0: допомагає функції 1 керувати напрямом руху за допомогою F . Повертається до функції 1.
  • 4: приймає копію поточного шляху і взаємопов'язаних з функцією 1 змін його в тому ж шляху , або з F, RабоL додається. Повертається до функції 1.
  • 5: бере шлях і виводить правильний шлях (наприклад, FFLF ), а потім припиняє програму.
  • -1: Відбитки Invalid maze! та припиняється.
  • (Подвійні стрілки): Через брак місця функція 1 / -11 повинна була вийти у простір вище функції -1.

Пам'ять

Макет пам'яті
Примітка: Завдяки Esoteric IDE знову за діаграму

Пам'ять складається з трьох основних частин:

  • Довідковий масив: Сітка зберігається в стовпчиках 2, зі значенням на кожному кроці:
    • 0 являє собою або a ,0 або дійсне місце, до якого було здійснено доступ більше кроків тому, ніж потрібно для виходу з місця в будь-якому напрямку.
    • 1 являє собою +ще не досягнутого.
    • (більша кількість) представляє номер переміщення, де було б достатньо ходів, щоб вийти з місця в будь-якому напрямку.
    • 10 також представляє новий рядок: вони ніколи не досягаються, якщо припустити, що вони безпосередньо слідують за останнім символом непробілу.
  • Rail: Складається з -1s -2зліва зліва, дозволяє вказівнику пам'яті швидко повернутися до основної області обробки.
  • Стек стежка: зберігає кожен неперевірений шлях у порядку ідентифікатора шляху (який безпосередньо пов'язаний з номером переміщення, тому коротші шляхи перевіряються спочатку). Шлях зберігається так:
    Макет шляху
    • Гниль: обертання в кінці поточного контуру: 0 вгору вліво і збільшення за годинниковою стрілкою до 5
    • Перемістити: поточний номер переміщення (інструкції - 1)
    • Шлях: поточний шлях, зберігається в четвертинному з F, R, Lа 1, 2, 3відповідно ,
    • x / y: координати в кінці поточного шляху: x + 1 -1s праворуч, тоді значення y вгору (хоча y = 0 обробляється як 1 все одно для цілей відокремлення рейки від еталонних даних)

Інші важливі місця пам'яті:

  1. Х / у E зберігається .
  2. Цей простір використовується для переходу шляхів переходу в пам'ять і поза нею.
  3. Це місце є центром, де зберігається кожен шлях під час обробки.

Наступним кроком є ​​запуск вашої програми через вашу програму, щоб знайти найкоротший маршрут лабіринту.
Веска

Я знаю, хтось опублікує це. Нарешті ... / У мене також інший план, який, мабуть, повинен брати менше коду, ніж ваш. Ніколи насправді не встигайте її здійснити.
користувач202729

@ user202729 було б цікаво почути про це. Цей метод, можливо, може бути гольф принаймні на 2 розміри вниз, я б сказав, але майже напевно є щось краще там.
boboquack

1
Тільки чекаю @lirtosiast.
Дж. Аткін

1
Вибачення за затримку.
lirtosiast

6

Python 3, 466 байт

Напевно, вийшло б менше, якби я використовував перший пошук глибини або щось подібне. Ця чудовисько використовує Дейкстру і досить швидко, але дуже довго.

Код визначає функцію, Sяка бере багаторядковий рядок з лабіринтом і повертає результат.

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]

Ось тест коду.

Безумовно

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

Нічого собі, дуже приємно. Скільки часу вам знадобилося писати?
Дж. Аткін

1
@JAtkin Ну, файл був створений 1,5 години тому, хоча я не впевнений, скільки часу я фактично витратив на роботу над кодом. Також тут 3 години ночі, тож моя продуктивність очевидно на максимумі.
PurkkaKoodari

Приємно, я провів 2+ годин, і більша частина моєї вже була написана на стандартний лабіринт.
Дж. Аткін

У вас є версія, що не має волі?
Дж. Аткін

1
@JAtkin Це потрібно, тому що, можливо, вам потрібно буде розвернутися на початку. Без вихідної позиції він би працював L,,R.
PurkkaKoodari

3

Groovy, 624 байти. Назад!

Час часу, щоб м'яч котився з великим. Приймає багаторядковий рядок як аргументQ

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

Безгольова версія:

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 байт

Повна програма, приймає вхід від STDIN, вихід на STDOUT.

Редагувати: в обробці обгорткою виникла помилка (не зламалася жодна з наведених тестових випадків), яка додала б 1 байт, тому я зробив трохи більше гольфу для компенсації.

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

Починається з читання на карті, додавання ( до кожного рядка, щоб він знав, де він закінчується, і може повернутися назад і додати в набір пробілів, щоб зробити карту прямокутною та з рядом пробілів уздовж правої сторони (це економить ми робимо обгорткові чеки, як буде пояснено нижче). Він визначає ширину прямокутника в якийсь момент цього і визначає загальну довжину Карти.

Далі він ініціалізує все для пошуку в першу чергу. Створено два масиви biggish, один для зберігання всіх станів, які нам потрібно дослідити, під час пошуку, інший для запису маршруту, який ми пройшли, щоб дістатися до кожного штату. Початковий стан додається до відповідного масиву, в якому вказівники голови та хвоста попередньо встановлені вище. Все є 1-індексованим.

Потім ми повторюємо, поки хвіст не вріжеться в голову, або, принаймні, схоже , врізався в голову. Для кожного стану, який ми відвідали, ми намагаємося додати новий стан у тому самому положенні, де ми повертаємося вліво або вправо, а потім у той, де ми рухалися вперед. Напрямки індексуються з початковим напрямком (за замовчуванням до0 ) відповідає "вгору-вліво".

Коли ми намагаємося встановити чергу, стан перевіряється, але не перевіряється загортанням через стовпчики пробілів на правій стороні, на яких позначено "чи ми можемо бути тут?" перевірити (вам не дозволяється знаходитись у пробілах). Якщо стан у черзі, ми перевіряємо, чи знаходиться він у Eкомірці, і якщо він є, встановлюємо голову черги як мінус, що призводить до виходу основного циклу, і повідомляє друку останнього рядка програми відповідний маршрут, а не повідомлення про помилку (яке показує, якщо у нас не вистачає штатів для розширення (хвіст врізається в голову)).

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

Як і більшість моїх графічних пошуків на цьому сайті, я добре використовую структури C #, які за замовчуванням можна порівняти за буквальним значенням.


2

Python 2, 703 байт

Не так добре, як інші дві версії, але принаймні це працює ха-ха. Встановити Mна лабіринт.

Оскільки я не маю досвіду розв’язування лабіринтів, він просто проходить під грубою силою, де він знайде всі можливі рішення, які не передбачають перетину через себе. Він обчислює оберти від найкоротших, а потім вибирає найкоротший результат із цього.

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)

Брудний неозорений варіант:

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

Ви можете замінити if heading != required_heading: while heading != required_heading: простоwhile heading != required_heading:
J Atkin

Так, спасибі, ха-ха, я помітив кілька речей, серед яких те, що, роблячи версію для гольфу, просто не оновлював оригінальний код, я це зроблю зараз, оскільки мені вдалося постригти ще кілька символів
Пітер

Приємно! (заповнення 15 чарівних хвилин)
J Atkin

<Це нерозпізнаний тег HTML, так що SE не подобається.>
CalculatorFeline
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.