Я хочу смотреть, как ты умираешь от жажды

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.

Если после вашего хода у вас будет ноль воды и вы не на E, вы умрете .

Вход и выход

Ваша программа получит стартовую карту произвольного размера STDINв формате ASCII в формате выше. Вы можете предположить, что это прямоугольник, то есть все строки одинаковой длины, что существует ровно один 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

Самый короткий код, который размещен с нетривиальным демонстрационным вводом, который он решает, выигрывает.

spraff
источник
Это необходимо уточнить, чтобы указать, должна ли программа иметь возможность решать любую разрешимую карту или она должна работать только для одной карты. Я определенно поощряю первое; в случае одной карты будет проще жестко закодировать решение, чем рассчитать его.
Отредактировано для наглядности. Да, вы выиграете, если у вас больше нуля воды, и да, ваша программа должна решить все разрешимые входы.
spraff
Что мешает вам использовать алгоритм, такой как A *, и сбрасывать путь по 5 единиц на каждой клетке, к которой вы последовательно переходите, и возвращаться к началу, если вы наступаете на плитку без 5 воды?
Blue
Ничего. Преуспевать.
spraff
Стратегия «нести всю воду из S» должна работать, хотя она будет ужасно утомительной. Рассмотрим S.,.,.,., E .... E где E и E действительно точки. Запятые - это то место, где вы храните свои тайники по пути, а «е» - это то, где вам нужно 5 воды, чтобы пробежать по E. 4 шага, чтобы переместить 1 воду к первой запятой (E + 0 E-1 W + 0 Вт + 4). 16 шагов, чтобы переместить 1 воду до второй запятой. 52 к третьему, 160 к четвертому, 484, чтобы сбросить 1 воду к э и вернуться к шагу S. 1926, и у вас есть 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

Пример (я добавил разрывы строк в вывод, чтобы избежать необходимости прокрутки и показать структуру):

E .....
# .....
# .....
# .....
##### 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
Я не думаю, что это будет (хотя, возможно, есть какой-то способ, которого я просто не вижу; это, безусловно, идея, заслуживающая рассмотрения). Вам все еще приходится иметь дело с четырьмя направлениями, и теперь вам также приходится иметь дело с краем карты, что не является проблемой в этой версии алгоритма.
Хороший вопрос, ребра. Я думаю, что это можно сделать на бесконечной карте.
Спарр