Pathfinder singletone-class was added. Fixed leaky abstraction Map - move - Object using Pathfinder.
This commit is contained in:
48
eb_engine.py
48
eb_engine.py
@@ -4,6 +4,7 @@ from common import pygame, pygame_gui
|
||||
import eb_objects
|
||||
import eb_terrain_objects
|
||||
import eb_creature_objects
|
||||
from pathfinder import pathfinder
|
||||
|
||||
#from pympler import muppy, summary
|
||||
import gc, psutil, os
|
||||
@@ -38,8 +39,6 @@ class Map:
|
||||
sprites: dict
|
||||
sprites_refresh: int = 60
|
||||
cells: dict = field(default_factory = dict)
|
||||
walkable_matrix: list = field(default_factory = list)
|
||||
rocks_matrix: list = field(default_factory = list)
|
||||
color: str = "gray57"
|
||||
target_color: str = "gold"
|
||||
cell_size: int = 150
|
||||
@@ -70,13 +69,6 @@ class Map:
|
||||
|
||||
self.cells[line].append(final_cell)
|
||||
|
||||
self.compute_walkable_rocks()
|
||||
for j in range(len(self.cells)):
|
||||
for cell in self.cells[j]:
|
||||
if cell.creature_obj:
|
||||
cell.creature_obj.walkable_matrix = self.walkable_matrix
|
||||
cell.creature_obj.rocks_matrix = self.rocks_matrix
|
||||
|
||||
def draw_obj(self, obj, draw_data):
|
||||
if draw_data["spr_up"] == 0:
|
||||
if obj.sprite_state == len(draw_data["sprites"][obj.sprite_name]) - 1:
|
||||
@@ -134,41 +126,15 @@ class Map:
|
||||
return (row, col)
|
||||
|
||||
def update_map(self, time_delta):
|
||||
self.compute_walkable_rocks()
|
||||
pathfinder.set_map(self.cells)
|
||||
for j in range(len(self.cells)):
|
||||
for cell in self.cells[j]:
|
||||
if cell.creature_obj:
|
||||
cell.creature_obj.walkable_matrix = self.walkable_matrix
|
||||
cell.creature_obj.rocks_matrix = self.rocks_matrix
|
||||
if cell.creature_obj.final_goal is not None:
|
||||
cell.creature_obj.calc_step(time_delta, self.cell_size, self)
|
||||
continue
|
||||
cell.creature_obj.update(time_delta)
|
||||
|
||||
def compute_walkable_rocks(self):
|
||||
"""Вычисляет матрицы walkable и rocks_only БЕЗ учета стартовой позиции"""
|
||||
rows = len(self.cells)
|
||||
if rows == 0:
|
||||
return None, None
|
||||
|
||||
cols = len(self.cells[0])
|
||||
|
||||
# ★ ИНИЦИАЛИЗАЦИЯ ★
|
||||
self.walkable_matrix = [[True] * cols for _ in range(rows)]
|
||||
self.rocks_matrix = [[False] * cols for _ in range(rows)]
|
||||
|
||||
# ★ ПОЛНЫЙ ПРОХОД ПО КАРТЕ ★
|
||||
for r in range(rows):
|
||||
for c in range(cols):
|
||||
cell = self.cells[r][c]
|
||||
# Запрещаем ВСЕ существа (включая стартовое!)
|
||||
if cell.creature_obj or \
|
||||
(cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small"):
|
||||
self.walkable_matrix[r][c] = False
|
||||
|
||||
# Отмечаем маленькие камни
|
||||
if cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small":
|
||||
self.rocks_matrix[r][c] = True
|
||||
|
||||
def OLDSTABLE_draw_map(self, screen, current_frame, grid=True):
|
||||
terrain_list = []
|
||||
@@ -421,12 +387,10 @@ class Engine:
|
||||
|
||||
elf = eb_objects.Creature(id=f"elf_{random.randint(1000,9999)}",
|
||||
name="Elf", sprite_name="elf_watching",
|
||||
grid_pos = pos,
|
||||
walkable_matrix = easy_map.walkable_matrix,
|
||||
rocks_matrix = easy_map.rocks_matrix)
|
||||
grid_pos = pos)
|
||||
|
||||
r_move_short = eb_objects.Action(sprite_name="elf_watching", func=partial(elf.patrol, easy_map.cells, 3), duration=0.01)
|
||||
r_move_long = eb_objects.Action(sprite_name="elf_watching", func=partial(elf.move_rand, easy_map.cells, 0, 99), duration=0.01)
|
||||
r_move_short = eb_objects.Action(sprite_name="elf_watching", func=partial(elf.patrol, 3), duration=0.01)
|
||||
r_move_long = eb_objects.Action(sprite_name="elf_watching", func=partial(elf.move_rand, 0, 99), duration=0.01)
|
||||
elf.tasks.append([r_move_short]*5 + [r_move_long])
|
||||
easy_map.cells[row][col].creature_obj = elf
|
||||
|
||||
@@ -558,7 +522,7 @@ class Engine:
|
||||
cell_coords = easy_map.get_cell_at_mouse(mouse_pos)
|
||||
if cell_coords:
|
||||
#print(f"Движение: {active_cell} -> {cell_coords}")
|
||||
easy_map.cells[active_cell[0]][active_cell[1]].creature_obj.move(easy_map.cells, cell_coords)
|
||||
easy_map.cells[active_cell[0]][active_cell[1]].creature_obj.move(cell_coords)
|
||||
|
||||
if keys[pygame.K_ESCAPE]:
|
||||
running = False
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from common import deepcopy, dataclass, field, random
|
||||
from pathfinder import pathfinder
|
||||
|
||||
@dataclass
|
||||
class Action:
|
||||
@@ -44,8 +45,8 @@ class Creature(Object):
|
||||
render_offset: tuple = (0.0, 0.0)
|
||||
start_pos: tuple = None # (row, col) начальная позиция сегмента пути
|
||||
waypoints: list = field(default_factory = list)
|
||||
walkable_matrix: list = field(default_factory = list)
|
||||
rocks_matrix: list = field(default_factory = list)
|
||||
#walkable_matrix: list = field(default_factory = list)
|
||||
#rocks_matrix: list = field(default_factory = list)
|
||||
|
||||
inventory: dict = field(default_factory = dict)
|
||||
quick_actions: list = field(default_factory = list)
|
||||
@@ -62,8 +63,7 @@ class Creature(Object):
|
||||
interrupt_action_status: str = "completed"
|
||||
|
||||
def replan(self, cells, pos):
|
||||
from common import find_way
|
||||
path = find_way(cells, pos, self.final_goal, self.walkable_matrix, self.rocks_matrix)
|
||||
path = pathfinder.find_way(pos, self.final_goal)
|
||||
if path and len(path) > 1:
|
||||
self.waypoints = path[1:]
|
||||
self.current_target = self.waypoints[0]
|
||||
@@ -131,10 +131,9 @@ class Creature(Object):
|
||||
offset_y = (target_row - start_row) * cell_size * self.move_progress
|
||||
self.render_offset = (offset_x, offset_y)
|
||||
|
||||
def move(self, cells, goal):
|
||||
from common import find_way
|
||||
def move(self, goal):
|
||||
self.final_goal = goal
|
||||
path = find_way(cells, self.grid_pos, self.final_goal, self.walkable_matrix, self.rocks_matrix)
|
||||
path = pathfinder.find_way(self.grid_pos, self.final_goal)
|
||||
if path and len(path) > 1:
|
||||
self.waypoints = path[1:]
|
||||
self.current_target = self.waypoints[0]
|
||||
@@ -205,12 +204,12 @@ class Creature(Object):
|
||||
# self.action_counter = 0
|
||||
# if self.task_counter >= len(self.tasks):
|
||||
# self.task_counter = 0
|
||||
#
|
||||
#
|
||||
# self.action = self.tasks[self.task_counter][self.action_counter]
|
||||
# self.action_counter += 1
|
||||
# self.action_time = 0.0
|
||||
|
||||
def patrol(self, cells, area):
|
||||
def patrol(self, area):
|
||||
goal = (random.randint(self.grid_pos[0] - area,
|
||||
self.grid_pos[0] + area),
|
||||
random.randint(self.grid_pos[1] - area,
|
||||
@@ -220,13 +219,13 @@ class Creature(Object):
|
||||
self.grid_pos[0] + area),
|
||||
random.randint(self.grid_pos[1] - area,
|
||||
self.grid_pos[1] + area))
|
||||
self.move(cells, goal)
|
||||
self.move(goal)
|
||||
|
||||
def move_rand(self, cells, area_start, area_end):
|
||||
def move_rand(self, area_start, area_end):
|
||||
goal = (random.randint(area_start, area_end), random.randint(area_start, area_end))
|
||||
while goal == self.grid_pos:
|
||||
goal = (random.randint(area_start, area_end), random.randint(area_start, area_end))
|
||||
self.move(cells, goal)
|
||||
self.move(goal)
|
||||
|
||||
@dataclass
|
||||
class Item(Object):
|
||||
|
||||
2
main.py
2
main.py
@@ -12,7 +12,6 @@ if __name__ == "__main__":
|
||||
# прокрутка баг консоль и карта
|
||||
# устроить краш тест поиску пути, запустив много объектов на маленьком поле, успел заметить баги
|
||||
# добавить функцию движения за каким-то объектом
|
||||
# сделать, чтобы в случае отменненого движения не телепортировался назад, а плавно
|
||||
# приступаем к логике
|
||||
# сделать по аналогии с текущей клеткой текущий объект
|
||||
|
||||
@@ -25,3 +24,4 @@ if __name__ == "__main__":
|
||||
# рисовать группой спрайтов
|
||||
# нужен ли теперь start_pos? grid_pos?
|
||||
# class Task с проверками выполнения экшонов
|
||||
# Нужен ли cells в виде словаря списков? может иначе сделать?
|
||||
587
pathfinder.py
Normal file
587
pathfinder.py
Normal file
@@ -0,0 +1,587 @@
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
class Pathfinder:
|
||||
_instance = None
|
||||
cells = None
|
||||
walkable_matrix = field(default_factory = list)
|
||||
rocks_matrix = field(default_factory = list)
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def set_map(self, cells):
|
||||
self.cells = cells
|
||||
self.update_matrices()
|
||||
|
||||
def update_matrices(self):
|
||||
rows = len(self.cells)
|
||||
if rows == 0:
|
||||
return None, None
|
||||
|
||||
cols = len(self.cells[0])
|
||||
|
||||
self.walkable_matrix = [[True] * cols for _ in range(rows)]
|
||||
self.rocks_matrix = [[False] * cols for _ in range(rows)]
|
||||
|
||||
for r in range(rows):
|
||||
for c in range(cols):
|
||||
cell = self.cells[r][c]
|
||||
if cell.creature_obj or \
|
||||
(cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small"):
|
||||
self.walkable_matrix[r][c] = False
|
||||
|
||||
if cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small":
|
||||
self.rocks_matrix[r][c] = True
|
||||
|
||||
def find_way(self, start, goal):
|
||||
result = self.bfs_quick(start, goal)
|
||||
return result
|
||||
|
||||
def bfs_quick(self, start, goal):
|
||||
"""★СУПЕРБЫСТРЫЙ BFS: массивы вместо set/deque★"""
|
||||
rows = len(self.cells)
|
||||
if rows == 0:
|
||||
print("Путь не найден: пустая карта")
|
||||
return None
|
||||
|
||||
cols = len(self.cells[0])
|
||||
s_row, s_col = start
|
||||
g_row, g_col = goal
|
||||
|
||||
if (s_row >= rows or s_col >= cols or
|
||||
g_row >= rows or g_col >= cols):
|
||||
#print(f"Путь не найден: выход за границы {start} -> {goal}")
|
||||
return None
|
||||
|
||||
## ★ МАТРИЦЫ вместо set (10x быстрее хэширования) ★
|
||||
#walkable = [[True] * cols for _ in range(rows)]
|
||||
#rocks_only = [[False] * cols for _ in range(rows)]
|
||||
#start_creature = cells[s_row][s_col].creature_obj
|
||||
#
|
||||
#for r in range(rows):
|
||||
# for c in range(cols):
|
||||
# cell = cells[r][c]
|
||||
# if (cell.creature_obj and cell.creature_obj != start_creature) or \
|
||||
# (cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small"):
|
||||
# walkable[r][c] = False
|
||||
# if cell.terrain_obj and cell.terrain_obj.sprite_name == "rock_small":
|
||||
# rocks_only[r][c] = True
|
||||
|
||||
# ★ ВЫЧИСЛЯЕМЫЕ МАССИВЫ ★
|
||||
visited = [[False] * cols for _ in range(rows)]
|
||||
parent = [[None] * cols for _ in range(rows)]
|
||||
|
||||
# ★ БЫСТРАЯ ОЧЕРЕДЬ: индекс вместо deque ★
|
||||
queue_size = 0
|
||||
queue = [[0, 0] for _ in range(rows * cols)] # Предварительно выделяем
|
||||
queue[0] = [s_row, s_col]
|
||||
queue_size = 1
|
||||
front = 0
|
||||
|
||||
visited[s_row][s_col] = True
|
||||
|
||||
directions = [(-1,0), (1,0), (0,-1), (0,1),
|
||||
(-1,-1), (-1,1), (1,-1), (1,1)]
|
||||
|
||||
while front < queue_size:
|
||||
# ★ БЫСТРОЕ извлечение ★
|
||||
r, c = queue[front]
|
||||
front += 1
|
||||
|
||||
if r == g_row and c == g_col:
|
||||
path = []
|
||||
cr, cc = g_row, g_col
|
||||
while True:
|
||||
path.append((cr, cc))
|
||||
if parent[cr][cc] is None:
|
||||
break
|
||||
pr, pc = parent[cr][cc]
|
||||
cr, cc = pr, pc
|
||||
return path[::-1]
|
||||
|
||||
for dr, dc in directions:
|
||||
nr, nc = r + dr, c + dc
|
||||
|
||||
if (0 <= nr < rows and 0 <= nc < cols and
|
||||
self.walkable_matrix[nr][nc] and not visited[nr][nc]):
|
||||
|
||||
# ★ ПРОВЕРКА ДИАГОНАЛИ ★
|
||||
if dr * dc == 0 or self.can_move_diagonal(r, c, nr, nc, self.rocks_matrix, rows, cols):
|
||||
visited[nr][nc] = True
|
||||
parent[nr][nc] = (r, c)
|
||||
|
||||
# ★ БЫСТРОЕ добавление в очередь ★
|
||||
queue[queue_size] = [nr, nc]
|
||||
queue_size += 1
|
||||
|
||||
#print(f"Путь не найден: {start} -> {goal}")
|
||||
return None
|
||||
|
||||
def can_move_diagonal(self, r, c, nr, nc, rocks_only, rows, cols):
|
||||
"""Запрет среза только около rock"""
|
||||
dr = nr - r
|
||||
dc = nc - c
|
||||
r1, c1 = r + dr, c
|
||||
r2, c2 = r, c + dc
|
||||
|
||||
check1_ok = (0 <= r1 < rows and 0 <= c1 < cols and not rocks_only[r1][c1])
|
||||
check2_ok = (0 <= r2 < rows and 0 <= c2 < cols and not rocks_only[r2][c2])
|
||||
|
||||
return check1_ok and check2_ok
|
||||
|
||||
pathfinder = Pathfinder()
|
||||
|
||||
'''
|
||||
#def find_way(cells, start, goal):
|
||||
# """Находит путь от start=(row, col) к goal=(row, col). row=y (строка), col=x (столбец)"""
|
||||
# rows = len(cells)
|
||||
# if rows == 0:
|
||||
# return None
|
||||
# cols = len(cells[0])
|
||||
# def is_passable(cell):
|
||||
# # Проходимо, если НЕ Rock в terrain И creature_obj отсутствует
|
||||
# return (cell.terrain_obj is None or not isinstance(cell.terrain_obj, Rock))
|
||||
# # Проверка границ и проходимости старт/гол
|
||||
# s_row, s_col = start
|
||||
# g_row, g_col = goal
|
||||
# if not (0 <= s_row < rows and 0 <= s_col < cols and 0 <= g_row < rows and 0 <= g_col < cols):
|
||||
# return None
|
||||
# start_cell = cells[s_row][s_col]
|
||||
# goal_cell = cells[g_row][g_col]
|
||||
# if not is_passable(start_cell) or not is_passable(goal_cell):
|
||||
# print(f"Старт/гол непроходимы: start={is_passable(start_cell)}, goal={is_passable(goal_cell)}")
|
||||
# return None
|
||||
# # A* поиск (используем прямые row,col вместо ID для простоты)
|
||||
# directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] # вверх, вниз, лево, право
|
||||
# open_set = []
|
||||
# # f_score = g + h (h=манхэттен)
|
||||
# h = abs(s_row - g_row) + abs(s_col - g_col)
|
||||
# heappush(open_set, (h, 0, s_row, s_col)) # f, g, row, col
|
||||
# came_from = {}
|
||||
# g_score = defaultdict(lambda: float('inf'))
|
||||
# g_score[(s_row, s_col)] = 0
|
||||
# while open_set:
|
||||
# _, _, row, col = heappop(open_set)
|
||||
# if (row, col) == (g_row, g_col):
|
||||
# # Восстанавливаем путь
|
||||
# path = []
|
||||
# current = (row, col)
|
||||
# while current in came_from:
|
||||
# path.append(current)
|
||||
# current = came_from[current]
|
||||
# path.append(start)
|
||||
# return path[::-1]
|
||||
# for dr, dc in directions:
|
||||
# nr, nc = row + dr, col + dc
|
||||
# if 0 <= nr < rows and 0 <= nc < cols:
|
||||
# if is_passable(cells[nr][nc]):
|
||||
# tentative_g = g_score[(row, col)] + 1
|
||||
# pos = (nr, nc)
|
||||
# if tentative_g < g_score[pos]:
|
||||
# came_from[pos] = (row, col)
|
||||
# g_score[pos] = tentative_g
|
||||
# f = tentative_g + abs(nr - g_row) + abs(nc - g_col)
|
||||
# heappush(open_set, (f, tentative_g, nr, nc))
|
||||
# print("Путь не найден (нет связи)")
|
||||
# return None
|
||||
|
||||
#def find_way(cells, start, goal):
|
||||
# """Находит путь от start=(row, col) к goal=(row, col) с диагональным движением"""
|
||||
# rows = len(cells)
|
||||
# if rows == 0:
|
||||
# return None
|
||||
# cols = len(cells[0])
|
||||
#
|
||||
# def is_passable(cell):
|
||||
# return (cell.terrain_obj is None or not isinstance(cell.terrain_obj, Rock))
|
||||
#
|
||||
# s_row, s_col = start
|
||||
# g_row, g_col = goal
|
||||
# if not (0 <= s_row < rows and 0 <= s_col < cols and 0 <= g_row < rows and 0 <= g_col < cols):
|
||||
# return None
|
||||
#
|
||||
# start_cell = cells[s_row][s_col]
|
||||
# goal_cell = cells[g_row][g_col]
|
||||
# if not is_passable(start_cell) or not is_passable(goal_cell):
|
||||
# print(f"Старт/гол непроходимы: start={is_passable(start_cell)}, goal={is_passable(goal_cell)}")
|
||||
# return None
|
||||
#
|
||||
# # ★ 8 НАПРАВЛЕНИЙ: 4 основных + 4 диагональных ★
|
||||
# directions = [
|
||||
# (-1, 0), (1, 0), (0, -1), (0, 1), # вверх, вниз, лево, право (стоимость 1.0)
|
||||
# (-1, -1), (-1, 1), (1, -1), (1, 1) # диагонали (стоимость √2 ≈ 1.414)
|
||||
# ]
|
||||
#
|
||||
# open_set = []
|
||||
# h = abs(s_row - g_row) + abs(s_col - g_col) # эвристика манхэттен
|
||||
# heappush(open_set, (h, 0, s_row, s_col)) # f, g, row, col
|
||||
#
|
||||
# came_from = {}
|
||||
# g_score = defaultdict(lambda: float('inf'))
|
||||
# g_score[(s_row, s_col)] = 0
|
||||
#
|
||||
# while open_set:
|
||||
# _, _, row, col = heappop(open_set)
|
||||
# if (row, col) == (g_row, g_col):
|
||||
# # Восстанавливаем путь
|
||||
# path = []
|
||||
# current = (row, col)
|
||||
# while current in came_from:
|
||||
# path.append(current)
|
||||
# current = came_from[current]
|
||||
# path.append(start)
|
||||
# return path[::-1]
|
||||
#
|
||||
# for dr, dc in directions:
|
||||
# nr, nc = row + dr, col + dc
|
||||
# if 0 <= nr < rows and 0 <= nc < cols:
|
||||
# if is_passable(cells[nr][nc]):
|
||||
# # ★ РАЗНЫЕ СТОИМОСТИ ДЛЯ ДИАГОНАЛЕЙ ★
|
||||
# if abs(dr) + abs(dc) == 2: # диагональ
|
||||
# move_cost = 1.414 # √2
|
||||
# else: # ортогональ
|
||||
# move_cost = 1.0
|
||||
#
|
||||
# tentative_g = g_score[(row, col)] + move_cost
|
||||
# pos = (nr, nc)
|
||||
#
|
||||
# if tentative_g < g_score[pos]:
|
||||
# came_from[pos] = (row, col)
|
||||
# g_score[pos] = tentative_g
|
||||
#
|
||||
# # ★ ЧЕБЫШЕВ для диагоналей лучше манхэттена ★
|
||||
# h = max(abs(nr - g_row), abs(nc - g_col))
|
||||
# f = tentative_g + h
|
||||
# heappush(open_set, (f, tentative_g, nr, nc))
|
||||
#
|
||||
# print("Путь не найден (нет связи)")
|
||||
# return None
|
||||
|
||||
#def find_way(cells, start, goal):
|
||||
# """Находит путь с диагональным движением, но БЕЗ прохода через углы"""
|
||||
# rows = len(cells)
|
||||
# if rows == 0:
|
||||
# return None
|
||||
# cols = len(cells[0])
|
||||
#
|
||||
# def is_passable(cell):
|
||||
# return (cell.terrain_obj is None or not isinstance(cell.terrain_obj, Rock))
|
||||
#
|
||||
# s_row, s_col = start
|
||||
# g_row, g_col = goal
|
||||
# if not (0 <= s_row < rows and 0 <= s_col < cols and 0 <= g_row < rows and 0 <= g_col < cols):
|
||||
# return None
|
||||
#
|
||||
# start_cell = cells[s_row][s_col]
|
||||
# goal_cell = cells[g_row][g_col]
|
||||
# if not is_passable(start_cell) or not is_passable(goal_cell):
|
||||
# print(f"Старт/гол непроходимы")
|
||||
# return None
|
||||
#
|
||||
# directions = [
|
||||
# (-1, 0), (1, 0), (0, -1), (0, 1), # ортогональ (1.0)
|
||||
# (-1, -1), (-1, 1), (1, -1), (1, 1) # диагональ (1.414)
|
||||
# ]
|
||||
#
|
||||
# def can_move_diagonal(current_r, current_c, dr, dc):
|
||||
# """Проверяет, можно ли двигаться по диагонали (НЕ через угол)"""
|
||||
# nr, nc = current_r + dr, current_c + dc
|
||||
#
|
||||
# # Ортогональные соседи ДОЛЖНЫ быть проходимыми для диагонального хода
|
||||
# ortho1_r, ortho1_c = current_r + dr, current_c # вертикальный сосед
|
||||
# ortho2_r, ortho2_c = current_r, current_c + dc # горизонтальный сосед
|
||||
#
|
||||
# # Проверяем границы для ортососедей
|
||||
# if not (0 <= ortho1_r < rows and 0 <= ortho1_c < cols):
|
||||
# return False
|
||||
# if not (0 <= ortho2_r < rows and 0 <= ortho2_c < cols):
|
||||
# return False
|
||||
#
|
||||
# return (is_passable(cells[ortho1_r][ortho1_c]) and
|
||||
# is_passable(cells[ortho2_r][ortho2_c]))
|
||||
#
|
||||
# open_set = []
|
||||
# h = max(abs(s_row - g_row), abs(s_col - g_col))
|
||||
# heappush(open_set, (h, 0, s_row, s_col))
|
||||
#
|
||||
# came_from = {}
|
||||
# g_score = defaultdict(lambda: float('inf'))
|
||||
# g_score[(s_row, s_col)] = 0
|
||||
#
|
||||
# while open_set:
|
||||
# _, _, row, col = heappop(open_set)
|
||||
# if (row, col) == (g_row, g_col):
|
||||
# path = []
|
||||
# current = (row, col)
|
||||
# while current in came_from:
|
||||
# path.append(current)
|
||||
# current = came_from[current]
|
||||
# path.append(start)
|
||||
# return path[::-1]
|
||||
#
|
||||
# for dr, dc in directions:
|
||||
# nr, nc = row + dr, col + dc
|
||||
# if 0 <= nr < rows and 0 <= nc < cols:
|
||||
# target_cell = cells[nr][nc]
|
||||
#
|
||||
# if (nr, nc) != start and target_cell.creature_obj is not None:
|
||||
# continue
|
||||
# if not is_passable(target_cell):
|
||||
# continue
|
||||
#
|
||||
# if abs(dr) + abs(dc) == 2:
|
||||
# if not can_move_diagonal(row, col, dr, dc):
|
||||
# continue
|
||||
#
|
||||
# move_cost = 1.414 if abs(dr) + abs(dc) == 2 else 1.0
|
||||
# tentative_g = g_score[(row, col)] + move_cost
|
||||
# pos = (nr, nc)
|
||||
#
|
||||
# if tentative_g < g_score[pos]:
|
||||
# came_from[pos] = (row, col)
|
||||
# g_score[pos] = tentative_g
|
||||
# h = max(abs(nr - g_row), abs(nc - g_col))
|
||||
# f = tentative_g + h
|
||||
# heappush(open_set, (f, tentative_g, nr, nc))
|
||||
#
|
||||
# print("Путь не найден")
|
||||
# return None
|
||||
|
||||
|
||||
#def find_way(cells, start, goal):
|
||||
# """A* pathfinding — только новая библиотека"""
|
||||
# rows = len(cells)
|
||||
# if rows == 0:
|
||||
# print("Путь не найден: пустая карта")
|
||||
# return None
|
||||
#
|
||||
# cols = len(cells[0])
|
||||
#
|
||||
# # ★ Проверка границ ★
|
||||
# s_row, s_col = start
|
||||
# g_row, g_col = goal
|
||||
# if (s_row >= rows or s_col >= cols or
|
||||
# g_row >= rows or g_col >= cols):
|
||||
# print(f"Путь не найден: выход за границы карты {start} -> {goal}")
|
||||
# return None
|
||||
#
|
||||
# # ★ НАХОДИМ существо в start ★
|
||||
# start_creature = cells[s_row][s_col].creature_obj
|
||||
#
|
||||
# # Матрица препятствий
|
||||
# matrix = [[1 for _ in cells[row]] for row in range(rows)]
|
||||
#
|
||||
# for r in range(rows):
|
||||
# for c in range(cols):
|
||||
# cell_creature = cells[r][c].creature_obj
|
||||
# if cell_creature and cell_creature != start_creature:
|
||||
# matrix[r][c] = 0
|
||||
#
|
||||
# from pathfinding.core.grid import Grid
|
||||
# from pathfinding.finder.a_star import AStarFinder
|
||||
#
|
||||
# grid = Grid(matrix=matrix)
|
||||
# start_node = grid.node(s_row, s_col)
|
||||
# end_node = grid.node(g_row, g_col)
|
||||
#
|
||||
# finder = AStarFinder()
|
||||
# path_nodes, _ = finder.find_path(start_node, end_node, grid)
|
||||
#
|
||||
# if not path_nodes or len(path_nodes) <= 1:
|
||||
# print(f"Путь не найден: {start} -> {goal}")
|
||||
# return None
|
||||
#
|
||||
# path = [(node.x, node.y) for node in path_nodes]
|
||||
#
|
||||
# return path
|
||||
|
||||
'''
|
||||
|
||||
'''
|
||||
#def bfs_quick_d(obstacle_matrix, start, goal):
|
||||
# rows = len(obstacle_matrix)
|
||||
# if rows == 0:
|
||||
# print("❌ DEBUG: ПУСТАЯ МАТРИЦА")
|
||||
# return None
|
||||
#
|
||||
# cols = len(obstacle_matrix[0])
|
||||
# s_row, s_col = start
|
||||
# g_row, g_col = goal
|
||||
#
|
||||
# print(f"🔍 DEBUG: start={start}, goal={goal}, размер={rows}x{cols}")
|
||||
#
|
||||
# if (s_row >= rows or s_col >= cols or
|
||||
# g_row >= rows or g_col >= cols):
|
||||
# print(f"❌ DEBUG: ВЫХОД ЗА ГРАНИЦЫ: start({s_row},{s_col}) goal({g_row},{g_col})")
|
||||
# return None
|
||||
#
|
||||
# print(f"✅ DEBUG: Границы OK. obstacle[start]={obstacle_matrix[s_row][s_col]}, obstacle[goal]={obstacle_matrix[g_row][g_col]}")
|
||||
#
|
||||
# visited = [[False] * cols for _ in range(rows)]
|
||||
# parent = [[None] * cols for _ in range(rows)]
|
||||
#
|
||||
# queue_size = 0
|
||||
# queue = [[0, 0] for _ in range(rows * cols)]
|
||||
# queue[0] = [s_row, s_col]
|
||||
# queue_size = 1
|
||||
# front = 0
|
||||
#
|
||||
# visited[s_row][s_col] = True
|
||||
# print(f"✅ DEBUG: Старт добавлен в очередь. queue_size={queue_size}")
|
||||
#
|
||||
# directions = [(-1,0), (1,0), (0,-1), (0,1), (-1,-1), (-1,1), (1,-1), (1,1)]
|
||||
#
|
||||
# DEBUG_COUNTER = 0
|
||||
#
|
||||
# while front < queue_size:
|
||||
# r, c = queue[front]
|
||||
# front += 1
|
||||
# DEBUG_COUNTER += 1
|
||||
#
|
||||
# print(f"🔄 DEBUG[{DEBUG_COUNTER}]: обрабатываем ({r},{c}), очередь={front}/{queue_size}")
|
||||
#
|
||||
# if r == g_row and c == g_col:
|
||||
# print(f"✅ DEBUG: НАЙДЕН ЦЕЛЬ! Путь: ({r},{c})")
|
||||
# path = []
|
||||
# cr, cc = g_row, g_col
|
||||
# while True:
|
||||
# path.append((cr, cc))
|
||||
# if parent[cr][cc] is None:
|
||||
# break
|
||||
# pr, pc = parent[cr][cc]
|
||||
# cr, cc = pr, pc
|
||||
# return path[::-1]
|
||||
#
|
||||
# for dr, dc in directions:
|
||||
# nr, nc = r + dr, c + dc
|
||||
#
|
||||
# print(f" 📍 Проверяем соседа ({nr},{nc}): граница={0<=nr<rows and 0<=nc<cols}, "
|
||||
# f"visited={visited[nr][nc]}, obstacle={obstacle_matrix[nr][nc]}")
|
||||
#
|
||||
# if (0 <= nr < rows and 0 <= nc < cols and
|
||||
# not visited[nr][nc] and
|
||||
# not obstacle_matrix[nr][nc]):
|
||||
#
|
||||
# diagonal_ok = True
|
||||
# if dr * dc != 0:
|
||||
# diagonal_ok = can_move_diagonal(r, c, nr, nc, obstacle_matrix)
|
||||
# print(f" ↘️ Диагональ: {diagonal_ok}")
|
||||
#
|
||||
# if diagonal_ok:
|
||||
# visited[nr][nc] = True
|
||||
# parent[nr][nc] = (r, c)
|
||||
# queue[queue_size] = [nr, nc]
|
||||
# queue_size += 1
|
||||
# print(f" ✅ Добавили ({nr},{nc}) в очередь. queue_size={queue_size}")
|
||||
# else:
|
||||
# print(f" ❌ Диагональ заблокирована!")
|
||||
# else:
|
||||
# print(f" ❌ Сосед отклонен!")
|
||||
#
|
||||
# print(f"❌ DEBUG: ОЧЕРЕДЬ ОПУСТЕЛА! Обработано {DEBUG_COUNTER} узлов")
|
||||
# print(f" Последняя клетка в очереди: {queue[front-1] if front > 0 else 'ПУСТО'}")
|
||||
# print(f" Цель ({g_row},{g_col}) помечена как visited? {visited[g_row][g_col]}")
|
||||
# return None
|
||||
#
|
||||
#
|
||||
#def bfs_quick(obstacle_matrix, start, goal):
|
||||
# """★СУПЕРБЫСТРЫЙ BFS 8-направлений★
|
||||
# obstacle_matrix - ТОЛЬКО камни
|
||||
# """
|
||||
# rows = len(obstacle_matrix)
|
||||
# if rows == 0:
|
||||
# return None
|
||||
#
|
||||
# cols = len(obstacle_matrix[0])
|
||||
# s_row, s_col = start
|
||||
# g_row, g_col = goal
|
||||
#
|
||||
# if (s_row >= rows or s_col >= cols or
|
||||
# g_row >= rows or g_col >= cols):
|
||||
# return None
|
||||
#
|
||||
# # ★ МАТРИЦЫ состояния ★
|
||||
# visited = [[False] * cols for _ in range(rows)]
|
||||
# parent = [[None] * cols for _ in range(rows)]
|
||||
#
|
||||
# # ★ БЫСТРАЯ ОЧЕРЕДЬ ★
|
||||
# queue_size = 0
|
||||
# queue = [[0, 0] for _ in range(rows * cols)]
|
||||
# queue[0] = [s_row, s_col]
|
||||
# queue_size = 1
|
||||
# front = 0
|
||||
#
|
||||
# visited[s_row][s_col] = True
|
||||
#
|
||||
# # ★ 8 НАПРАВЛЕНИЙ ★
|
||||
# directions = [(-1,0), (1,0), (0,-1), (0,1), # Кардинальные (всегда)
|
||||
# (-1,-1), (-1,1), (1,-1), (1,1)] # Диагональные
|
||||
#
|
||||
# while front < queue_size:
|
||||
# r, c = queue[front]
|
||||
# front += 1
|
||||
#
|
||||
# if r == g_row and c == g_col:
|
||||
# # ★ Реконструкция пути ★
|
||||
# path = []
|
||||
# cr, cc = g_row, g_col
|
||||
# while True:
|
||||
# path.append((cr, cc))
|
||||
# if parent[cr][cc] is None:
|
||||
# break
|
||||
# pr, pc = parent[cr][cc]
|
||||
# cr, cc = pr, pc
|
||||
# return path[::-1]
|
||||
#
|
||||
# for dr, dc in directions:
|
||||
# nr, nc = r + dr, c + dc
|
||||
#
|
||||
# if not (0 <= nr < rows and 0 <= nc < cols):
|
||||
# continue
|
||||
# if visited[nr][nc] or obstacle_matrix[nr][nc]:
|
||||
# continue
|
||||
#
|
||||
# diagonal_ok = True
|
||||
# if dr * dc != 0:
|
||||
# diagonal_ok = can_move_diagonal(r, c, nr, nc, obstacle_matrix)
|
||||
# if diagonal_ok:
|
||||
# visited[nr][nc] = True
|
||||
# parent[nr][nc] = (r, c)
|
||||
# queue[queue_size] = [nr, nc]
|
||||
# queue_size += 1
|
||||
#
|
||||
# return None
|
||||
'''
|
||||
#def can_move_diagonal(r, c, nr, nc, obstacle_matrix):
|
||||
# """Диагональ БЛОКИРУЕТСЯ только камнями по углам"""
|
||||
# rows, cols = len(obstacle_matrix), len(obstacle_matrix[0])
|
||||
# dr = nr - r
|
||||
# dc = nc - c
|
||||
#
|
||||
# r1, c1 = r + dr, c # Вертикальная соседка
|
||||
# r2, c2 = r, c + dc # Горизонтальная соседка
|
||||
#
|
||||
# check1_ok = (0 <= r1 < rows and 0 <= c1 < cols and not obstacle_matrix[r1][c1])
|
||||
# check2_ok = (0 <= r2 < rows and 0 <= c2 < cols and not obstacle_matrix[r2][c2])
|
||||
#
|
||||
# return check1_ok and check2_ok
|
||||
|
||||
#def can_move_diagonal(r, c, nr, nc, obstacle_matrix):
|
||||
# """Проверка диагонали с границами"""
|
||||
# rows, cols = len(obstacle_matrix), len(obstacle_matrix[0])
|
||||
# dr = nr - r
|
||||
# dc = nc - c
|
||||
#
|
||||
# # ★ ПРОВЕРКА ГРАНИЦ ПОПЕРЕДУ ★
|
||||
# r1, c1 = r + dr, c # вертикальная
|
||||
# r2, c2 = r, c + dc # горизонтальная
|
||||
#
|
||||
# # Если УЖЕ за границей - False
|
||||
# if not (0 <= r1 < rows and 0 <= c1 < cols):
|
||||
# return False
|
||||
# if not (0 <= r2 < rows and 0 <= c2 < cols):
|
||||
# return False
|
||||
#
|
||||
# return not obstacle_matrix[r1][c1] and not obstacle_matrix[r2][c2]
|
||||
Reference in New Issue
Block a user