forked from UNN/2026-rff_mp
Task 2
This commit is contained in:
parent
b4efeaf13e
commit
df3afe9dbf
9
KuzminskiyAA/Task 2/Docs/Data/01_small_maze.txt
Normal file
9
KuzminskiyAA/Task 2/Docs/Data/01_small_maze.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
##########
|
||||
#S #
|
||||
# ### ## #
|
||||
# # #
|
||||
### # ####
|
||||
# # #
|
||||
# ### # #
|
||||
# # E#
|
||||
##########
|
||||
13
KuzminskiyAA/Task 2/Docs/Data/02_medium_maze.txt
Normal file
13
KuzminskiyAA/Task 2/Docs/Data/02_medium_maze.txt
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
####################
|
||||
#S #
|
||||
# ### ########### #
|
||||
# # # # # #
|
||||
# # # # ####### # #
|
||||
# # # # # #
|
||||
# # ######### # # #
|
||||
# # # # #
|
||||
# # ########### # #
|
||||
# # # #
|
||||
# ############### #
|
||||
# E#
|
||||
####################
|
||||
26
KuzminskiyAA/Task 2/Docs/Data/03_large_maze.txt
Normal file
26
KuzminskiyAA/Task 2/Docs/Data/03_large_maze.txt
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
##################################################
|
||||
#S #
|
||||
# ############################################# #
|
||||
# # # #
|
||||
# # ######################################### # #
|
||||
# # # # # #
|
||||
# # # ##################################### # # #
|
||||
# # # # # # # #
|
||||
# # # # ################################# # # # #
|
||||
# # # # # # # # # #
|
||||
# # # # # ############################# # # # # #
|
||||
# # # # # # # # # # # #
|
||||
# # # # # # ######################### # # # # # #
|
||||
# # # # # # # # # # # # # #
|
||||
# # # # # # # ##################### # # # # # # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # # # # # # ################# # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # ############# # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # ######### # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # ##### # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #E#
|
||||
##################################################
|
||||
39
KuzminskiyAA/Task 2/Docs/Data/04_empty_maze.txt
Normal file
39
KuzminskiyAA/Task 2/Docs/Data/04_empty_maze.txt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
########################################
|
||||
#S #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# #
|
||||
# E#
|
||||
########################################
|
||||
9
KuzminskiyAA/Task 2/Docs/Data/05_no_exit_maze.txt
Normal file
9
KuzminskiyAA/Task 2/Docs/Data/05_no_exit_maze.txt
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
##########
|
||||
#S #
|
||||
# ### ## #
|
||||
# # #
|
||||
### # ####
|
||||
# # #
|
||||
# ### # #
|
||||
# #
|
||||
##########
|
||||
653
KuzminskiyAA/Task 2/Docs/Data/2.py
Normal file
653
KuzminskiyAA/Task 2/Docs/Data/2.py
Normal file
|
|
@ -0,0 +1,653 @@
|
|||
import os
|
||||
import time
|
||||
import heapq
|
||||
from collections import deque
|
||||
from typing import List, Dict, Optional, Tuple
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
class Cell:
|
||||
def __init__(self, x: int, y: int, is_wall: bool = True, is_start: bool = False, is_exit: bool = False):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.is_wall = is_wall
|
||||
self.is_start = is_start
|
||||
self.is_exit = is_exit
|
||||
|
||||
def is_passable(self) -> bool:
|
||||
return not self.is_wall
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Cell):
|
||||
return False
|
||||
return self.x == other.x and self.y == other.y
|
||||
|
||||
def __hash__(self):
|
||||
return hash((self.x, self.y))
|
||||
|
||||
def __repr__(self):
|
||||
return f"Cell({self.x}, {self.y})"
|
||||
|
||||
|
||||
class Maze:
|
||||
def __init__(self, width: int = 0, height: int = 0):
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.grid: List[List[Cell]] = []
|
||||
self.start: Optional[Cell] = None
|
||||
self.exit: Optional[Cell] = None
|
||||
|
||||
def set_cell(self, x: int, y: int, cell: Cell) -> None:
|
||||
if 0 <= x < self.width and 0 <= y < self.height:
|
||||
self.grid[y][x] = cell
|
||||
|
||||
def get_cell(self, x: int, y: int) -> Optional[Cell]:
|
||||
if 0 <= x < self.width and 0 <= y < self.height:
|
||||
return self.grid[y][x]
|
||||
return None
|
||||
|
||||
def get_neighbors(self, cell: Cell) -> List[Cell]:
|
||||
neighbors = []
|
||||
directions = [(0, -1), (0, 1), (-1, 0), (1, 0)]
|
||||
|
||||
for dx, dy in directions:
|
||||
nx, ny = cell.x + dx, cell.y + dy
|
||||
neighbor = self.get_cell(nx, ny)
|
||||
if neighbor and neighbor.is_passable():
|
||||
neighbors.append(neighbor)
|
||||
|
||||
return neighbors
|
||||
class MazeBuilder(ABC):
|
||||
@abstractmethod
|
||||
def build_from_file(self, filename: str) -> Maze:
|
||||
pass
|
||||
|
||||
|
||||
class TextFileMazeBuilder(MazeBuilder):
|
||||
|
||||
def build_from_file(self, filename: str) -> Maze:
|
||||
with open(filename, 'r', encoding='utf-8') as file:
|
||||
lines = [line.rstrip('\n') for line in file.readlines()]
|
||||
|
||||
while lines and not lines[0].strip():
|
||||
lines.pop(0)
|
||||
while lines and not lines[-1].strip():
|
||||
lines.pop()
|
||||
|
||||
height = len(lines)
|
||||
width = max(len(line) for line in lines) if height > 0 else 0
|
||||
|
||||
maze = Maze(width, height)
|
||||
maze.grid = [[None for _ in range(width)] for _ in range(height)]
|
||||
|
||||
start_count = 0
|
||||
exit_count = 0
|
||||
|
||||
for y, line in enumerate(lines):
|
||||
for x in range(width):
|
||||
char = line[x] if x < len(line) else '#'
|
||||
|
||||
if char == '#':
|
||||
cell = Cell(x, y, is_wall=True)
|
||||
elif char == ' ':
|
||||
cell = Cell(x, y, is_wall=False)
|
||||
elif char == 'S':
|
||||
cell = Cell(x, y, is_wall=False, is_start=True)
|
||||
maze.start = cell
|
||||
start_count += 1
|
||||
elif char == 'E':
|
||||
cell = Cell(x, y, is_wall=False, is_exit=True)
|
||||
maze.exit = cell
|
||||
exit_count += 1
|
||||
else:
|
||||
cell = Cell(x, y, is_wall=True)
|
||||
|
||||
maze.set_cell(x, y, cell)
|
||||
|
||||
if start_count == 0:
|
||||
raise ValueError("Лабиринт должен содержать старт (S)")
|
||||
if start_count > 1:
|
||||
raise ValueError("Лабиринт может содержать только один старт (S)")
|
||||
if exit_count == 0:
|
||||
raise ValueError("Лабиринт должен содержать выход (E)")
|
||||
if exit_count > 1:
|
||||
raise ValueError("Лабиринт может содержать только один выход (E)")
|
||||
|
||||
return maze
|
||||
class PathFindingStrategy(ABC):
|
||||
|
||||
@abstractmethod
|
||||
def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> Tuple[List[Cell], int]:
|
||||
pass
|
||||
|
||||
def _reconstruct_path(self, parents: Dict[Cell, Cell], start: Cell, exit_cell: Cell) -> List[Cell]:
|
||||
path = []
|
||||
current = exit_cell
|
||||
|
||||
while current != start:
|
||||
path.append(current)
|
||||
if current not in parents:
|
||||
return []
|
||||
current = parents[current]
|
||||
path.append(start)
|
||||
path.reverse()
|
||||
return path
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return self.__class__.__name__.replace('Strategy', '')
|
||||
|
||||
|
||||
class BFSStrategy(PathFindingStrategy):
|
||||
|
||||
def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> Tuple[List[Cell], int]:
|
||||
queue = deque([start])
|
||||
visited = {start}
|
||||
parents: Dict[Cell, Cell] = {}
|
||||
visited_count = 1
|
||||
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
|
||||
if current == exit_cell:
|
||||
return self._reconstruct_path(parents, start, exit_cell), visited_count
|
||||
|
||||
for neighbor in maze.get_neighbors(current):
|
||||
if neighbor not in visited:
|
||||
visited.add(neighbor)
|
||||
visited_count += 1
|
||||
parents[neighbor] = current
|
||||
queue.append(neighbor)
|
||||
|
||||
return [], visited_count
|
||||
|
||||
|
||||
class DFSStrategy(PathFindingStrategy):
|
||||
|
||||
def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> Tuple[List[Cell], int]:
|
||||
stack = [(start, [start])]
|
||||
visited = {start}
|
||||
visited_count = 1
|
||||
|
||||
while stack:
|
||||
current, path = stack.pop()
|
||||
|
||||
if current == exit_cell:
|
||||
return path, visited_count
|
||||
|
||||
for neighbor in maze.get_neighbors(current):
|
||||
if neighbor not in visited:
|
||||
visited.add(neighbor)
|
||||
visited_count += 1
|
||||
stack.append((neighbor, path + [neighbor]))
|
||||
|
||||
return [], visited_count
|
||||
|
||||
|
||||
class AStarStrategy(PathFindingStrategy):
|
||||
|
||||
def _heuristic(self, cell: Cell, target: Cell) -> int:
|
||||
return abs(cell.x - target.x) + abs(cell.y - target.y)
|
||||
|
||||
def find_path(self, maze: Maze, start: Cell, exit_cell: Cell) -> Tuple[List[Cell], int]:
|
||||
counter = 0
|
||||
open_set = [(0, counter, start)]
|
||||
came_from: Dict[Cell, Cell] = {}
|
||||
|
||||
g_score: Dict[Cell, float] = {start: 0}
|
||||
f_score: Dict[Cell, float] = {start: self._heuristic(start, exit_cell)}
|
||||
|
||||
open_set_hash = {start}
|
||||
visited_count = 1
|
||||
|
||||
while open_set:
|
||||
current = heapq.heappop(open_set)[2]
|
||||
open_set_hash.remove(current)
|
||||
|
||||
if current == exit_cell:
|
||||
path = self._reconstruct_path(came_from, start, exit_cell)
|
||||
return path, visited_count
|
||||
|
||||
for neighbor in maze.get_neighbors(current):
|
||||
tentative_g_score = g_score[current] + 1
|
||||
|
||||
if tentative_g_score < g_score.get(neighbor, float('inf')):
|
||||
came_from[neighbor] = current
|
||||
g_score[neighbor] = tentative_g_score
|
||||
f_score[neighbor] = tentative_g_score + self._heuristic(neighbor, exit_cell)
|
||||
|
||||
if neighbor not in open_set_hash:
|
||||
visited_count += 1
|
||||
counter += 1
|
||||
heapq.heappush(open_set, (f_score[neighbor], counter, neighbor))
|
||||
open_set_hash.add(neighbor)
|
||||
|
||||
return [], visited_count
|
||||
@dataclass
|
||||
class SearchStats:
|
||||
execution_time_ms: float
|
||||
path_length: int
|
||||
visited_cells: int
|
||||
success: bool
|
||||
|
||||
|
||||
class MazeSolver:
|
||||
|
||||
def __init__(self, maze: Maze, strategy: PathFindingStrategy):
|
||||
self.maze = maze
|
||||
self.strategy = strategy
|
||||
|
||||
def set_strategy(self, strategy: PathFindingStrategy) -> None:
|
||||
self.strategy = strategy
|
||||
|
||||
def solve(self) -> Tuple[List[Cell], SearchStats]:
|
||||
if not self.maze.start or not self.maze.exit:
|
||||
raise ValueError("Лабиринт должен содержать старт и выход")
|
||||
|
||||
start_time = time.perf_counter()
|
||||
path, visited_cells = self.strategy.find_path(self.maze, self.maze.start, self.maze.exit)
|
||||
end_time = time.perf_counter()
|
||||
|
||||
execution_time = (end_time - start_time) * 1000
|
||||
|
||||
stats = SearchStats(
|
||||
execution_time_ms=execution_time,
|
||||
path_length=len(path),
|
||||
visited_cells=visited_cells,
|
||||
success=len(path) > 0
|
||||
)
|
||||
|
||||
return path, stats
|
||||
class MazeVisualizer:
|
||||
|
||||
@staticmethod
|
||||
def render(maze: Maze, path: List[Cell] = None, player_pos: Cell = None):
|
||||
print("\n" + "=" * (maze.width + 2))
|
||||
|
||||
for y in range(maze.height):
|
||||
row = "|"
|
||||
for x in range(maze.width):
|
||||
cell = maze.get_cell(x, y)
|
||||
if cell:
|
||||
if player_pos and cell == player_pos:
|
||||
row += "P"
|
||||
elif path and cell in path and not cell.is_start and not cell.is_exit:
|
||||
row += "."
|
||||
elif cell.is_start:
|
||||
row += "S"
|
||||
elif cell.is_exit:
|
||||
row += "E"
|
||||
elif cell.is_wall:
|
||||
row += "#"
|
||||
else:
|
||||
row += " "
|
||||
else:
|
||||
row += " "
|
||||
row += "|"
|
||||
print(row)
|
||||
print("=" * (maze.width + 2))
|
||||
def create_test_mazes():
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
small_maze = """##########
|
||||
#S #
|
||||
# ### ## #
|
||||
# # #
|
||||
### # ####
|
||||
# # #
|
||||
# ### # #
|
||||
# # E#
|
||||
##########"""
|
||||
|
||||
medium_maze = """####################
|
||||
#S #
|
||||
# ### ########### #
|
||||
# # # # # #
|
||||
# # # # ####### # #
|
||||
# # # # # #
|
||||
# # ######### # # #
|
||||
# # # # #
|
||||
# # ########### # #
|
||||
# # # #
|
||||
# ############### #
|
||||
# E#
|
||||
####################"""
|
||||
|
||||
large_maze = """##################################################
|
||||
#S #
|
||||
# ############################################# #
|
||||
# # # #
|
||||
# # ######################################### # #
|
||||
# # # # # #
|
||||
# # # ##################################### # # #
|
||||
# # # # # # # #
|
||||
# # # # ################################# # # # #
|
||||
# # # # # # # # # #
|
||||
# # # # # ############################# # # # # #
|
||||
# # # # # # # # # # # #
|
||||
# # # # # # ######################### # # # # # #
|
||||
# # # # # # # # # # # # # #
|
||||
# # # # # # # ##################### # # # # # # #
|
||||
# # # # # # # # # # # # # # # #
|
||||
# # # # # # # # ################# # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # ############# # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # ######### # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # ##### # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #
|
||||
# # # # # # # # # # # # # # # # # # # # # # # #E#
|
||||
##################################################"""
|
||||
|
||||
empty_maze_lines = ["#" + "#" * 38 + "#"]
|
||||
empty_maze_lines.append("#S" + " " * 37 + "#")
|
||||
for i in range(35):
|
||||
empty_maze_lines.append("#" + " " * 38 + "#")
|
||||
empty_maze_lines.append("#" + " " * 37 + "E#")
|
||||
empty_maze_lines.append("#" + "#" * 38 + "#")
|
||||
empty_maze = "\n".join(empty_maze_lines)
|
||||
|
||||
no_exit_maze = """##########
|
||||
#S #
|
||||
# ### ## #
|
||||
# # #
|
||||
### # ####
|
||||
# # #
|
||||
# ### # #
|
||||
# #
|
||||
##########"""
|
||||
|
||||
mazes = [
|
||||
("01_small_maze.txt", small_maze),
|
||||
("02_medium_maze.txt", medium_maze),
|
||||
("03_large_maze.txt", large_maze),
|
||||
("04_empty_maze.txt", empty_maze),
|
||||
("05_no_exit_maze.txt", no_exit_maze)
|
||||
]
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("CREATING TEST MAZES")
|
||||
print("="*60)
|
||||
|
||||
for filename, content in mazes:
|
||||
filepath = os.path.join(current_dir, filename)
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
print(f"Created: {filename}")
|
||||
|
||||
print("="*60)
|
||||
|
||||
|
||||
def list_available_mazes():
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
maze_files = [f for f in os.listdir(current_dir) if f.endswith('.txt') and ('maze' in f.lower() or f.startswith('0'))]
|
||||
|
||||
if not maze_files:
|
||||
return []
|
||||
|
||||
print("\nAVAILABLE MAZES:")
|
||||
print("-" * 50)
|
||||
for i, file in enumerate(maze_files, 1):
|
||||
print(f" {i}. {file}")
|
||||
|
||||
return maze_files
|
||||
class Benchmark:
|
||||
|
||||
def __init__(self):
|
||||
self.strategies = [
|
||||
BFSStrategy(),
|
||||
DFSStrategy(),
|
||||
AStarStrategy()
|
||||
]
|
||||
|
||||
def run_experiment(self, maze: Maze, runs: int = 5) -> Dict:
|
||||
results = {}
|
||||
|
||||
for strategy in self.strategies:
|
||||
times = []
|
||||
path_lengths = []
|
||||
visited_counts = []
|
||||
|
||||
for _ in range(runs):
|
||||
solver = MazeSolver(maze, strategy)
|
||||
path, stats = solver.solve()
|
||||
|
||||
if stats.success:
|
||||
times.append(stats.execution_time_ms)
|
||||
path_lengths.append(stats.path_length)
|
||||
visited_counts.append(stats.visited_cells)
|
||||
|
||||
if times:
|
||||
results[strategy.name] = {
|
||||
'avg_time_ms': sum(times) / len(times),
|
||||
'avg_path_length': sum(path_lengths) / len(path_lengths),
|
||||
'avg_visited_cells': sum(visited_counts) / len(visited_counts),
|
||||
'success_rate': len(times) / runs * 100
|
||||
}
|
||||
else:
|
||||
results[strategy.name] = {
|
||||
'avg_time_ms': float('inf'),
|
||||
'avg_path_length': 0,
|
||||
'avg_visited_cells': 0,
|
||||
'success_rate': 0
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
def print_results(results: Dict, maze_name: str):
|
||||
print(f"\n{'='*60}")
|
||||
print(f"RESULTS FOR MAZE: {maze_name}")
|
||||
print(f"{'='*60}")
|
||||
print(f"{'Algorithm':<12} {'Time(ms)':<12} {'PathLen':<12} {'Visited':<12} {'Success':<8}")
|
||||
print(f"{'-'*60}")
|
||||
|
||||
for strategy_name, stats in results.items():
|
||||
print(f"{strategy_name:<12} {stats['avg_time_ms']:>8.3f} "
|
||||
f"{stats['avg_path_length']:>8.1f} "
|
||||
f"{stats['avg_visited_cells']:>8.1f} "
|
||||
f"{stats['success_rate']:>6.1f}%")
|
||||
def interactive_mode():
|
||||
builder = TextFileMazeBuilder()
|
||||
visualizer = MazeVisualizer()
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
while True:
|
||||
print("\n" + "="*60)
|
||||
print("MAZE PATHFINDING PROGRAM")
|
||||
print("="*60)
|
||||
print("1. Load maze and find path")
|
||||
print("2. Compare all algorithms on maze")
|
||||
print("3. Create test mazes (5 pieces)")
|
||||
print("4. Run benchmark on all mazes")
|
||||
print("5. Show available mazes")
|
||||
print("6. Exit")
|
||||
print("="*60)
|
||||
|
||||
choice = input("Choose action (1-6): ").strip()
|
||||
|
||||
if choice == '6':
|
||||
print("\nGoodbye!")
|
||||
break
|
||||
|
||||
elif choice == '3':
|
||||
create_test_mazes()
|
||||
input("\nPress Enter to continue...")
|
||||
|
||||
elif choice == '5':
|
||||
maze_files = list_available_mazes()
|
||||
if not maze_files:
|
||||
print("\nNo available mazes. Create them first (action 3)")
|
||||
input("\nPress Enter to continue...")
|
||||
|
||||
elif choice == '1':
|
||||
print("\nAvailable mazes:")
|
||||
maze_files = list_available_mazes()
|
||||
|
||||
if not maze_files:
|
||||
print("\nNo available mazes. Create them first (action 3)")
|
||||
continue
|
||||
|
||||
filename = input("\nEnter path to maze file: ").strip()
|
||||
|
||||
if not os.path.exists(filename):
|
||||
test_path = os.path.join(current_dir, filename)
|
||||
if os.path.exists(test_path):
|
||||
filename = test_path
|
||||
|
||||
try:
|
||||
maze = builder.build_from_file(filename)
|
||||
print("\nLOADED MAZE:")
|
||||
visualizer.render(maze)
|
||||
|
||||
print("\nChoose algorithm:")
|
||||
print(" 1. BFS - finds SHORTEST path")
|
||||
print(" 2. DFS - FAST but path may be longer")
|
||||
print(" 3. A* - OPTIMAL balance")
|
||||
|
||||
algo_choice = input("\nYour choice (1-3): ").strip()
|
||||
|
||||
strategies = {
|
||||
'1': BFSStrategy(),
|
||||
'2': DFSStrategy(),
|
||||
'3': AStarStrategy()
|
||||
}
|
||||
|
||||
if algo_choice in strategies:
|
||||
print("\nSearching for path...")
|
||||
solver = MazeSolver(maze, strategies[algo_choice])
|
||||
path, stats = solver.solve()
|
||||
|
||||
if stats.success:
|
||||
print(f"\nPATH FOUND!")
|
||||
print(f"\nSTATISTICS:")
|
||||
print(f" Time: {stats.execution_time_ms:.3f} ms")
|
||||
print(f" Path length: {stats.path_length} steps")
|
||||
print(f" Visited cells: {stats.visited_cells}")
|
||||
print(f" Efficiency: {stats.visited_cells/stats.path_length:.1f} cells per step")
|
||||
|
||||
print("\nPATH ON MAP (. = path):")
|
||||
visualizer.render(maze, path)
|
||||
else:
|
||||
print("\nPATH NOT FOUND! Exit unreachable from start.")
|
||||
else:
|
||||
print("Invalid choice!")
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"\nFile '{filename}' not found!")
|
||||
except Exception as e:
|
||||
print(f"\nError: {e}")
|
||||
|
||||
input("\nPress Enter to continue...")
|
||||
|
||||
elif choice == '2':
|
||||
print("\nAvailable mazes:")
|
||||
maze_files = list_available_mazes()
|
||||
|
||||
if not maze_files:
|
||||
print("\nNo available mazes. Create them first (action 3)")
|
||||
continue
|
||||
|
||||
filename = input("\nEnter path to maze file: ").strip()
|
||||
|
||||
if not os.path.exists(filename):
|
||||
test_path = os.path.join(current_dir, filename)
|
||||
if os.path.exists(test_path):
|
||||
filename = test_path
|
||||
|
||||
try:
|
||||
maze = builder.build_from_file(filename)
|
||||
print("\nLOADED MAZE:")
|
||||
visualizer.render(maze)
|
||||
|
||||
print("\nRunning algorithm comparison (3 runs each)...")
|
||||
benchmark = Benchmark()
|
||||
results = benchmark.run_experiment(maze, runs=3)
|
||||
|
||||
maze_name = os.path.basename(filename)
|
||||
benchmark.print_results(results, maze_name)
|
||||
|
||||
print("\nANALYSIS:")
|
||||
print("-" * 40)
|
||||
fastest = min(results.items(), key=lambda x: x[1]['avg_time_ms'])
|
||||
print(f"Fastest: {fastest[0]} ({fastest[1]['avg_time_ms']:.3f} ms)")
|
||||
|
||||
shortest = min(results.items(), key=lambda x: x[1]['avg_path_length'])
|
||||
print(f"Shortest path: {shortest[0]} ({shortest[1]['avg_path_length']:.0f} steps)")
|
||||
|
||||
efficient = min(results.items(), key=lambda x: x[1]['avg_visited_cells'])
|
||||
print(f"Most efficient: {efficient[0]} (checked {efficient[1]['avg_visited_cells']:.0f} cells)")
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"\nFile '{filename}' not found!")
|
||||
except Exception as e:
|
||||
print(f"\nError: {e}")
|
||||
|
||||
input("\nPress Enter to continue...")
|
||||
|
||||
elif choice == '4':
|
||||
print("\nRUNNING FULL BENCHMARK")
|
||||
print("="*60)
|
||||
|
||||
test_files = [
|
||||
"01_small_maze.txt",
|
||||
"02_medium_maze.txt",
|
||||
"03_large_maze.txt",
|
||||
"04_empty_maze.txt",
|
||||
"05_no_exit_maze.txt"
|
||||
]
|
||||
|
||||
benchmark = Benchmark()
|
||||
all_results = {}
|
||||
|
||||
for test_file in test_files:
|
||||
filepath = os.path.join(current_dir, test_file)
|
||||
|
||||
if not os.path.exists(filepath):
|
||||
print(f"\nFile {test_file} not found. Creating test mazes...")
|
||||
create_test_mazes()
|
||||
break
|
||||
|
||||
try:
|
||||
print(f"\nTesting: {test_file}")
|
||||
maze = builder.build_from_file(filepath)
|
||||
results = benchmark.run_experiment(maze, runs=5)
|
||||
benchmark.print_results(results, test_file)
|
||||
all_results[test_file] = results
|
||||
except Exception as e:
|
||||
print(f"Error testing {test_file}: {e}")
|
||||
|
||||
if all_results:
|
||||
csv_filename = os.path.join(current_dir, "benchmark_results.csv")
|
||||
with open(csv_filename, "w", encoding='utf-8') as f:
|
||||
f.write("Maze,Algorithm,AvgTimeMs,AvgPathLength,AvgVisitedCells,SuccessPercent\n")
|
||||
for maze_name, results in all_results.items():
|
||||
for strategy_name, stats in results.items():
|
||||
f.write(f"{maze_name},{strategy_name},"
|
||||
f"{stats['avg_time_ms']:.3f},{stats['avg_path_length']:.1f},"
|
||||
f"{stats['avg_visited_cells']:.1f},{stats['success_rate']:.1f}\n")
|
||||
|
||||
print(f"\nResults saved to: {csv_filename}")
|
||||
|
||||
input("\nPress Enter to continue...")
|
||||
def main():
|
||||
print("="*60)
|
||||
print("MAZE PATHFINDING PROGRAM")
|
||||
print("Patterns: Builder, Strategy")
|
||||
print("Algorithms: BFS, DFS, A*")
|
||||
print("="*60)
|
||||
|
||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
existing_mazes = [f for f in os.listdir(current_dir) if f.endswith('.txt') and ('maze' in f.lower() or f.startswith('0'))]
|
||||
|
||||
if not existing_mazes:
|
||||
print("First run: create test mazes (action 3)\n")
|
||||
|
||||
interactive_mode()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
KuzminskiyAA/Task 2/Docs/Отчет.docx
Normal file
BIN
KuzminskiyAA/Task 2/Docs/Отчет.docx
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user