Материал: АиСД. Проектная работа

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

5. КОД ПРОГРАММЫ

Далее в таблицах содержится код файлов программы:

test_maze.py. Главный файл (файл запуска).

Данный файл запускается для тестирования всех алгоритмов.

Тип окна: окно командной строки интерпретатора. Ввод: с клавиатуры.

Вывод: в командную строку интерпретатора.

Как работает: вводится количество тестов, минимальные и максимальные размеры лабиринтов для алгоритмов генерации идеальных и неидеальных лабиринтов, выводятся результаты тестирования алгоритмов генерации и алгоритмов поиска кратчайших путей.

perfect_maze.py. Содержит алгоритмы генерации идеальных лабиринтов.

Данный файл может запускаться для отображения примеров идеальных лабиринтов и тестирования на скорость алгоритмов генерации идеальных лабиринтов.

Тип окна: окно командной строки интерпретатора. Ввод: с клавиатуры, Вывод: в командную строку интерпретатора.

Как работает: вводятся размеры лабиринта, далее выводятся сгенерированные лабиринты всех алгоритмов файла для этих размеров, потом вводится количество тестов, минимальные и максимальные размеры лабиринтов для тестирования на скорость алгоритмов генерации лабиринтов.

imperfect_maze.py. Содержит алгоритмы генерации неидеальных

лабиринтов.

Данный файл может запускаться для отображения примеров неидеальных лабиринтов и тестирования на скорость алгоритмов генерации неидеальных лабиринтов.

Тип окна: окно командной строки интерпретатора. Ввод: с клавиатуры, Вывод: в командную строку интерпретатора.

Как работает: вводятся размеры лабиринта, далее выводятся сгенерированные лабиринты всех алгоритмов файла для этих размеров, потом вводится количество тестов, минимальные и максимальные размеры лабиринтов для тестирования на скорость алгоритмов генерации лабиринтов.

solve_maze.py. Содержит алгоритмы поиска кратчайших путей в

лабиринтах.

Данный файл не следует запускать напрямую (ничего не будет происходить).

Все файлы необходимо располагать в одной папке.

Пример того, что может выводить файл-программа test_maze.py, можно посмотреть в таблице вывода после кода всех четырёх файлов.

46

Таблица 25. Код главного файла

test_maze.py

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

"""

Testing shortest path search algorithms in mazes 2D perfect maze generation algorithms:

-Aldous-Broder algorithm (unbiased maze)

-Wilson's algorithm (unbiased maze)

-Backtracking (depth-first search, iterative version)

-Binary tree algorithm

-Division algorithm

-Eller's algorithm

-Growing tree algorithm

-Kruskal's algorithm

-Prim's algorithm

-Prim's algorithm (modified)

-Sidewinder algorithm

2D imperfect maze generation algorithms:

-Serpentine algorithm

-Small rooms algorithm

-Spiral algorithm

Algorithms for finding the shortest paths in 2D mazes:

-A* algorithm (4 heuristics)

-BFS (breadth-first search) (iterative version)

-Dijkstra algorithm

"""

from imperfect_maze import * from perfect_maze import * from solve_maze import *

import gc as garbage_collector

from random import randint, randrange from time import process_time

from typing import Callable, List

def test_algorithms(n_tests: int, min_length: int, max_length: int, maze_functions: List[Callable], maze_n_functions: List[str], solve_maze_functions: List[Callable], solve_maze_n_functions: List[str], *, is_perfect_mazes: bool):

maze_time_functions = [0 for _ in maze_functions]

solve_maze_time_functions = [[0 for _ in maze_n_functions] for _ in solve_maze_n_functions] for i in range(n_tests):

