A * может работать с любым количеством измерений; это алгоритм обхода графа, и независимо от того, сколько измерений имеет ваше проблемное пространство, соединение одной позиции с другой все равно создает граф.
Однако у вас есть две проблемы с генерацией новых узлов.
Вы включили (0, 0, 0)
в список, поэтому без изменений . Вы продолжаете помещать текущую позицию обратно в очередь для рассмотрения. Это просто занятая работа, потому что текущая позиция уже находится в закрытом списке.
Вы никогда не вычитаете из любых ваших координат, вы только добавляете . Таким образом, ваши значения x
, y
и z
могут только когда-либо go up . Если для достижения вашей цели требуется путь вокруг препятствия, то у вас есть проблема, потому что все, что может сделать ваша версия, это двигаться в одном направлении вдоль любой заданной оси.
В трехмерной матрице 3 на 3 на 3 с текущим положением посередине 3 раза 3 раза 3 минус 1 == 26 позиций, которые можно достичь за один шаг. Ваш код достигает только 7 из них, плюс один, который остается на месте.
Если вы извлекаете свои кортежи из списка for new_position in [...]
в отдельную переменную и добавляете несколько новых строк, и немного их переставляете, чтобы сгруппировать их на сколько 1
s у вас есть в кортеже, вы получите следующее определение. Я переименовал это в deltas
, потому что это не новая позиция, это изменение относительно старой позиции или delta . Я перестроил ваши кортежи, чтобы упростить их логическую группировку:
deltas = [
(0, 0, 0), # no change,
(0, 0, 1), (0, 1, 0), (1, 0, 0), # add to a single axis
(0, 1, 1), (1, 0, 1), (1, 1, 0), # add to two axes
(1, 1, 1) # move in all 3 directions at once.
]
for delta in deltas:
# ...
Вы хотите отбросить первый ((0, 0, 0)
), и вам нужно добавить -1
версии других вариантов. , Вам нужно 26 разных кортежей, но написание их вручную становится громоздким, очень быстрым. Вы можете использовать itertools.product()
для их генерации, вместо этого:
from itertools import product
# combinations of -1, 0 and 1, filtering out the (0, 0, 0) case:
deltas = [d for d in product((-1, 0, 1), repeat=3) if any(d)]
Теперь ваш комментарий рядом с l oop говорит:
# Adjacent squares removed to ignor diagonal movement
Это не совсем понятно, что вы подразумеваете под этим, потому что ваш первоначальный список дельт включает диагонали ((0, 1, 1)
перемещается как в направлениях y
и z
, так и у вас есть кортежи, сочетающие движение в x
плюс y
и x
плюс z
осей) и даже одну, которая перемещается по диагонали, увеличивая все 3 оси. Если вы только хотели переместиться на вверх , вниз , влево , вправо , вперед и назад , вы хотите ограничить движение одной осью за раз, и deltas
должно быть:
# movement without diagonals
deltas = [
(1, 0, 0), (-1, 0, 0), # only x
(0, 1, 0), (0, -1, 0), # only y
(0, 0, 1), (0, 0, -1), # only z
]
Лично я бы перенес весь бизнес генерации новых позиций на отдельную метод для Node
класса :
def possible_moves(self, map):
x, y, z = self.x, self.y, self.z
for dx, dy, dz in DELTAS:
newx, newy, newz = x + dx, y + dy, z + dz
try:
if maze[newx][newy][newz] != 0:
yield Node(self, (newx, newy, newz))
except IndexError:
# not inside the maze anymore, ignore
pass
Этот метод предполагает, что существует глобальная переменная DELTAS
, которая определяет возможные перемещения, и является генератором; каждый раз, когда достигается yield
, новый экземпляр Node()
возвращается тому, кто использует его в качестве итератора (например, for
l oop).
Затем просто используйте этот метод вместо children = []
список, который вы заполнили for new_position in ...:
l oop, поэтому непосредственно в той части, которая использует оригинальный список children
:
# Loop through children
for child in current_node.possible_moves(map):
# Child is on the closed list
for closed_child in closed_list:
if child == closed_child:
continue
# etc.