Миша з динамітом


23

Ти миша. Ваші друзі миші були захоплені, вони непритомніли і потрапили в лабіринт, який має лише один вхід / вихід. У вас, мабуть, є ідеальна карта лабіринту, тож ви можете скласти рішення, щоб кинутися і перенести їх на безпеку. Однак лабіринт охороняється системою безпеки, яка спровокує сповіщення, якщо 1000буде досягнуто поріг , що призведе до того, що ви потрапите в полон і не зможете виконати свою рятувальну місію.

З попередніх досліджень лабіринту кожен квадрат, на який ви крокуєте (тобто кожен горизонтальний або вертикальний рух - миші не можуть рухатися по діагоналі ), додає 1до лічильника системи безпеки. Однак, якщо ви переносите вагу (або блок динаміту, або непритомний друг миші), він замість цього додає, 2оскільки виявляє додатковий тиск. На площі входу / виходу немає цієї системи безпеки, і тому вона не додається до лічильника.

У вас є необмежений запас динаміту, який ви принесли до входу, тому ви можете просто підірвати всі стіни, щоб звільнити своїх друзів. Але з цим потрібно бути обережними, оскільки кожен вибух збільшує 50лічильник від струсу струсу. Крім того, ви можете переносити по одній речі одночасно - одну мишу або один блок динаміту. Оскільки кожен блок динаміту може підірвати лише один простір для стін, це означає, що якщо є кілька стін поспіль, вам потрібно здійснити поїздку з порожніми руками назад до входу, щоб захопити більше.

Приклад, пророблений

Припустимо, наш лабіринт виглядає наступним чином:

######
#M# E#
######

Я буду використовувати cдля лічильника. Ми починаємо в EN-Trance, перемістити один квадрат вліво, несучи динаміт, c=2. Ми вибухали динаміт , щоб підірвати стіну, c=52. Ми рухаємо два квадрати вліво, з порожніми руками, щоб дістати c=54, і ми тепер стоїмо на площі миші. Ми підбираємо нашого друга і переміщуємо 3 квадрата назад до Exit, але останній квадрат не рахується, оскільки у нього немає ніяких датчиків, тож це всього 2 квадрата з чимось на спині. Це означає, що коли ми доходимо до виходу з кінцевою мишкою c=58, що менше 1000, і тому місія вдається.

Виклик

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

