Merge pull request '[1] 1-st-exercise FINAL' (#320) from semyanovra/2026-rff_mp:1-st-exercise into develop

Reviewed-on: UNN/2026-rff_mp#320
This commit is contained in:
kit8nino 2026-05-30 12:00:46 +00:00
commit a5777ae4bc
18 changed files with 1622 additions and 0 deletions

View File

@ -0,0 +1,31 @@
Structure,Mode,Repeat,Insert (sec),Search (sec),Delete (sec)
LinkedList,random,1,3.972341,0.027657,0.012911
LinkedList,random,2,4.045646,0.023430,0.015166
LinkedList,random,3,4.108713,0.029786,0.011930
LinkedList,random,4,4.177241,0.028833,0.014464
LinkedList,random,5,4.185596,0.029333,0.012727
LinkedList,sorted,1,3.790176,0.025204,0.010269
LinkedList,sorted,2,3.810435,0.022951,0.011524
LinkedList,sorted,3,3.803720,0.025208,0.010396
LinkedList,sorted,4,3.815409,0.027041,0.010837
LinkedList,sorted,5,3.803349,0.025340,0.011777
HashTable,random,1,0.010245,0.000075,0.000036
HashTable,random,2,0.008733,0.000079,0.000069
HashTable,random,3,0.013354,0.000094,0.000044
HashTable,random,4,0.008903,0.000078,0.000036
HashTable,random,5,0.009199,0.000072,0.000033
HashTable,sorted,1,0.010286,0.000114,0.000052
HashTable,sorted,2,0.009219,0.000073,0.000034
HashTable,sorted,3,0.011302,0.000068,0.000033
HashTable,sorted,4,0.009324,0.000068,0.000033
HashTable,sorted,5,0.008641,0.000068,0.000034
BST,random,1,0.027580,0.000190,0.000118
BST,random,2,0.020693,0.000188,0.000116
BST,random,3,0.020889,0.000190,0.000109
BST,random,4,0.022945,0.000182,0.000110
BST,random,5,0.022395,0.000207,0.000114
BST,sorted,1,9.109235,0.083432,0.049594
BST,sorted,2,9.177649,0.097374,0.050929
BST,sorted,3,9.414714,0.067665,0.054041
BST,sorted,4,9.062772,0.090823,0.048369
BST,sorted,5,8.994138,0.072883,0.049921
1 Structure Mode Repeat Insert (sec) Search (sec) Delete (sec)
2 LinkedList random 1 3.972341 0.027657 0.012911
3 LinkedList random 2 4.045646 0.023430 0.015166
4 LinkedList random 3 4.108713 0.029786 0.011930
5 LinkedList random 4 4.177241 0.028833 0.014464
6 LinkedList random 5 4.185596 0.029333 0.012727
7 LinkedList sorted 1 3.790176 0.025204 0.010269
8 LinkedList sorted 2 3.810435 0.022951 0.011524
9 LinkedList sorted 3 3.803720 0.025208 0.010396
10 LinkedList sorted 4 3.815409 0.027041 0.010837
11 LinkedList sorted 5 3.803349 0.025340 0.011777
12 HashTable random 1 0.010245 0.000075 0.000036
13 HashTable random 2 0.008733 0.000079 0.000069
14 HashTable random 3 0.013354 0.000094 0.000044
15 HashTable random 4 0.008903 0.000078 0.000036
16 HashTable random 5 0.009199 0.000072 0.000033
17 HashTable sorted 1 0.010286 0.000114 0.000052
18 HashTable sorted 2 0.009219 0.000073 0.000034
19 HashTable sorted 3 0.011302 0.000068 0.000033
20 HashTable sorted 4 0.009324 0.000068 0.000033
21 HashTable sorted 5 0.008641 0.000068 0.000034
22 BST random 1 0.027580 0.000190 0.000118
23 BST random 2 0.020693 0.000188 0.000116
24 BST random 3 0.020889 0.000190 0.000109
25 BST random 4 0.022945 0.000182 0.000110
26 BST random 5 0.022395 0.000207 0.000114
27 BST sorted 1 9.109235 0.083432 0.049594
28 BST sorted 2 9.177649 0.097374 0.050929
29 BST sorted 3 9.414714 0.067665 0.054041
30 BST sorted 4 9.062772 0.090823 0.048369
31 BST sorted 5 8.994138 0.072883 0.049921

View File

@ -0,0 +1,303 @@
import random
import time
import csv
import sys
import pandas as pd
import matplotlib.pyplot as plt
sys.setrecursionlimit(20000)
def ll_insert(head, name, phone):
current = head
while current is not None:
if current['name'] == name:
current['phone'] = phone
return head
current = current['next']
new_node = {'name': name, 'phone': phone, 'next': None}
if head is None:
return new_node
current = head
while current['next'] is not None:
current = current['next']
current['next'] = new_node
return head
def ll_find(head, name):
current = head
while current is not None:
if current['name'] == name:
return current['phone']
current = current['next']
return None
def ll_delete(head, name):
if head is None:
return None
if head['name'] == name:
return head['next']
prev = head
current = head['next']
while current is not None:
if current['name'] == name:
prev['next'] = current['next']
return head
prev = current
current = current['next']
return head
def ll_list_all(head):
records = []
current = head
while current is not None:
records.append((current['name'], current['phone']))
current = current['next']
records.sort(key=lambda x: x[0])
return records
HASH_SIZE = 997
def hash_func(name, size):
return hash(name) % size
def ht_create():
return [None] * HASH_SIZE
def ht_insert(table, name, phone):
idx = hash_func(name, len(table))
table[idx] = ll_insert(table[idx], name, phone)
return table
def ht_find(table, name):
idx = hash_func(name, len(table))
return ll_find(table[idx], name)
def ht_delete(table, name):
idx = hash_func(name, len(table))
table[idx] = ll_delete(table[idx], name)
return table
def ht_list_all(table):
all_records = []
for head in table:
current = head
while current is not None:
all_records.append((current['name'], current['phone']))
current = current['next']
all_records.sort(key=lambda x: x[0])
return all_records
def bst_create_node(name, phone):
return {'name': name, 'phone': phone, 'left': None, 'right': None}
def bst_insert(root, name, phone):
if root is None:
return bst_create_node(name, phone)
if name == root['name']:
root['phone'] = phone
elif name < root['name']:
root['left'] = bst_insert(root['left'], name, phone)
else:
root['right'] = bst_insert(root['right'], name, phone)
return root
def bst_find(root, name):
if root is None:
return None
if name == root['name']:
return root['phone']
elif name < root['name']:
return bst_find(root['left'], name)
else:
return bst_find(root['right'], name)
def bst_find_min(node):
while node['left'] is not None:
node = node['left']
return node
def bst_delete(root, name):
if root is None:
return None
if name < root['name']:
root['left'] = bst_delete(root['left'], name)
elif name > root['name']:
root['right'] = bst_delete(root['right'], name)
else:
if root['left'] is None:
return root['right']
if root['right'] is None:
return root['left']
min_node = bst_find_min(root['right'])
root['name'] = min_node['name']
root['phone'] = min_node['phone']
root['right'] = bst_delete(root['right'], min_node['name'])
return root
def bst_list_all(root):
result = []
def inorder(node):
if node is None:
return
inorder(node['left'])
result.append((node['name'], node['phone']))
inorder(node['right'])
inorder(root)
return result
def generate_records(num_records, seed=42):
random.seed(seed)
records = []
for i in range(1, num_records + 1):
name = f"User_{i:05d}"
phone = f"{random.randint(100,999)}-{random.randint(1000,9999)}"
records.append((name, phone))
return records
def prepare_datasets(base_records):
shuffled = base_records.copy()
random.shuffle(shuffled)
sorted_records = sorted(base_records, key=lambda x: x[0])
return shuffled, sorted_records
def run_experiment_for_structure(struct_funcs, records, mode_name, repeats=5):
results = []
for rep in range(repeats):
ds = struct_funcs['create']()
start = time.perf_counter()
for name, phone in records:
ds = struct_funcs['insert'](ds, name, phone)
insert_time = time.perf_counter() - start
existing_names = [rec[0] for rec in records]
sample_existing = random.sample(existing_names, 100)
nonexistent = [f"None_{i}" for i in range(10)]
search_names = sample_existing + nonexistent
random.shuffle(search_names)
start = time.perf_counter()
for name in search_names:
_ = struct_funcs['find'](ds, name)
find_time = time.perf_counter() - start
to_delete = random.sample(existing_names, 50)
start = time.perf_counter()
for name in to_delete:
ds = struct_funcs['delete'](ds, name)
delete_time = time.perf_counter() - start
results.append({
'structure': struct_funcs['name'],
'mode': mode_name,
'repetition': rep + 1,
'insert_time': insert_time,
'find_time': find_time,
'delete_time': delete_time
})
return results
def main_experiment():
N = 10000
REPEATS = 5
print("Генерация тестовых данных...")
base_records = generate_records(N)
shuffled_records, sorted_records = prepare_datasets(base_records)
print(f"Создано {N} записей. Случайный порядок и отсортированный готовы.")
structures = {
'LinkedList': {
'name': 'LinkedList',
'create': lambda: None,
'insert': ll_insert,
'find': ll_find,
'delete': ll_delete
},
'HashTable': {
'name': 'HashTable',
'create': ht_create,
'insert': ht_insert,
'find': ht_find,
'delete': ht_delete
},
'BST': {
'name': 'BST',
'create': lambda: None,
'insert': bst_insert,
'find': bst_find,
'delete': bst_delete
}
}
all_results = []
for struct_name, funcs in structures.items():
print(f"Тестирование {struct_name} на случайном порядке...")
all_results.extend(run_experiment_for_structure(funcs, shuffled_records, 'random', REPEATS))
print(f"Тестирование {struct_name} на отсортированном порядке...")
all_results.extend(run_experiment_for_structure(funcs, sorted_records, 'sorted', REPEATS))
csv_file = "experiment_results.csv"
with open(csv_file, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['Structure', 'Mode', 'Repeat', 'Insert (sec)', 'Search (sec)', 'Delete (sec)'])
for rec in all_results:
writer.writerow([
rec['structure'],
rec['mode'],
rec['repetition'],
f"{rec['insert_time']:.6f}",
f"{rec['find_time']:.6f}",
f"{rec['delete_time']:.6f}"
])
print(f"Результаты сохранены в {csv_file}")
plot_results(csv_file)
def plot_results(csv_path):
df = pd.read_csv(csv_path)
mean_times = df.groupby(['Structure', 'Mode'])[['Insert (sec)', 'Search (sec)', 'Delete (sec)']].mean().reset_index()
structures = mean_times['Structure'].unique()
modes = mean_times['Mode'].unique()
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
operations = ['Insert (sec)', 'Search (sec)', 'Delete (sec)']
titles = ['Вставка', 'Поиск', 'Удаление']
for ax, op, title in zip(axes, operations, titles):
x = range(len(structures))
width = 0.35
random_vals = []
sorted_vals = []
for s in structures:
rand_row = mean_times[(mean_times['Structure'] == s) & (mean_times['Mode'] == 'random')]
sort_row = mean_times[(mean_times['Structure'] == s) & (mean_times['Mode'] == 'sorted')]
random_vals.append(rand_row[op].values[0] if not rand_row.empty else 0)
sorted_vals.append(sort_row[op].values[0] if not sort_row.empty else 0)
ax.bar([i - width/2 for i in x], random_vals, width, label='Случайный порядок')
ax.bar([i + width/2 for i in x], sorted_vals, width, label='Отсортированный порядок')
ax.set_xticks(x)
ax.set_xticklabels(structures)
ax.set_ylabel('Время (секунды)')
ax.set_title(title)
ax.legend()
plt.tight_layout()
plt.savefig('performance_comparison.png', dpi=150)
plt.show()
print("График сохранён как performance_comparison.png")
if __name__ == "__main__":
main_experiment()

Binary file not shown.

View File

@ -0,0 +1,629 @@
import sys
from collections import deque
import heapq
import time
import os
import csv
import matplotlib.pyplot as plt
import numpy as np
# ----------------------------- Модель клетки -----------------------------
class GridCell:
def __init__(self, x, y):
self._x = x
self._y = y
self._blocked = False
self._entry = False
self._exit_flag = False
@property
def x(self):
return self._x
@property
def y(self):
return self._y
@property
def is_wall(self):
return self._blocked
@is_wall.setter
def is_wall(self, value):
self._blocked = value
@property
def is_start(self):
return self._entry
@is_start.setter
def is_start(self, value):
self._entry = value
@property
def is_exit(self):
return self._exit_flag
@is_exit.setter
def is_exit(self, value):
self._exit_flag = value
def passable(self):
return not self._blocked
# ----------------------------- Модель лабиринта -----------------------------
class Labyrinth:
def __init__(self, width, height):
self._width = width
self._height = height
self._cells = [[GridCell(x, y) for x in range(width)] for y in range(height)]
self._start_cell = None
self._exit_cell = None
@property
def width(self):
return self._width
@property
def height(self):
return self._height
@property
def start(self):
return self._start_cell
@property
def exit(self):
return self._exit_cell
def cell_at(self, x, y):
if 0 <= x < self._width and 0 <= y < self._height:
return self._cells[y][x]
return None
def configure_cell(self, x, y, cell_type):
cell = self.cell_at(x, y)
if cell is None:
return
if cell_type == 'wall':
cell.is_wall = True
elif cell_type == 'start':
if self._start_cell:
self._start_cell.is_start = False
cell.is_start = True
cell.is_wall = False
self._start_cell = cell
elif cell_type == 'exit':
if self._exit_cell:
self._exit_cell.is_exit = False
cell.is_exit = True
cell.is_wall = False
self._exit_cell = cell
elif cell_type == 'path':
cell.is_wall = False
def adjacent_cells(self, cell):
neighbours = []
directions = [(0, -1), (0, 1), (-1, 0), (1, 0)]
for dx, dy in directions:
nx, ny = cell.x + dx, cell.y + dy
neighbour = self.cell_at(nx, ny)
if neighbour and neighbour.passable():
neighbours.append(neighbour)
return neighbours
# ----------------------------- Загрузка лабиринта -----------------------------
class LabyrinthBuilder:
def build_from_file(self, filename):
raise NotImplementedError
class TxtLabyrinthBuilder(LabyrinthBuilder):
def build_from_file(self, filename):
with open(filename, 'r') as f:
lines = [line.rstrip('\n') for line in f.readlines()]
height = len(lines)
width = max(len(line) for line in lines) if height > 0 else 0
start_cnt = 0
exit_cnt = 0
lab = Labyrinth(width, height)
for y, line in enumerate(lines):
for x, ch in enumerate(line):
if ch == "#":
lab.configure_cell(x, y, "wall")
elif ch == "S":
lab.configure_cell(x, y, "start")
start_cnt += 1
elif ch == "E":
lab.configure_cell(x, y, "exit")
exit_cnt += 1
else:
lab.configure_cell(x, y, 'path')
if start_cnt != 1 or exit_cnt != 1:
raise ValueError(f"Maze must have exactly one S and one E. Found S={start_cnt}, E={exit_cnt}")
return lab
# ----------------------------- Алгоритмы поиска -----------------------------
class SearchAlgorithm:
def compute_path(self, maze, start, goal):
raise NotImplementedError
def _build_path(self, came_from, start, goal):
path = []
cur = goal
while cur is not None:
path.append(cur)
cur = came_from.get(cur)
path.reverse()
return path
def visited_nodes(self):
return getattr(self, '_visited', 0)
class BFS(SearchAlgorithm):
def compute_path(self, maze, start, goal):
q = deque()
q.append(start)
came_from = {start: None}
visited = {start}
while q:
cur = q.popleft()
if cur == goal:
self._visited = len(visited)
return self._build_path(came_from, start, goal)
for nb in maze.adjacent_cells(cur):
if nb not in visited:
visited.add(nb)
came_from[nb] = cur
q.append(nb)
self._visited = len(visited)
return []
class DFS(SearchAlgorithm):
def compute_path(self, maze, start, goal):
stack = [start]
came_from = {start: None}
visited = {start}
while stack:
cur = stack.pop()
if cur == goal:
self._visited = len(visited)
return self._build_path(came_from, start, goal)
for nb in maze.adjacent_cells(cur):
if nb not in visited:
visited.add(nb)
came_from[nb] = cur
stack.append(nb)
self._visited = len(visited)
return []
class AStar(SearchAlgorithm):
def _heuristic(self, cell, goal):
return abs(cell.x - goal.x) + abs(cell.y - goal.y)
def compute_path(self, maze, start, goal):
heap = []
counter = 0
start_f = self._heuristic(start, goal)
heapq.heappush(heap, (start_f, counter, start))
counter += 1
came_from = {}
g_score = {start: 0}
f_score = {start: start_f}
visited = set()
while heap:
cur_f, _, cur = heapq.heappop(heap)
visited.add(cur)
if cur == goal:
self._visited = len(visited)
return self._build_path(came_from, start, goal)
if cur_f > f_score.get(cur, float('inf')):
continue
for nb in maze.adjacent_cells(cur):
tentative_g = g_score[cur] + 1
if tentative_g < g_score.get(nb, float('inf')):
came_from[nb] = cur
g_score[nb] = tentative_g
new_f = tentative_g + self._heuristic(nb, goal)
f_score[nb] = new_f
heapq.heappush(heap, (new_f, counter, nb))
counter += 1
self._visited = len(visited)
return []
# ----------------------------- Оркестратор -----------------------------
class Pathfinder:
def __init__(self, maze):
self._maze = maze
self._algorithm = None
self._listeners = []
def attach(self, listener):
self._listeners.append(listener)
def notify(self, event, data):
for lst in self._listeners:
lst.update(event, data)
def set_algorithm(self, algorithm):
self._algorithm = algorithm
def solve(self):
if self._algorithm is None:
return None
t0 = time.perf_counter()
path = self._algorithm.compute_path(self._maze, self._maze.start, self._maze.exit)
t1 = time.perf_counter()
elapsed_ms = (t1 - t0) * 1000
self.notify("path_found", path)
return PerformanceData(elapsed_ms, self._algorithm.visited_nodes(), len(path))
class PerformanceData:
def __init__(self, time_ms, visited, length):
self.time_ms = time_ms
self.visited_cells = visited
self.path_length = length
# ----------------------------- Наблюдатель и отображение -----------------------------
class EventListener:
def update(self, event_type, data):
raise NotImplementedError
class ConsoleDisplay(EventListener):
def __init__(self, walker=None):
self._last_path = None
self._walker = walker
def update(self, event_type, data):
if event_type == "maze_loaded":
self._render_maze(data)
elif event_type == "path_found":
self._last_path = data
self._render_path(data)
elif event_type == "player_moved":
self._render_maze_with_player(data)
def _render_maze(self, maze):
os.system('cls' if os.name == 'nt' else 'clear')
print("=" * (maze.width * 2 + 4))
print(" LABYRINTH")
print("=" * (maze.width * 2 + 4))
for y in range(maze.height):
print(" ", end='')
for x in range(maze.width):
cell = maze.cell_at(x, y)
if cell == maze.start:
print('S', end=' ')
elif cell == maze.exit:
print('E', end=' ')
elif cell.is_wall:
print('#', end=' ')
else:
print('.', end=' ')
print()
print("=" * (maze.width * 2 + 4))
print(" S - start E - exit # - wall . - path")
def _render_maze_with_player(self, maze):
os.system('cls' if os.name == 'nt' else 'clear')
print("=" * (maze.width * 2 + 4))
print(" LABYRINTH (P - player)")
print("=" * (maze.width * 2 + 4))
for y in range(maze.height):
print(" ", end='')
for x in range(maze.width):
cell = maze.cell_at(x, y)
if self._walker and cell == self._walker.current:
print('P', end=' ')
elif cell == maze.start:
print('S', end=' ')
elif cell == maze.exit:
print('E', end=' ')
elif cell.is_wall:
print('#', end=' ')
else:
print('.', end=' ')
print()
print("=" * (maze.width * 2 + 4))
print(f" Player position: ({self._walker.current.x}, {self._walker.current.y})")
print(" S - start E - exit # - wall . - path P - player")
def _render_path(self, path):
if not path:
print("\n Path not found!")
return
print(f"\n Path found! Length: {len(path)}")
# ----------------------------- Игрок и команды -----------------------------
class Walker:
def __init__(self, start_cell, lab):
self._current = start_cell
self._previous = None
self._labyrinth = lab
@property
def current(self):
return self._current
def move_to(self, cell):
if cell and cell.passable():
self._previous = self._current
self._current = cell
return True
return False
def undo_move(self):
if self._previous:
self._current, self._previous = self._previous, None
return True
return False
class Action:
def execute(self):
raise NotImplementedError
def undo(self):
raise NotImplementedError
class MoveAction(Action):
def __init__(self, walker, direction, lab):
self._walker = walker
self._dx, self._dy = direction
self._lab = lab
self._executed = False
def execute(self):
new_x = self._walker.current.x + self._dx
new_y = self._walker.current.y + self._dy
target = self._lab.cell_at(new_x, new_y)
if target and target.passable():
self._walker.move_to(target)
self._executed = True
return True
return False
def undo(self):
if self._executed:
self._walker.undo_move()
self._executed = False
return True
return False
# ----------------------------- Эксперименты и статистика -----------------------------
def run_benchmark(maze_file, algorithm, runs=5):
builder = TxtLabyrinthBuilder()
maze = builder.build_from_file(maze_file)
total_time = 0.0
total_visited = 0
total_length = 0
for _ in range(runs):
solver = Pathfinder(maze)
solver.set_algorithm(algorithm)
stats = solver.solve()
if stats:
total_time += stats.time_ms
total_visited += stats.visited_cells
total_length += stats.path_length
return {
'time_ms': total_time / runs,
'visited_cells': total_visited / runs,
'path_length': total_length / runs
}
def generate_charts(results):
mazes = list(set(r['maze'] for r in results))
alg_names = ['BFS', 'DFS', 'AStar']
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
x = np.arange(len(mazes))
width = 0.25
for i, alg in enumerate(alg_names):
times = []
for m in mazes:
val = next((r['time_ms'] for r in results if r['maze'] == m and r['strategy'] == alg), 0)
times.append(val)
axes[0].bar(x + i * width, times, width, label=alg)
axes[0].set_xlabel('Maze')
axes[0].set_ylabel('Time (ms)')
axes[0].set_title('Execution Time')
axes[0].set_xticks(x + width)
axes[0].set_xticklabels(mazes, rotation=45, ha='right')
axes[0].legend()
axes[0].grid(True, alpha=0.3)
for i, alg in enumerate(alg_names):
visited = []
for m in mazes:
val = next((r['visited_cells'] for r in results if r['maze'] == m and r['strategy'] == alg), 0)
visited.append(val)
axes[1].bar(x + i * width, visited, width, label=alg)
axes[1].set_xlabel('Maze')
axes[1].set_ylabel('Visited Cells')
axes[1].set_title('Visited Nodes')
axes[1].set_xticks(x + width)
axes[1].set_xticklabels(mazes, rotation=45, ha='right')
axes[1].legend()
axes[1].grid(True, alpha=0.3)
for i, alg in enumerate(alg_names):
lengths = []
for m in mazes:
val = next((r['path_length'] for r in results if r['maze'] == m and r['strategy'] == alg), 0)
lengths.append(val)
axes[2].bar(x + i * width, lengths, width, label=alg)
axes[2].set_xlabel('Maze')
axes[2].set_ylabel('Path Length')
axes[2].set_title('Optimality')
axes[2].set_xticks(x + width)
axes[2].set_xticklabels(mazes, rotation=45, ha='right')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('maze_benchmark.png', dpi=150, bbox_inches='tight')
plt.show()
def run_experiments():
test_mazes = [
("maze/level1.txt", "Small 10x6"),
("maze/medium10x10.txt", "Medium 10x10"),
("maze/large20x20.txt", "Large 20x20"),
("maze/empty15x15.txt", "Empty 15x15"),
("maze/no_exit10x10.txt", "No exit 10x10")
]
algorithms = [
("BFS", BFS()),
("DFS", DFS()),
("AStar", AStar())
]
results = []
for filepath, display_name in test_mazes:
print(f"Testing {display_name}...")
for alg_name, alg_obj in algorithms:
try:
stats = run_benchmark(filepath, alg_obj, runs=3)
results.append({
'maze': display_name,
'strategy': alg_name,
'time_ms': stats['time_ms'],
'visited_cells': stats['visited_cells'],
'path_length': stats['path_length']
})
print(f" {alg_name}: time={stats['time_ms']:.3f}ms, visited={stats['visited_cells']:.0f}, length={stats['path_length']:.0f}")
except Exception as e:
print(f" {alg_name}: ERROR - {e}")
results.append({
'maze': display_name,
'strategy': alg_name,
'time_ms': -1,
'visited_cells': -1,
'path_length': -1
})
valid = [r for r in results if r['time_ms'] >= 0]
if not valid:
print("No valid results to save.")
return
with open('maze_experiment.csv', 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['maze', 'strategy', 'time_ms', 'visited_cells', 'path_length'])
writer.writeheader()
writer.writerows(valid)
generate_charts(valid)
print("\nResults saved to maze_experiment.csv")
print("Plot saved to maze_benchmark.png")
def play_game():
builder = TxtLabyrinthBuilder()
maze = builder.build_from_file("maze/level1.txt")
walker = Walker(maze.start, maze)
view = ConsoleDisplay(walker)
view._render_maze(maze)
solver = Pathfinder(maze)
solver.attach(view)
print("\n CONTROLS:")
print(" H (left) J (down) K (up) L (right)")
print(" U - undo Q - quit")
print("\n AUTO SEARCH:")
print(" B - BFS D - DFS A - A*")
print("\n" + "=" * 50)
action_stack = []
while True:
cmd = input("\n Command > ").lower()
if cmd == 'q':
print("\n Goodbye!")
break
elif cmd == 'b':
solver.set_algorithm(BFS())
stats = solver.solve()
if stats:
print(f"\n BFS: time={stats.time_ms:.3f}ms, visited={stats.visited_cells}, length={stats.path_length}")
elif cmd == 'd':
solver.set_algorithm(DFS())
stats = solver.solve()
if stats:
print(f"\n DFS: time={stats.time_ms:.3f}ms, visited={stats.visited_cells}, length={stats.path_length}")
elif cmd == 'a':
solver.set_algorithm(AStar())
stats = solver.solve()
if stats:
print(f"\n A*: time={stats.time_ms:.3f}ms, visited={stats.visited_cells}, length={stats.path_length}")
elif cmd in ['h', 'j', 'k', 'l']:
dir_map = {'h': (-1, 0), 'l': (1, 0), 'k': (0, -1), 'j': (0, 1)}
action = MoveAction(walker, dir_map[cmd], maze)
if action.execute():
action_stack.append(action)
view._render_maze_with_player(maze)
if walker.current == maze.exit:
print("\n CONGRATULATIONS! YOU FOUND THE EXIT!")
print(f" Total moves: {len(action_stack)}")
break
else:
print("\n Cannot go there! It's a wall.")
elif cmd == 'u':
if action_stack:
last = action_stack.pop()
last.undo()
view._render_maze_with_player(maze)
print("\n Undo last move")
else:
print("\n Nothing to undo")
else:
print("\n Unknown command. Use h,j,k,l to move, u to undo, q to quit")
print("\n Game over. Thanks for playing!")
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] in ('experiment', 'benchmark'):
run_experiments()
else:
play_game()