y_size, x_size = (randint(min_length, max_length) // 2) * 2 + 1, ( randint(min_length, max_length) // 2) * 2 + 1

for j, func in enumerate(maze_functions): garbage_collector.disable() maze_time_functions[j] -= process_time() maze = func(y_size, x_size) maze_time_functions[j] += process_time() garbage_collector.enable()

(yFrom, xFrom, yTo, xTo) = 0, 0, 0, 0

while maze[yFrom][xFrom] or maze[yTo][xTo] or (yFrom, xFrom) == (yTo, xTo): yFrom = randrange(y_size)

xFrom = randrange(x_size) yTo = randrange(y_size) xTo = randrange(x_size)

solve_results = []

for k, solve_func in enumerate(solve_maze_functions): garbage_collector.disable() solve_maze_time_functions[k][j] -= process_time() result = solve_func(maze, (yFrom, xFrom), (yTo, xTo)) solve_maze_time_functions[k][j] += process_time() garbage_collector.enable() solve_results.append(result)

if is_perfect_mazes and not solve_results[k]: print('Invalid answer or imperfect maze')

print(*[''.join('█' if col else ' ' for col in row) for row in maze], sep='\n') print((yFrom, xFrom), (yTo, xTo))

47

test_maze.py

print(solve_maze_n_functions[k], solve_results[k]) raise AttributeError

if is_perfect_mazes and k and solve_results[k - 1] != solve_results[k] \

or not is_perfect_mazes and len(solve_results[k - 1]) != len(solve_results[k]): print('Miscellaneous answers')

print(*[''.join('█' if col else ' ' for col in row) for row in maze], sep='\n') print((yFrom, xFrom), (yTo, xTo))

print(solve_maze_n_functions[k - 1], solve_results[k - 1]) print(solve_maze_n_functions[k], solve_results[k])

raise AttributeError

print('Time of execution of maze generation algorithms')

for time, n in sorted(zip(maze_time_functions, maze_n_functions)): print(n + ': ' + str(time))

print('\n' + 'Time of execution of algorithms to find the shortest paths in the mazes (briefly)') for time, sn in sorted(zip(solve_maze_time_functions, solve_maze_n_functions), key=lambda x:

sum(x[0])):

print(sn + ': ' + str(sum(time)))

print('\n' + 'Time of execution of algorithms to find the shortest paths in the mazes (in detail)') for time, sn in sorted(zip(solve_maze_time_functions, solve_maze_n_functions), key=lambda x:

sum(x[0])):

print('#' * 30 + '\n' + 'Function:', sn)

for n, t in sorted(zip(maze_n_functions, time), key=lambda x: x[1]): print(n + ': ' + str(t))

def main():

def get_input(string, begin, end): string += f" ({begin}-{end}):" input_result = input(string)

while not (input_result.isdecimal() and begin <= int(input_result) <= end): input_result = input(string)

return int(input_result)

print('=== Perfect algorithms ===')

n_tests = get_input('Enter the number of tests', 1, 100000)

min_length = get_input('Enter the minimum length of the maze (odd number)', 5, 100000) max_length = get_input('Enter the maximum length of the maze (odd number)', 5, 100000)

maze_functions = [aldous_broder, wilson, backtracking, binary_tree, division, eller, growing_tree, kruskal,

prim, modified_prim, sidewinder]

maze_n_functions = ['Aldous-Broder algorithm', 'Wilson\'s algorithm', 'Backtracking algorithm', 'Binary tree algorithm', 'Division algorithm', 'Eller\'s algorithm', 'Growing tree

algorithm',

'Kruskal\'s algorithm', 'Prim\'s algorithm', 'Prim\'s algorithm (modified)', 'Sidewinder algorithm']

solve_maze_functions = [bfs, dijkstra,

lambda mz, begin, end: a_star(mz, begin, end, Heuristic.octile), lambda mz, begin, end: a_star(mz, begin, end, Heuristic.manhattan), lambda mz, begin, end: a_star(mz, begin, end, Heuristic.chebyshev), lambda mz, begin, end: a_star(mz, begin, end, Heuristic.euclidean)]

solve_maze_n_functions = ['BFS algorithm', 'Dijkstra algorithm', 'A* algorithm (Octile heuristic)', 'A* algorithm (Manhattan heuristic)', 'A* algorithm (Chebyshev heuristic)', 'A* algorithm (Euclidean heuristic)']

test_algorithms(n_tests, min_length, max_length, maze_functions, maze_n_functions, solve_maze_functions,

solve_maze_n_functions, is_perfect_mazes=True)

print('\n', '=== Imperfect algorithms ===', sep='\n') n_tests = get_input('Enter the number of tests', 1, 100000)

min_length = get_input('Enter the minimum length of the maze (odd number)', 5, 100000) max_length = get_input('Enter the maximum length of the maze (odd number)', 5, 100000) maze_functions = [serpentine, small_rooms, spiral]

maze_n_functions = ['Serpentine algorithm', 'Small rooms algorithm', 'Spiral algorithm'] test_algorithms(n_tests, min_length, max_length, maze_functions, maze_n_functions,

solve_maze_functions,

solve_maze_n_functions, is_perfect_mazes=False)

input()

48

test_maze.py

if __name__ == '__main__': main()

Таблица 26. Файл с алгоритмами генерации идеальных лабиринтов

perfect_maze.py

#!/usr/bin/env python3

# -*- coding: utf-8 -*-

"""

Implementation of 2D perfect maze generation algorithms. Algorithms:

-Aldous-Broder algorithm (unbiased maze)

-Wilson's algorithm (unbiased maze)

-Backtracking (depth-first search, iterative version)

-Binary tree algorithm

-Division algorithm

-Eller's algorithm

-Growing tree algorithm

-Kruskal's algorithm

-Prim's algorithm

-Prim's algorithm (modified)

-Sidewinder algorithm

"""

from random import choice, random, randrange, sample, shuffle from typing import List

def aldous_broder(y_max: int, x_max: int) -> List[List[bool]]:

"""

Aldous-Broder 2D perfect maze generation algorithm (unbiased maze) :param y_max: height

:param x_max: width

:return: 2D perfect maze grid

"""

assert y_max % 2 and x_max % 2 and y_max >= 3 and x_max >= 3 grid = [[True for _ in range(x_max)] for _ in range(y_max)] directions = ((2, 0), (-2, 0), (0, 2), (0, -2))

(current_row, current_col) = (randrange(1, y_max, 2), randrange(1, x_max, 2)) grid[current_row][current_col] = False

num_visited = 1

max_visited = ((y_max - 1) // 2 * (x_max - 1) // 2) while num_visited < max_visited:

neighbors = [(current_row + dy, current_col + dx) for dy, dx in directions]

valid_neighbors = [(y, x) for y, x in neighbors if 0 < y < y_max and 0 < x < x_max and grid[y][x]] if not valid_neighbors:

free_neighbors = [(y, x) for y, x in neighbors if 0 < y < y_max and 0 < x < x_max and not

grid[y][x]]

(current_row, current_col) = choice(free_neighbors) continue

shuffle(valid_neighbors)

for new_row, new_col in valid_neighbors: if grid[new_row][new_col]:

grid[new_row][new_col] = grid[(new_row + current_row) // 2][(new_col + current_col) // 2]

= False

(current_row, current_col) = (new_row, new_col) num_visited += 1

break

return grid

def wilson(y_max: int, x_max: int) -> List[List[bool]]:

"""

Wilson's 2D perfect maze generation algorithm (unbiased maze) :param y_max: height

:param x_max: width

:return: 2D perfect maze grid

"""

assert y_max % 2 and x_max % 2 and y_max >= 3 and x_max >= 3

49

perfect_maze.py

grid = [[True for _ in range(x_max)] for _ in range(y_max)] directions = ((2, 0), (-2, 0), (0, 2), (0, -2))

free = {(y, x) for y in range(1, y_max, 2) for x in range(1, x_max, 2)} (y, x) = (2 * randrange(y_max // 2) + 1, 2 * randrange(x_max // 2) + 1) grid[y][x] = False

free.remove((y, x)) while free:

y, x = key = sample(free, 1)[0] free.remove(key)

path = [key] grid[y][x] = False

neighbors = ((y + dy, x + dx) for dy, dx in directions)

neighbors = [(y, x) for y, x in neighbors if 0 < y < y_max and 0 < x < x_max] y, x = key = choice(neighbors)

while grid[y][x]:

grid[y][x] = grid[(y + path[-1][0]) // 2][(x + path[-1][1]) // 2] = False neighbors = ((y + dy, x + dx) for dy, dx in directions)

neighbors = [(y, x) for y, x in neighbors if 0 < y < y_max and 0 < x < x_max] neighbors.remove(path[-1])

free.remove(key) path.append(key)

y, x = key = choice(neighbors) if key in path:

last_key = path.pop() free.add(last_key) grid[last_key[0]][last_key[1]] = True for key in reversed(path):

free.add(key)

grid[key[0]][key[1]] = grid[(last_key[0] + key[0]) // 2][(last_key[1] + key[1]) // 2] =

True

last_key = key

else:

grid[(y + path[-1][0]) // 2][(x + path[-1][1]) // 2] = False return grid

def backtracking(y_max: int, x_max: int) -> List[List[bool]]:

"""

Iterative version of depth-first search 2D perfect maze generation algorithm :param y_max: height

:param x_max: width

:return: 2D perfect maze grid

"""

grid = [[True for _ in range(x_max)] for _ in range(y_max)] directions = [(0, -2), (0, 2), (-2, 0), (2, 0)]

stack = [(2 * randrange(y_max // 2) + 1, 2 * randrange(x_max // 2) + 1)] while stack:

y, x = stack.pop() grid[y][x] = False

neighbors = ((y + dy, x + dx) for dy, dx in directions)

neighbors = [(y, x) for y, x in neighbors if 0 < y < y_max and 0 < x < x_max and grid[y][x]] if len(neighbors) > 1:

stack.append((y, x)) if neighbors:

ny, nx = choice(neighbors)

grid[(y + ny) // 2][(x + nx) // 2] = False stack.append((ny, nx))

return grid

def binary_tree(y_max: int, x_max: int) -> List[List[bool]]:

"""

Binary tree 2D perfect maze generation algorithm :param y_max: height

:param x_max: width

:return: 2D perfect maze grid

"""

assert y_max % 2 and x_max % 2 and y_max >= 3 and x_max >= 3 grid = [[True for _ in range(x_max)] for _ in range(y_max)] grid[1][1] = False

50

Источник: https://studfile.net/preview/16380301/