ТФВР. Лабиринт
Давайте подумаем о задаче лабиринта в игре The Farmer Was Replaced.
Прежде всего, формализуем задачу. У нас есть
𝑛×𝑛-лабиринт, где n = get_world_size(). Наша текущая позиция — (get_pos_x(), get_pos_y()). Дрон двигается командами move(East|North|West|South). Клад можно обнаружить по условию get_entity_type() == Entities.Treasure. Информацию о проходимости соседних клеток получаем через can_move(East|North|West|South).
Честно говоря, я понятия не имею, почему так популярен подход «правая рука» или «левая рука». Я начал с DFS (поиск в глубину) и хочу объяснить этот подход в посте.
Кажется, довольно популярно начинать с этих «ручных» подходов. Но они терпят неудачу, когда вы пытаетесь заработать больше денег на охоте за сокровищами. По правилам, приз удваивается, если вместо сбора вы используете на сокровище странное вещество. В этом случае вы повторно используете лабиринт, и сокровище перескакивает в какое-то другое место. Вы можете сделать это 30 раз (и должны, если хотите иметь эффективную ферму). Также, когда сокровище прыгает, некоторые стены лабиринта исчезают. Это исчезновение имеет два последствия. Во-первых, «ручные» подходы больше не работают. С другой стороны, лабиринт упрощается, и, если вы используете соответствующий алгоритм, ваш дрон находит сокровище все быстрее и быстрее.
В этой теме я хочу показать простейший возможный код DFS, который не опирается на «строгую древовидную структуру» лабиринта.
Давайте перейдем к коду. DIRECTIONS — массив направлений для move(). visited — множество уже посещенных клеток. OPPOSITE — словарь противоположных направлений.
def go_best(d1, d2, l1, l2):
d, l = d1, l1
if l2 < l1:
d, l = d2, l2
for _ in range(l):
move(d)
def nav(x2, y2):
n = get_world_size()
x1, y1 = get_pos_x(), get_pos_y()
go_best(East, West, (x2 - x1)%n, (x1 - x2)%n)
go_best(North, South, (y2 - y1)%n, (y1 - y2)%n)
def apply_proper_substance():
substance = get_world_size() * 2**(num_unlocked(Unlocks.Mazes) - 1)
use_item(Items.Weird_Substance, substance)
set_world_size(8)
nav(3, 3)
plant(Entities.Bush)
apply_proper_substance()
DIRECTIONS=[East, North, South, West]
OPPOSITE={East:West, West:East, North:South, South:North}
def dfs(visited):
if (get_pos_x(), get_pos_y()) in visited:
return
visited.add((get_pos_x(), get_pos_y()))
if get_entity_type() == Entities.Treasure:
harvest()
for dir_to_move in DIRECTIONS:
if can_move(dir_to_move):
move(dir_to_move)
dfs(visited)
move(OPPOSITE[dir_to_move])
visited = set()
dfs(visited)
while True:
pass
Код максимально прост и поэтому содержит несколько очевидных проблем:
проблема: в этом алгоритме дрон все время «трясется» вперед-назад
причина: он проверяет свою текущую позицию в посещенной, поэтому ему действительно нужно перейти в ячейку, чтобы проверить ее
как исправить: написать функцию «проекции», которая вычисляет позицию дрона после шага и проверяет ее в visited
проблема: этот код немедленно собирает сокровище
причина: я хотел, чтобы этот код был максимально простым
как исправить: добавить логику, чтобы прервать рекурсию, когда дрон находится над сокровищем, и создать внешний цикл для обновления лабиринта
