import numpy as np import abc from collections import deque import heapq #Классы клетки и лабиринта class Cell: def __init__(self, coords, isWall = False, isStart = False, isExit = False): self.coords = coords self.isWall = isWall self.isStart = isStart self.isExit = isExit def isPassable(self): if self.isWall: return False return True class Maze: def __init__(self, cells, width, height, st, ex): self.cells = cells self.width = width self.height = height self.st = st self.ex = ex def getCell(self,x,y): try: return self.cells[x][y] except: return None def getNeighbors(self,cell): x,y = cell.coords res = [] for i,j in (x,y+1),(x,y-1),(x-1,y),(x+1,y): cellij = self.getCell(i,j) if i <= self.width-1 and j <= self.height-1 and 0 <= i and 0 <= j and cellij is not None: if cellij.isPassable(): res.append(cellij) else: res.append(None) else: res.append(None) return res #Тестирование классов клетки и лабиринта cell1 = Cell((1,2), isExit = True, isWall = True, isStart = False) print(cell1.isPassable()) print(cell1.isStart) print(cell1.coords) width, height = 3,3 cells = np.full((width,height), None, dtype=object) for x in range(width): for y in range(height): if x != 0 and x != width-1 and y != 0 and y != height-1: cells[x][y] = Cell((x,y), isWall = False) else: cells[x][y] = Cell((x,y), isWall = True) print(cells) maze1 = Maze(cells, width, height, cells[0], cells[-1]) for column in cells: for cell in column: print(cell.coords) print(maze1.getNeighbors(cell)) print('\n') #Интерфейс постройки лабиринта class MazeBuilder(abc.ABC): @abc.abstractmethod def buildFromFile(filename): pass #Наследуем от него класс постройки из текстового файла class TextFileMazeBuilder(MazeBuilder): def buildFromFile(filename): with open(filename, "r") as file: rows = file.read().splitlines() print(rows) width = 0 height = 0 for row in rows: height += 1 if len(row) > width: width = len(row) print(width, height) st = (0,0) ex = (width,height) cells = np.full((width,height), None, dtype=object) for y in range(height): for x in range(width): isWall = False isStart = False isExit = False if rows[-(y+1)][x] == '#': isWall = True elif rows[-(y+1)][x] == 'S': isStart = True st = (x,y) print('Старт в',x,y) elif rows[-(y+1)][x] == 'E': isExit = True ex = (x,y) print('Выход в',x,y) elif rows[-(y+1)][x] != ' ': raise ValueError("Неверный формат лабиринта! Пожалуйста, используйте только символы #,S,E и пробелы") cells[x][y] = Cell((x,y), isWall, isStart, isExit) return Maze(cells, width, height, cells[st[0]][st[1]], cells[ex[0]][ex[1]]) builder = TextFileMazeBuilder maze = builder.buildFromFile('maze1.txt') print(maze) #Интерфейс поиска пути class PathFindingStrategy(abc.ABC): @abc.abstractmethod def findPath(maze, st, ex): pass #Поиск в глубину class DFS(PathFindingStrategy): def findPath(maze,st,ex): stack = [st] visited = {st.coords} #по координатам надёжнее, а то вдруг адрес изменится pathmap = {} while stack: cell = stack.pop() if cell.coords == ex.coords: #маршрут выстраивается в обратном порядке и разворачивается path = [] while cell.coords != st.coords: path.append(cell) cell = pathmap[cell.coords] path.append(st) path = path[::-1] return path for n in maze.getNeighbors(cell): if n != None and n.coords not in visited: visited.add(n.coords) pathmap[n.coords] = cell stack.append(n) return None path = DFS.findPath(maze,maze.st,maze.ex) print('путь поиском в глубину:') for cell in path: print(cell.coords) class BFS(PathFindingStrategy): def findPath(maze,st,ex): queue = deque([st]) visited = {st.coords} #по координатам надёжнее, а то вдруг адрес изменится pathmap = {} while queue: cell = queue.popleft() if cell.coords == ex.coords: path = [] while cell.coords != st.coords: path.append(cell) cell = pathmap[cell.coords] path.append(st) path = path[::-1] return path for n in maze.getNeighbors(cell): if n != None and n.coords not in visited: visited.add(n.coords) pathmap[n.coords] = cell queue.append(n) return None path = BFS.findPath(maze,maze.st,maze.ex) print('путь поиском в ширину:') for cell in path: print(cell.coords) class Astar(PathFindingStrategy): def findPath(maze,st,ex): c = 0 hp_queue = [(0,c,st)] g_score = {st.coords: 0} pathmap = {} hp_queue_coords = {st.coords} #нам важна скорость while hp_queue: cell = heapq.heappop(hp_queue)[2] hp_queue_coords.remove(cell.coords) if cell.coords == ex.coords: path = [] while cell.coords != st.coords: path.append(cell) cell = pathmap[cell.coords] path.append(st) path = path[::-1] return path for n in maze.getNeighbors(cell): new_g_score = g_score[cell.coords] + 1 if n is not None and new_g_score < g_score.get(n.coords, float('inf')): pathmap[n.coords] = cell g_score[n.coords] = new_g_score h_score = abs(n.coords[0]-ex.coords[0]) + abs(n.coords[1]-ex.coords[1]) #f = g + h #h - манхэттенское расстояние full_score = new_g_score + h_score if n.coords not in hp_queue_coords: c += 1 heapq.heappush(hp_queue, (full_score, c, n)) hp_queue_coords.add(n.coords) return None path = Astar.findPath(maze,maze.st,maze.ex) print('путь с A*:') for cell in path: print(cell.coords)