Я хочу спостерігати, як ти вмираєш від спраги


12

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

Правила

Пустеля виглядає так: сітка WxH здебільшого порожнього місця. Пробіл, позначений Sтам, де ви починаєте, Eце місце, де ви хочете закінчитися, а квадрат, позначений цифрою N, містить N одиниць води. Квадрати, позначені .водою з утриманням нуля

.....................................
........S............................
.....................................
.........7...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

Ви починаєте з S з 5 одиницями води.

Можна носити не більше 5 одиниць води.

Кожен поворот вас

  1. перемістити один квадрат вгору, вниз, вліво або вправо,
  2. споживайте 1 одиницю води, яку ви несете,
  3. набрати або скинути деяку кількість одиниць води.

Поворот є нотірованних таким чином: (direction)(+|-)(units of water), +означає , що ви збираєте воду, -що ви впустити.

Приклад витків:

D+0        Move Down
R+0        Move Right
D+2        Move Down, pick up two units of water.
U-1        Move Up, drop one unit of water.

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

.....................................
........S............................
.........1...........................
.........5...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

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

Ви можете забрати воду лише на 5 одиниць.

Жодна плитка не може містити більше 9 одиниць, крім S, яка містить одиниці нескінченності.

Ви можете капнути стільки води, скільки зараз у вас є.

Вода на землі залишається незмінною, поки ви її знову не заберете.

Якщо ви повернетесь до S, ви можете взяти будь-яку кількість води, не виснажуючи її.

Якщо ви досягнете E, то ви виграєте . Ви все одно виграєте, якщо споживаєте останню одиницю води на Е.

Якщо після своєї черги у вас нульова вода і ви не на E, ви помрете .

Вхід і вихід

Ваша програма отримає стартову карту довільного розміру на STDINASCII мистецтві у форматі вище. Ви можете припустити, що це прямокутні, тобто всі лінії однакової довжини, що є рівно один Sі один Eквадрат, всі лінії закінчуються \n, і весь STDIN буде відповідати цьому регулярному вираженню:/^[SE1-9\.\n]+$/

Ваша програма запише наступний вихід у STDOUT:

  1. список ходів,
  2. кінцевий стан карти.

Ви можете вивести список рухів у будь-якому зручному форматі.

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

ПРИКЛАД Введення:

.....S.
.......
.......
E......
....8..

ПРИКЛАД виграшного результату:

D+0
D+0
D+0
D+0
L+5
L+0
L+0
L+0
L+0
U+0
.....S.
.....#.
.....#.
E....#.
####3#.

Нетривіальність

Коли ви публікуєте свій код, опублікуйте зразок введення карти, для якого ваш код знаходить рішення, для якого відповідають наступні умови нетривіальності:

  • S і E розташовані як мінімум на 10 ходів один від одного.
  • Будь-який квадрат, який спочатку містить N одиниць води, повинен бути оточений кордоном ширини N, у якому всі квадрати .(немає води, не S або E)

ПРИКЛАД

........2.
..........
..........
S.1..2....
..........
..........
........1.
..3.......
.........E

Якщо збільшити кількість води на будь-якій плитці, вищесказане стає дрібницею.

Вимоги

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

  1. Зрештою, ваша програма повинна вирішити будь-який вирішуваний вхід.
  2. Я хочу спостерігати, як ти вмираєш - твоя програма виводить ходи та остаточну карту маршруту до смерті для кожної невдалої спроби знайти рішення.
  3. Якщо ви зіткнулися з виграшним рішенням, надрукуйте повний висновок для цього та припиніть.
  4. Бігайте до тих пір, поки не знайдеться рішення, але не намагайтеся двічі застосовувати одне і те ж рішення - усі випадки смерті повинні бути різними маршрутами.
  5. Скористайтеся цим тестовим входом:

(для отримання потрібного ходу хоч один крок, щоб скинути схованку з води в якусь середину точки).

 S........
 .........
 .........
 ........E

Найкоротший код, який розміщується з нетривіальним демонстраційним вкладом, який він вирішує, виграє.


Це потрібно уточнити, щоб уточнити, чи потрібно програмі вміти вирішувати будь-яку вирішувану карту, чи вона повинна працювати лише для однієї карти. Я б напевно заохотив колишнього; у випадку з однією картою рішення буде простіше жорстким, ніж обчислити.

Відредаговано для наочності. Так, ви виграєте, якщо у вас більше нуля води, і так, ваша програма повинна вирішувати всі вирішувані введення.
шприц

Що заважає тобі використовувати такий алгоритм, як A *, і скидати шлях по 5 одиниць на кожну плитку, на який ти послідовно переходиш і повертаєшся до початку, якщо наступаєш на плитку без 5 води?
Блакитний

Нічого. Іди вперед.
сприф

Стратегія "нести всю воду з S" повинна працювати, хоча це буде дуже жахливо. Розглянемо S.,.,.,.,. E .... E де, і e - це справді крапки. Коми - це місце, де ви тримаєте свої ставки по дорозі, а "e" - це те, коли вам потрібно мати 5 води, щоб зробити пробіг для E. 4 кроки для переміщення 1 води до першої коми (E + 0 E-1 W + 0 Вт + 4). 16 кроків для переміщення 1 води до другої коми. 52 до третьої, 160 до четвертої, 484, щоб опустити 1 воду на e і повернутися до S. 1926 кроків, і ви на e несете 5 води, ще 5 для бігу до E, 1931 сходинок. Кожні два кроки шляху ефективно вдвічі збільшує довжину рішення.
Спар