Вхідні дані

  • 2D-лабіринт у будь-якому прийнятному форматі (багаторядковий рядок, масив рядків тощо).
  • Для цього завдання я буду використовувати #як внутрішні, так і зовнішні стіни, Mдля друзів миші та Eдля входу.
  • Вхід ніколи не буде примикати безпосередньо до внутрішньої стіни (завжди знайдеться хоча б один простір, в якому можна вільно пересуватися).
  • Ви можете підміняти будь-які друковані символи ASCII, якщо ви хочете, щоб це було послідовно. Це дійсно дозволяє використовувати два різні символ для внутрішніх стін проти зовнішніх стін, так довго , як ви підтримувати узгодженість (наприклад, якщо ви вирішили використовувати @для внутрішніх стін замість і залишити #на зовнішній вигляд, кожна внутрішня стінка повинна бути @і кожна зовнішня стіна #).
  • Лабіринт завжди буде повністю обмежений стінами, але не обов’язково прямокутний. За бажанням ви можете припустити, що лабіринт прокладений пробілами, щоб зробити прямокутний введення (необов’язково).
  • У лабіринті можуть бути ділянки, недоступні без динаміту.
  • Ви не можете динамізувати зовнішні стіни лабіринту.

Вихід

Truthy / falsey значення. Truthy для "Так, миша може врятувати будь-яку іншу мишку" або Falsey за "Ні, сигналізація буде спрацьована".

Правила

  • Прийнятна або повна програма, або функція.
  • Стандартні лазівки заборонені.
  • Це тому діють усі звичайні правила гольфу, і найкоротший код (у байтах) виграє.

Приклади

Приклади правди, розділені порожніми рядками.

#####
#M E#
#####

######
#M# E#
######

########
#E  # M#
#   #  #
#   #  #
#      #
########

#############################
#    ##      #       #      #
#  M ## M    #       #      #
#    ##      #   M   #    E #
#M   ##      #       #      #
#############################

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMM MM#
#MMMMMMMMMMMME#
###############

Приклади Фальсі, розділені порожніми рядками

#############################
#M   ##      ##      ##     #
#  M ## M    ##      ##     #
#    ##      ##  M   ##   E #
#M   ##      ##      ##     #
#############################
#############################
                     ########
                     ########
                     #   #  #
                     # M # M#
                     ########

              #####
              # M #
              #####
              #####
              #####
              #####
###################
# # # ##   ## # # #
#M#M#M## E ##M#M#M#
# # # ##   ## # # #
###################
#######
######
#####
####
# M#
####

###############
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMMM#
#MMMMMMMMMMMME#
###############

3
Миші були люті (м'який спойлер)
Луїс Мендо

3
@AlexA. Вибачте, що вам довелося дізнатися це від незнайомця в Інтернеті. ;-)
AdmBorkBork

2
Я підозрюю, що більшості людей важко буде вирішити це за допомогою звичайного коду, не кажучи вже про те, щоб пограти в нього. Це великий виклик, над яким я, на жаль, не маю часу працювати.
Моше Кац

2
Чи прийнятно мати інший шар для зовнішніх стін (так як вони не піддаються проникненню)?
Тенсібай

2
@Moshe Katz , впевнений , що у тебе немає часу. Ви просто не хочете врятувати Мазу.
msh210

Відповіді:


19

Perl, 216 215 байт

Включає +2 для -0p

Введіть дані про STDIN. Використовуйте %для зовнішніх стін, #для внутрішніх стін, 0для порожніх місць, 8для мишей та rдля вихідного положення. Цілі дошки повинні бути оббиті, щоб вона утворювала прямокутник. Ви можете перетворити та запустити приклади як:

cat dynamite.txt | perl -p0e 's/.+/$a^=$&/egr;s//sprintf"%-*s",length$a,$&/eg;1while/\n/,s/^ *\K#|#(?= *$)|^ *.{@{-}}\K#|\A[^\n]*\K#|#(?=[^\n]*\n\z)|#(?=.{@{-}} *$)/%/sm;y/ EM/0x2/' | dynamite.pl

dynamite.pl:

#!/usr/bin/perl -0p
sub f{@a{@_}||=push@{$%+($&?$1?50:$&=~8?0:$&&"5"?2:1:0)},@_}f$_;for(@{$%}){f y/xr|/ytx/r;{f s/\pL\d/$&^(E&$&)x2/er}{f s/(q|s|y)#/$&^"\x01\x13"/er}my$o;{$\|=/x/>/2/;$o.="
"while s/.$/$o.=$&,""/meg}f$o}$%++>999|$\||redo}{

Замініть \xhh кривошип для заявленої оцінки.

Програма не може реально обробляти складні випадки. Зокрема, він не може впоратися з жодним із випадків відмови. Це тому, що існує занадто багато різних способів підірвати внутрішні стінки або забрати мишей, щоб пошук став занадто широким і використовував занадто багато пам’яті, хоча він принаймні досить розумний, щоб ніколи не обробляти один і той же стан кілька разів. Межа тиску повинна бути знижена до100 приблизно цього, для деякого терміну виконання та використання пам'яті.

Пояснення

Я використовую бітовий візерунок символу для представлення стану поля:

contains victim: 0000 0010
has hero:        0100 0000
carry dynamite   0000 0001
carry mouse      0000 0100
home             0000 1000
walkable         0001 0000 (not really needed but results in shorter regexes)
make printable   0010 0000
wall             0010 xxxx (bit patterns not very important,
permawall        0010 xxxx  just avoid letters and digits)

Наприклад, герой ( 01000000), що переносить динаміт ( 00000001), повинен знаходитися на місці, по якому він може ходити ( 00010000), і ми хочемо, щоб усі значення були надруковані ASCII ( 00100000). Взяття orпобіжно всіх цих макетів дає01110001 код ASCII q. Всього це стає ::

p: hero                     r hero on victim
q: hero carrying dynamite   s hero carrying dynamite on victim
t: hero carrying mouse      v hero carrying mouse on victim

x : hero at home
y : hero at home carrying dynamite
| : hero at home carrying mouse

0: empty  without hero
8: home   without hero
2: victim without hero

%: permanent wall
#: normal wall

Програма буде враховувати лише того, що герой рухається праворуч (обертання, пояснене пізніше, подбає про інші напрямки). Бітмаски були ретельно вибрані таким чином, щоб герой завжди був представлений буквою та місцем, до якого він може переміститися цифрою (за винятком того, що герой вдома несе жертву, але знову це навмисно, щоб єдиним кроком героя було падіння потерпілий). Так герой, який може рухатися вперед, узгоджується /\pL\d/. Збірна підрядка повинна бути модифікована так, щоб герой і те, що він несе, були видалені з першого символу і додані до другого, що може бути виконано з побітним розміром xorз однаковим значенням для обох символів. Значення xor складається з біта героя ( 01000000), біта динаміту ( 00000001) та біта миші ( 00000100). Разом вони orдо01000101 який ASCII)E . Отже, переміщення героя стає:

s/\pL\d/$&^(E&$&)x2/e

Герой може підірвати стіну , якщо він стоїть прямо перед ним і несе динаміт ( q, sабо y). Герой втратить свій динаміт ( xorз 00000001) і стіна #зміниться на прохід 0(xor з 00010011), так

s/(q|s|y)#/$&^"\x01\x13"/e

Для обробки інших напрямків повертається вся дошка (повернута дошка закінчується $o):

my$o;$o.="\n"while s/.$/$o.=$&,""/meg

Окрім руху героя, є також ряд інших варіантів, які він може зробити:

When at home, pick up dynamite:                   x -> y
When on victim not carrying anything pick him up: r -> t
When at home carrying a victim, drop him off:     | -> x

Це робиться шляхом

y/xr|/ytx/

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

/x/>/2/

Після того, як дошка буде вирішена, я хочу запам'ятати цей стан і наприкінці роздрукувати його. Для цього я несу в собі прапор "вирішено" $\і друкую, що в кінці програми без друку $_, так

$\|=/x/>/2/  ...   }{

Стани, що підлягають обробці при тиску 0, зберігаються @0, під тиском 1 @1і т. Д. Поточний тиск підтримується $%. Використання $nабо щось подібне було б коротше, але код не працює, якщо змінна не ініціалізується на щось, тому що автовівіфікація в іншому випадку зміниться $nна посилання ARRAY. Перегляд станів під певним тиском робиться за допомогою a, forа не mapтому, що з a forви можете розширити масив, поки він ще перекидається, і підбиратиме нові елементи. Це потрібно, тому що обертання та вибір одного поля героя трапляються за 0 разів і опиняються в одному масиві тиску. Отже, петля для заданого тиску робиться за допомогою

for(@{$%}){...}

Це повторюється, поки тиск не досягне 1000 або не знайдеться розчин:

$%++>999|$\||redo

Залишилось лише додавання нововиявлених станів до їх відповідних масивів тиску. Це буде зроблено за допомогою підпрограми f. Ми хочемо додати стан лише тоді, коли він ще не був помічений. Держави, які були помічені раніше, зберігаються %aтаким чином:

sub f{@a{@_}||=push@$n, @_}

$nявляє собою новий тиск для держави. Я виведу це із стану, що змінні регулярних виразів все ще є в результаті дії героя, що веде до цього виклику:

if $1 is set it was from s/(q|s|y)#/$&^"\x01\x13"/e which blows a wall -> 50
else if $& is set it was from s/\pL\d/$&^(E&$&)x2/e, so the hero moves.
    if $& contains 8 the hero went home -> 0
    else if the hero has carry bits (5) -> 2
    else                                   1
else ($& was not set) it was from y/xr|/ytx/r -> 0

Це призводить до наступної формули для $n:

$%+($&?$1?50:$&=~8?0:$&&"5"?2:1:0)

Усі заміни отримують rмодифікатор, тому вони повертають змінений стан і залишають поточний стан у $_спокої. fпотім викликається у цьому зміненому стані, тож ви отримуєте подібний код

f y/xr|/ytx/r;

Оскільки для обчислення $nпотреб змінні регулярних виразів вони повинні бути встановлені за замовчуванням, якщо заміна нічого не змінить (оскільки умова для їх запуску не виконується). Я також не повинен підбирати жодні змінні регексу з попереднього циклу. Тому заміни загортаються в {}блоки для збереження та відновлення стану регулярних виразів. Ось так ви отримуєте висловлювання на кшталт

{f s/\pL\d/$&^(E&$&)x2/er}

Зокрема, обертання настільки завершено, що викликає fбез змінних змінних величин і отримує внесок тиску 0.

Єдине, що ще потрібно зробити, це прокласти @0в початковому стані на початку

f$_

Це в основному циклі, тому він також намагається додати $_до більш пізніх масивів тиску, але оскільки початковий стан вже буде %aні в чому не відбудеться.

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


3
Ооо, інтригуюче. Це значно коротше, ніж я очікував на будь-яку відповідь. Ви можете додати трохи пояснень? Я не дуже б'ю Perl.
AdmBorkBork

3
@TimmyD Гаразд, пояснення додано з достатньою кількістю деталей, щоб навіть програміст, який не мав перл, мав хоча б скласти враження, як це працює
Ton Hospel

1
Дуже вражає!
Емінья

Дивовижний гольф, це справді вражає ... Коли я думаю, що я не так поганий у гольфі з Perl, я дивлюся на ваші гольфи, і це відчуття проходить досить швидко!
Дада

13

JavaScript, 863 834 785 781 байт

Збережено 29 байт завдяки ETHproductions
Збережено 53 байти завдяки Джордану

L=[]
f=(S,r="",R="",p=0,s=S.replace(RegExp(r),R),l=`((.|\n){${s.split`
`[0].length}})`,q=p+1,o=p+2,n=p+50)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...[[/ E/,"me",q],[/ E/,"de",o],[/ME/,"ce",q],[/E /,"em",q],[/E /,"ed",o],[/EM/,"ec",q],[`E${l} `,"e$1m",q],[`E${l} `,"e$1d",o],[`E${l}M`,"e$1c",q],[` ${l}E`,"m$1e",q],[` ${l}E`,"d$1e",o],[`M${l}E`,"c$1e",q],[/ m/,"m ",q],[/m /," m",q],[`m${l} `," $1m",q],[` ${l}m`,"m$1 ",q],[/ (d|c)/,"$1 ",o],[/(d|c) /," $1",o],[`(d|c)${l} `," $2$1",o],[` ${l}(d|c)`,"$3$1 ",o],[/d#/,"m ",n],[/#d/," m",n],[`#${l}d`," $1m",n],[`d${l}#`,"m$1 ",n],[/mM/," c",q],[/Mm/,"c ",q],[`M${l}m`,"c$1 ",q],[`m${l}M`," $1c",q],[/[mc]e/," E",p],[/e[mc]/,"E ",p],[`e${l}[mc]`,"E$1 ",p],[`[mc]${l}e`," $1E",p]].map(a=>f(s,...a)))
F=s=>f(s)<1e3

Вводиться як багаторядковий рядок.

Це визначає анонімну функцію, яка використовує рекурсивну функцію, fщоб визначити, чи вимкнути будильник перед тим, як отримати всіх мишей. fповертається, 1000якщо тиск вище 1000 (щоб уникнути нескінченної рекурсії), повертає тиск, якщо в виході немає більше мишей, а миша у виході, і повертає мінімальний тиск усіх можливих рухів із поточного стану в іншому випадку. Він використовує масив Lдля відстеження вже відвіданих позицій, де L[pos]==0він відвідується, і невизначений, якщо він не є. Це може не бути необхідним, але це заважає миші робити марні рухи та кидати помилки рекурсії як мінімум. (Це означає, що вам слід переосмислити, Lякщо ви тестуєте це кілька разів)

Для цього використовується інший формат у запитанні, ніж він вимагає від вас використання іншого символу для зовнішніх стін. (Все, крім # MEmecd)

Більш прочитана версія:

stateList = []
f=(s,regex="",replacement="",pressure=0,state=s.replace(regexp(regex),replacement),line=`((.|\n){${state.split("\n")[0].length}})`)=>{
    if (state in stateList || pressure > 999) return 1e3
    if (!/M/.test(state) && /E/.test(state)) return pressure

    stateList[state] = 0

    return [
        [/ E/,"me",pressure+1],
        [/ E/,"de",pressure+2],
        [/ME/,"ce",pressure+1],
        [/E /,"em",pressure+1],
        [/E /,"ed",pressure+2],
        [/EM/,"ec",pressure+1],
        [`E${line} `,"e$1m",pressure+1],
        [`E${line} `,"e$1d",pressure+2],
        [`E${line}M`,"e$1c",pressure+1],
        [` ${line}E`,"m$1e",pressure+1],
        [` ${line}E`,"d$1e",pressure+2],
        [`M${line}E`,"c$1e",pressure+1],
        [/ m/,"m ",pressure+1],
        [/m /," m",pressure+1],
        [`m${line} `," $1m",pressure+1],
        [` ${line}m`,"m$1 ",pressure+1],
        [/ ([dc])/,"$1 ",pressure+2],
        [/([dc]) /," $1",pressure+2],
        [`([dc])${line} `," $2$1",pressure+2],
        [` ${line}([dc])`,"$3$1 ",pressure+2],
        [/d#/,"m ",pressure+50],
        [/#d/," m",pressure+50],
        [`#${line}d`," $1m",pressure+50],
        [`d${line}#`,"m$1 ",pressure+50],
        [/mM/," c",pressure+1],
        [/Mm/,"c ",pressure+1],
        [`M${line}m`,"c$1 ",pressure+1],
        [`m${line}M`," $1c",pressure+1],
        [/[mc]e/," E",pressure],
        [/e[mc]/,"E ",pressure],
        [`e${line}[mc]`,"E$1 ",pressure],
        [`[mc]${line}e`," $1E",pressure]
    ].map(a=>f(state,...a)).reduce((a,b)=>a-b<0?a:b) //reduce used for support in more browsers.
}
s=>f(s)>1e3

Марні пробіли при s in L|p > 999.
Yytsi

@TuukkaX Дякую, що нагадали про це, базовий рахунок був для версії без пробілів.
DanTheMan

Дивіться , якщо ви можете зберегти байти обгортання код evalі замінити @з $1(не впевнені , якщо це буде працювати, але ви пишете $1багато)
Cyoce

Я думаю, ви могли б зберегти купу, зробивши ff=(...)=>s in L|p>999?1e3:!/M/.test(s,L[s]=0)&/E/.test(s)?p:Math.min(...
однолінійку

@Cyoce я використовую $1 18 разів, і .replace("@","$1")це 18 байт. Я не бачу способу зняти це.
DanTheMan
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.