View File

@ -0,0 +1,15 @@
###############
#S #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# #
# E#
###############

View File

@ -0,0 +1,21 @@
####################
#S #
# ### ##### ##### ##
# # # # # #
### # # ### ### # # #
# # # # # # #
# ### ### # # ### ###
# # # # #
##### ### # ####### #
# # # # #
# ### # ### ### # # #
# # # # # # #
# # ### ### # # ### #
# # # # #
# # ######### # ### #
# # # # # #
# ### # ### # # # # #
# # # # # #
### ### # ### # # ###
# E #
####################

View File

@ -0,0 +1,6 @@
##########
#S #
# ### ####
# # #
# # E#
##########

View File

@ -0,0 +1,10 @@
##########
#S #
# ### ### #
# # # #
### # ### #
# # #
# ### ### #
# # #
# ### ###E#
##########

View File

@ -0,0 +1,11 @@
##########
#S #
# ### ### #
# # # #
### # ### #
# # #
# ### ### #
# # #
# ### ### #
########E #
##########

Binary file not shown.

View File

@ -0,0 +1,16 @@
maze,strategy,time_ms,visited_cells,path_length
Small 10x6,BFS,0.031851000433865316,24.0,11.0
Small 10x6,DFS,0.01671833342697937,17.0,11.0
Small 10x6,AStar,0.06431333319293724,24.0,11.0
Medium 10x10,BFS,0.04361866679876888,42.0,16.0
Medium 10x10,DFS,0.024233000052239124,26.0,16.0
Medium 10x10,AStar,0.06044533317132542,30.0,16.0
Large 20x20,BFS,0.24542399993758104,211.0,36.0
Large 20x20,DFS,0.2113953335841264,170.0,100.0
Large 20x20,AStar,0.2638656663596824,103.0,36.0
Empty 15x15,BFS,0.19875599991792114,169.0,25.0
Empty 15x15,DFS,0.12158433310105465,169.0,97.0
Empty 15x15,AStar,0.4113716665112103,169.0,25.0
No exit 10x10,BFS,0.0542050001968164,45.0,18.0
No exit 10x10,DFS,0.029572332702324882,28.0,18.0
No exit 10x10,AStar,0.08293900009448407,35.0,18.0
Internal Server Error - DRE.lab repo

Internal Server Error

Gitea Version: 1.22.0