Відповіді:


12

Perl, 299 + 1 = 300 254 + 1 = 255 байт

Це майже напевно буде побито однією з мов гольфу, коли люди побачать мій алгоритм :-)

Виконати з -n(1 байт).

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

push@a,[split//];($s,$t)=(length$`,$.)if/S/;($e,$f)=(length$`,$.)if/E/}{$_.=$s<$e?($s++,R):$s>$e?($s--,L):$t<$f?($t++,D):($t--,U);$\=reverse$";$".=y/LRUD/RLDU/r.Y.reverse.($"=~y/Y/X/r);$a[$t-1][$s]=~y/./#/;$\.=$s-$e||$t-$f?redo:$_;print map{join'',@$_}@a

Приклад (я додав розриви рядків до виводу, щоб уникнути необхідності прокрутки та показу структури):

Е .....
# .....
# .....
# .....
##### S
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUXDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUUYDDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUYDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUYDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLYRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLYRRRR
 LXR LLXRR LXR LLLYRRR LXR LLYRR LYR LLLLLUUUU

В позначеннях , які використовуються програмою, рух представлено з допомогою L, R, U, і Dдля вліво, вгору, вправо, вниз відповідно. За замовчуванням ви набираєте 1 одиницю води після кожного руху, але це можна змінити, додавши символ:

  • X: капніть 2 одиниці води, а не набирайте 1
  • Y: капніть 1 одиницю води, а не набирайте 1
  • (Тобто простір): повністю заправка на воді (вихід тільки після переїзду в S, програму також виводиться з провідним простором, який має сенс , тому що ви починаєте повністю на воді)

Як бачите, цю (досить безплідну) карту можна перетнути, не використовуючи додаткових пулів. Фактично, можна дістати будь-яку відстань за цими правилами, на будь-якій карті, без допомоги будь-якої попередньо встановленої води. Таким чином, цей алгоритм просто ігнорує будь-яку попередньо встановлену воду, тобто я не повинен витрачати байти, намагаючись впоратися з цим. Це також означає, що ви не збираєтесь бачити, як бот помирає, вибачте. Він ніколи не виходить за межі діапазону, в якому знає, що гарантовано виживе.

Причиною, що нам потрібно і те, Xі Y(і трохи додаткового коду, щоб забезпечити Xвсю стратегію, але Yв кінці випадкових випадків ), є те, що специфікація вимагає вивести остаточну версію карти. Найпростіший спосіб здійснити це - залишити карту цілком недоторканою (крім нашого шляху через спочатку порожні квадрати), тим більше, що квадрат, який розпочався 9водою, закінчився б 10(порушивши мистецтво ASCII), якби це трапилося на шляху і ми тільки використовувалиX, і тому нам потрібно знайти рішення, яке дозволяє уникнути потрапляння зайвої води на карту. Алгоритм тут «природним чином» випав би 1 додаткову одиницю води на кожен квадрат на маршруті; як такий, передостанній час, коли ми відвідуємо кожну площу, ми зменшуємо кількість води, скинутої на 1 через використання Y, а не X, так що під час останнього відвідування ми повертаємо площу до початкової кількості води, а не залишаючи його трохи мокрішим, ніж коли ми починали.

Я б рекомендував не запускати це на великій карті, оскільки він має O (2 ^ n) продуктивність (хоча бот ніколи не вмирає від спраги, правдоподібно думати, що загине від голоду, використовуючи таку стратегію.)

Читаема версія:

# implicit with -n: read a line of input into $_
push @a, [split //]; #/ split $_ into characters, store at the end of @a
($s,$t) = (length$`,$.) if /S/; # if we see S, store its coordinates
($e,$f) = (length$`,$.) if /E/  # if we see E, store its coordinates
}{ # Due to -n, loop back to start if there are more lines.

# From here onwards, $" stores the partial solution this iteration;
#                    $\ stores the partial solution last iteration;
#                    $_ stores the path from ($s,$t) to S.
# At the start of the program, $" is a space, $\ and $_ are empty.

$_ .=  # Work out the next step on the path:
  $s < $e ? ($s++,R) # if too far left, move right, record that in $_;
: $s > $e ? ($s--,L) # if too far right, move left, record that in $_;
: $t < $f ? ($t++,D) # if too far up,    move down, record that in $_;
: ($t--,U);          # in other cases we must be too far down.
$\ = reverse $";     # Store last iteration; $" is constructed backwards.
$" .=                # Extend $" by appending
  y/LRUD/RLDU/r .    # the path from ($s, $t) back to S;
  Y .                # a literal 'Y';
  reverse .          # $/ backwards (i.e. the path from S to ($s, $t);
  ($"=~y/Y/X/r);     # a copy of $" with all 'Y' changed to 'X'.
$a[$t-1][$s] =~      # At the current path coordinate,
  y/./#/;            # replace any '.' with '#'.
$\ .=                # Start appending to $\;
  $s-$e||$t-$f?redo  # if we're not at E, abort that and jump back to {{,
: $_;                # otherwise append $_ (the path from S to E).
print map            # For each element of some array
  {join'',@$_}       # output its concatenated elements
  @a                 # specifying that array as @a.
# Implicitly: print $\ (which specifies the sort of newline print uses).

Чи затоплення, що заповнює світ по спіралі, буде меншим кодом, ніж ваш блок умовних умов для напряму на пошук виходу?
Спар

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

Хороша точка, знову краї. Я думаю, це можна зробити на нескінченній карті.
Спарр
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.