SmirnovaVYu #380

Merged
git_admin merged 58 commits from smirnovavyu/2026-rff_mp:SmirnovaVYu into develop 2026-09-05 07:04:17 +00:00
5 changed files with 315 additions and 0 deletions
Showing only changes of commit ac4f3924d8 - Show all commits

View File

@ -0,0 +1,62 @@
from abc import ABC, abstractmethod
from typing import Optional
from models import Cell, Maze
class Player:
def __init__(self, start_cell: Cell):
self.current_cell = start_cell
def move_to(self, new_cell: Cell) -> None:
self.current_cell = new_cell
class Command(ABC):
@abstractmethod
def execute(self) -> bool:
pass
@abstractmethod
def undo(self) -> None:
pass
class MoveCommand(Command):
def __init__(self, player: Player, maze: Maze, direction: str):
self.player = player
self.maze = maze
self.direction = direction
self.previous_cell: Optional[Cell] = None
self.new_cell: Optional[Cell] = None
def _get_target_cell(self) -> Optional[Cell]:
x, y = self.player.current_cell.x, self.player.current_cell.y
if self.direction == 'w':
y -= 1
elif self.direction == 's':
y += 1
elif self.direction == 'a':
x -= 1
elif self.direction == 'd':
x += 1
else:
return None
return self.maze.get_cell(x, y)
def execute(self) -> bool:
self.previous_cell = self.player.current_cell
self.new_cell = self._get_target_cell()
if self.new_cell and self.new_cell.is_passable():
self.player.move_to(self.new_cell)
return True
return False
def undo(self) -> None:
if self.previous_cell:
self.player.move_to(self.previous_cell)

View File

@ -0,0 +1,13 @@
maze_file,maze_size,strategy,time_mean,time_min,time_max,visited_mean,path_length_mean,path_found
small.txt,10×10,BFS,0.14973321231082082,0.08187501225620508,0.3416250110603869,15.0,15.0,True
small.txt,10×10,DFS,0.05074121290817857,0.036958022974431515,0.09620800847187638,21.0,21.0,True
small.txt,10×10,A*,0.11340839555487037,0.07800001185387373,0.24145899806171656,15.0,15.0,True
medium.txt,20×11,BFS,0.28489179676398635,0.21541700698435307,0.3855000250041485,26.0,26.0,True
medium.txt,20×11,DFS,0.22732499055564404,0.16850000247359276,0.396291958168149,90.0,90.0,True
medium.txt,20×11,A*,0.2952334121800959,0.290708034299314,0.30733400490134954,26.0,26.0,True
large.txt,30×15,BFS,0.5741997971199453,0.4562910180538893,0.8350000134669244,40.0,40.0,True
large.txt,30×15,DFS,0.4984830040484667,0.38241699803620577,0.6361660198308527,196.0,196.0,True
large.txt,30×15,A*,0.6602250039577484,0.6104999920353293,0.7946669938974082,40.0,40.0,True
empty.txt,30×1,BFS,0.06608341354876757,0.04250003257766366,0.11475000064820051,30.0,30.0,True
empty.txt,30×1,DFS,0.048741791397333145,0.039041973650455475,0.06312498589977622,30.0,30.0,True
empty.txt,30×1,A*,0.06089139496907592,0.055582961067557335,0.07212499622255564,30.0,30.0,True
1 maze_file maze_size strategy time_mean time_min time_max visited_mean path_length_mean path_found
2 small.txt 10×10 BFS 0.14973321231082082 0.08187501225620508 0.3416250110603869 15.0 15.0 True
3 small.txt 10×10 DFS 0.05074121290817857 0.036958022974431515 0.09620800847187638 21.0 21.0 True
4 small.txt 10×10 A* 0.11340839555487037 0.07800001185387373 0.24145899806171656 15.0 15.0 True
5 medium.txt 20×11 BFS 0.28489179676398635 0.21541700698435307 0.3855000250041485 26.0 26.0 True
6 medium.txt 20×11 DFS 0.22732499055564404 0.16850000247359276 0.396291958168149 90.0 90.0 True
7 medium.txt 20×11 A* 0.2952334121800959 0.290708034299314 0.30733400490134954 26.0 26.0 True
8 large.txt 30×15 BFS 0.5741997971199453 0.4562910180538893 0.8350000134669244 40.0 40.0 True
9 large.txt 30×15 DFS 0.4984830040484667 0.38241699803620577 0.6361660198308527 196.0 196.0 True
10 large.txt 30×15 A* 0.6602250039577484 0.6104999920353293 0.7946669938974082 40.0 40.0 True
11 empty.txt 30×1 BFS 0.06608341354876757 0.04250003257766366 0.11475000064820051 30.0 30.0 True
12 empty.txt 30×1 DFS 0.048741791397333145 0.039041973650455475 0.06312498589977622 30.0 30.0 True
13 empty.txt 30×1 A* 0.06089139496907592 0.055582961067557335 0.07212499622255564 30.0 30.0 True

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -0,0 +1,94 @@
import csv
import time
from typing import List, Dict
from models import Maze
from builders import TextFileMazeBuilder
from strategies import BFSStrategy, DFSStrategy, AStarStrategy
from solver import MazeSolver
def run_experiment(maze: Maze, strategy_name: str, strategy, repeats: int = 5) -> Dict:
times = []
visited_counts = []
path_lengths = []
path_found = True
for _ in range(repeats):
solver = MazeSolver(maze, strategy)
path, stats = solver.solve()
times.append(stats.time_ms)
visited_counts.append(stats.visited_cells)
path_lengths.append(stats.path_length)
path_found = stats.path_found
return {
'strategy': strategy_name,
'time_mean': sum(times) / len(times),
'time_min': min(times),
'time_max': max(times),
'visited_mean': sum(visited_counts) / len(visited_counts),
'path_length_mean': sum(path_lengths) / len(path_lengths) if path_found else 0,
'path_found': path_found
}
def run_all_experiments(maze_files: List[str], repeats: int = 5) -> List[Dict]:
builder = TextFileMazeBuilder()
strategies = [
('BFS', BFSStrategy()),
('DFS', DFSStrategy()),
('A*', AStarStrategy())
]
results = []
for maze_file in maze_files:
try:
maze = builder.build_from_file(maze_file)
except (ValueError, FileNotFoundError) as e:
print(f" Ошибка: {e}")
continue
print(f" Размер: {maze.width}×{maze.height}")
print(f" Старт: ({maze.start.x}, {maze.start.y})")
print(f" Выход: ({maze.exit.x}, {maze.exit.y})")
for strategy_name, strategy in strategies:
print(f" Тестирование: {strategy_name}")
result = run_experiment(maze, strategy_name, strategy, repeats)
result['maze_file'] = maze_file.split('/')[-1]
result['maze_size'] = f"{maze.width}×{maze.height}"
results.append(result)
status = "ok" if result['path_found'] else "ne ok"
print(f" {status} Время: {result['time_mean']:.2f} мс, "
f"Посещено: {result['visited_mean']:.0f}, "
f"Путь: {result['path_length_mean']:.0f}")
return results
def save_results_to_csv(results: List[Dict], filename: str = "experiment_results.csv") -> None:
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=[
'maze_file', 'maze_size', 'strategy',
'time_mean', 'time_min', 'time_max',
'visited_mean', 'path_length_mean', 'path_found'
])
writer.writeheader()
writer.writerows(results)
def print_results_table(results: List[Dict]) -> None:
print("\n" + "=" * 80)
print("РЕЗУЛЬТАТЫ ЭКСПЕРИМЕНТОВ")
print("=" * 80)
for res in results:
print(f"\nЛабиринт: {res['maze_file']}")
print(f" Стратегия: {res['strategy']}")
print(f" Время (ср): {res['time_mean']:.2f} мс")
print(f" Посещено: {res['visited_mean']:.0f} клеток")
print(f" Длина пути: {res['path_length_mean']:.0f}")

View File

@ -0,0 +1,146 @@
import os
from builders import TextFileMazeBuilder
from strategies import BFSStrategy, DFSStrategy, AStarStrategy
from solver import MazeSolver
from observers import ConsoleView
from commands import Player
from experiments import run_all_experiments, save_results_to_csv, print_results_table
def create_test_mazes():
os.makedirs("mazes", exist_ok=True)
small = """##########
#S #
# ### ## #
# # #
### # ####
# # #
# ### # #
# # #
# # E#
##########"""
medium = """####################
#S #
# # # # # # # # # #
# #
# # # # # # # # # #
# #
# # # # # # # # # #
# #
# # # # # # # # # #
# E#
####################"""
large = """##############################
#S #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# #
# # # # # # # # # # # # # # #
# E#
##############################"""
empty = "S" + " " * 28 + "E"
no_exit = """#######
#S #
# ### #
# # #
#######"""
with open("mazes/small.txt", "w") as f:
f.write(small)
with open("mazes/medium.txt", "w") as f:
f.write(medium)
with open("mazes/large.txt", "w") as f:
f.write(large)
with open("mazes/empty.txt", "w") as f:
f.write(empty)
with open("mazes/no_exit.txt", "w") as f:
f.write(no_exit)
def demo_maze_solver():
print("\n" + "=" * 60)
print("ДЕМОНСТРАЦИЯ РАБОТЫ MAZE SOLVER")
print("=" * 60)
builder = TextFileMazeBuilder()
view = ConsoleView()
maze = builder.build_from_file("mazes/small.txt")
view.update("maze_loaded", {"maze": maze})
strategies = [
("BFS", BFSStrategy(), "BFS"),
("DFS", DFSStrategy(), "DFSs"),
("A*", AStarStrategy(), "A*")
]
for name, strategy, description in strategies:
solver = MazeSolver(maze, strategy)
view.update("search_start", {"algorithm": description})
path, stats = solver.solve()
if stats.path_found:
view.update("path_found", {"maze": maze, "path": path, "stats": stats})
else:
view.update("no_path", {"stats": stats})
def demo_player_controls():
print("\n" + "=" * 60)
print("Command + Observer")
print("=" * 60)
builder = TextFileMazeBuilder()
view = ConsoleView()
maze = builder.build_from_file("mazes/small.txt")
player = Player(maze.start)
view.update("maze_loaded", {"maze": maze})
view.render(maze, player_position=player.current_cell)
def run_experiments():
print("\n" + "=" * 60)
print("ЭКСПЕРИМЕНТАЛЬНОЕ СРАВНЕНИЕ АЛГОРИТМОВ")
print("=" * 60)
maze_files = [
"mazes/small.txt",
"mazes/medium.txt",
"mazes/large.txt",
"mazes/empty.txt",
"mazes/no_exit.txt"
]
results = run_all_experiments(maze_files, repeats=5)
save_results_to_csv(results)
print_results_table(results)
def main():
print("Объектно-ориентированная реализация с паттернами")
print("Паттерны: Builder, Strategy, Observer, Command")
create_test_mazes()
demo_maze_solver()
demo_player_controls()
run_experiments()
if __name__ == "__main__":
main()