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

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

imperfect_maze.py

grid[row][randrange(2, x_max - 1)] = False for row in range(4, y_max - 1, 4):

grid[row][x_max - 2] = False grid[row][randrange(1, x_max - 2)] = False

return grid

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

"""

Small rooms 2D imperfect maze generation algorithm :param y_max: height

:param x_max: width

:return: 2D imperfect 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)] for y in range(1, y_max - 1):

if y % 2:

for x in range(1, x_max - 3, 4): grid[y][x] = False

grid[y][x + 1] = False grid[y][x + 2] = False

if x < x_max - 4 and not randrange(3): grid[y][x + 3] = False

else:

for x in range(2, x_max - 1, 4): grid[y][x] = False

y_mid = y_max // 2

for x in range(1, x_max - 1): grid[y_mid][x] = False

return grid

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

"""

Spiral 2D imperfect maze generation algorithm :param y_max: height

:param x_max: width

:return: 2D imperfect 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), (0, 2), (2, 0), (0, -2)]

if randrange(2): directions.reverse()

current = (1, 1) grid[1][1] = False next_dir = 0

while True:

y, x = current

new_y, new_x = (y + directions[next_dir][0], x + directions[next_dir][1]) 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 if grid[y][x]] if (new_y, new_x) in neighbors:

grid[(y + new_y) // 2][(x + new_x) // 2] = False grid[new_y][new_x] = False

current = (new_y, new_x) elif not neighbors:

break else:

next_dir = (next_dir + 1) % 4 for i in range(max(y_max, x_max)):

grid[randrange(1, y_max - 1)][randrange(1, x_max - 1)] = False return grid

def main():

from timeit import timeit

def print_maze(maze):

56

imperfect_maze.py

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

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)

functions = [serpentine, small_rooms, spiral]

n_functions = ['Serpentine algorithm', 'Small rooms algorithm', 'Spiral algorithm'] time_functions = [0 for _ in n_functions]

print('== Imperfect maze ===')

width = (get_input('Enter the width of the maze (odd number)', 5, 100000) // 2) * 2 + 1 height = (get_input('Enter the height of the maze (odd number)', 5, 100000) // 2) * 2 + 1 for n, func in zip(n_functions, functions):

print(n) print_maze(func(height, width)) input('next >>')

print('\n', '=== Time test ===', 'Tests: 15', 'Sizes: 11-41', sep='\n') for i in range(15):

y, x = (randrange(10, 40) // 2) * 2 + 1, (randrange(10, 40) // 2) * 2 + 1 for j, func in enumerate(functions):

name = func.__name__

time_functions[j] += timeit(f"{name}({y}, {x})", f"from __main__ import {name}", number=15) for time, n in sorted(zip(time_functions, n_functions)):

print(n + ': ' + str(time)) input()

if __name__ == '__main__': main()

Таблица 28. Файл с алгоритмами поиска кратчайших путей в лабиринтах

solve_maze.py

#!/usr/bin/env python3

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

"""

Implementation of algorithms for finding the shortest paths in 2D mazes. Algorithms:

-A* algorithm

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

-Dijkstra algorithm

"""

import heapq

from collections import deque from math import inf

from typing import Callable, List, Tuple

class Heuristic:

"""

A class with basic heuristics for the A * algorithm

"""

@staticmethod

def octile(y0, x0, y1, x1):

(ty, tx) = (abs(y0 - y1), abs(x0 - x1))

return max(ty, tx) + (2 ** 0.5 - 1) * min(ty, tx)

@staticmethod

def manhattan(y0, x0, y1, x1):

return abs(y0 - y1) + abs(x0 - x1)

@staticmethod

def chebyshev(y0, x0, y1, x1):

return max(abs(y0 - y1), abs(x0 - x1))

57

solve_maze.py

@staticmethod

def euclidean(y0, x0, y1, x1):

return ((y0 - y1) ** 2 + (x0 - x1) ** 2) ** 0.5

def a_star(maze: List[List[bool]], beginNode: Tuple[int, int], endNode: Tuple[int, int], heuristic: Callable = Heuristic.manhattan) -> List[Tuple[int, int]]:

"""

A* algorithm

This implementation uses heapq - a priority queue. :param maze: 2D maze grid

:param beginNode: initial position :param endNode: target position :param heuristic: heuristic function

:return: path from initial to target position

"""

current = (-1, -1)

(y_max, x_max) = (len(maze), len(maze[0])) q = []

cost = 0

heapq.heappush(q, (cost, endNode))

directions = ((-1, 0), (0, -1), (1, 0), (0, 1)) parentNode = {endNode: endNode}

costMap = {endNode: cost} while q:

current = heapq.heappop(q)[1] if current == beginNode:

break

cost = costMap[current] + 1 for y_dir, x_dir in directions:

(dy, dx) = (current[0] + y_dir, current[1] + x_dir)

if dy >= y_max or dy < 0 or dx >= x_max or dx < 0 or maze[dy][dx]: continue

neighbor = (dy, dx)

if cost < costMap.get(neighbor, inf): costMap[neighbor] = cost parentNode[neighbor] = current

heapq.heappush(q, (cost + heuristic(current[0], current[1], beginNode[0], beginNode[1]),

neighbor))

path = list()

if current == beginNode: path.append(current) while current != endNode:

current = parentNode[current] path.append(current)

return path

def bfs(maze: List[List[bool]], beginNode: Tuple[int, int], endNode: Tuple[int, int]) -> List[Tuple[int, int]]:

"""

Breadth-first search algorithm (iterative version)

(!) This implementation uses a deque instead of a queue. This choice is only related to the speed. :param maze: 2D maze grid

:param beginNode: initial position :param endNode: target position

:return: path from initial to target position

"""

current = (-1, -1)

(y_max, x_max) = (len(maze), len(maze[0])) q = deque()

q.append(endNode)

directions = ((-1, 0), (0, -1), (1, 0), (0, 1)) parentNode = {endNode: endNode}

while q:

current = q.popleft() if current == beginNode:

break

for y_dir, x_dir in directions:

58

solve_maze.py

(dy, dx) = (current[0] + y_dir, current[1] + x_dir)

if dy >= y_max or dy < 0 or dx >= x_max or dx < 0 or maze[dy][dx]: continue

neighbor = (dy, dx)

if neighbor not in parentNode: q.append(neighbor) parentNode[neighbor] = current

path = list()

if current == beginNode: path.append(current) while current != endNode:

current = parentNode[current] path.append(current)

return path

def dijkstra(maze: List[List[bool]], beginNode: Tuple[int, int], endNode: Tuple[int, int]) -> List[Tuple[int, int]]:

"""

Dijkstra algorithm

This implementation uses heapq - a priority queue. :param maze: 2D maze grid

:param beginNode: initial position :param endNode: target position

:return: path from initial to target position

"""

current = (-1, -1)

(y_max, x_max) = (len(maze), len(maze[0])) q = []

heapq.heappush(q, (0, endNode))

directions = ((-1, 0), (0, -1), (1, 0), (0, 1)) parentNode = {endNode: endNode}

costMap = {endNode: 0} while q:

current = heapq.heappop(q)[1] if current == beginNode:

break

cost = costMap[current] + 1 for y_dir, x_dir in directions:

(dy, dx) = (current[0] + y_dir, current[1] + x_dir)

if dy >= y_max or dy < 0 or dx >= x_max or dx < 0 or maze[dy][dx]: continue

neighbor = (dy, dx)

if cost < costMap.get(neighbor, inf): costMap[neighbor] = cost parentNode[neighbor] = current heapq.heappush(q, (cost, neighbor))

path = list()

if current == beginNode: path.append(current) while current != endNode:

current = parentNode[current] path.append(current)

return path

Таблица 29. Файл с выводом результата работы файла-программы test_maze.py

Вывод программы

=== Perfect algorithms ===

Enter the number of tests (1-100000):100

Enter the minimum length of the maze (odd number) (5-100000):200 Enter the maximum length of the maze (odd number) (5-100000):200 Time of execution of maze generation algorithms

Sidewinder algorithm: 2.046875 Binary tree algorithm: 2.625 Division algorithm: 5.515625 Backtracking algorithm: 11.046875 Eller's algorithm: 15.609375

59

Prim's algorithm (modified): 29.890625 Aldous-Broder algorithm: 165.359375 Prim's algorithm: 180.84375

Kruskal's algorithm: 283.0 Growing tree algorithm: 385.71875 Wilson's algorithm: 525.765625

Time of execution of algorithms to find the shortest paths in the mazes (briefly) BFS algorithm: 41.546875

A* algorithm (Manhattan heuristic): 53.65625 A* algorithm (Octile heuristic): 63.8125

A* algorithm (Chebyshev heuristic): 64.265625 A* algorithm (Euclidean heuristic): 70.34375 Dijkstra algorithm: 70.453125

Time of execution of algorithms to find the shortest paths in the mazes (in detail)

##############################

Function: BFS algorithm Kruskal's algorithm: 3.5625 Prim's algorithm: 3.578125 Sidewinder algorithm: 3.578125 Aldous-Broder algorithm: 3.625 Eller's algorithm: 3.640625 Backtracking algorithm: 3.65625 Wilson's algorithm: 3.8125 Growing tree algorithm: 3.9375 Binary tree algorithm: 3.984375 Division algorithm: 4.0

Prim's algorithm (modified): 4.171875

##############################

Function: A* algorithm (Manhattan heuristic) Prim's algorithm: 2.796875

Prim's algorithm (modified): 3.140625 Sidewinder algorithm: 4.171875

Binary tree algorithm: 4.265625 Wilson's algorithm: 4.71875 Growing tree algorithm: 4.8125 Kruskal's algorithm: 4.90625 Eller's algorithm: 5.5625 Aldous-Broder algorithm: 6.140625 Backtracking algorithm: 6.359375 Division algorithm: 6.78125

##############################

Function: A* algorithm (Octile heuristic) Prim's algorithm: 3.828125

Prim's algorithm (modified): 4.453125 Sidewinder algorithm: 5.3125

Binary tree algorithm: 5.4375 Wilson's algorithm: 5.46875 Kruskal's algorithm: 5.59375 Growing tree algorithm: 5.6875 Eller's algorithm: 6.46875 Aldous-Broder algorithm: 6.78125 Backtracking algorithm: 7.21875 Division algorithm: 7.5625

##############################

Function: A* algorithm (Chebyshev heuristic) Prim's algorithm: 4.21875

Prim's algorithm (modified): 4.734375 Binary tree algorithm: 5.4375 Sidewinder algorithm: 5.609375 Growing tree algorithm: 5.78125 Kruskal's algorithm: 5.78125

Wilson's algorithm: 5.828125 Eller's algorithm: 6.3125 Backtracking algorithm: 6.65625 Aldous-Broder algorithm: 6.671875 Division algorithm: 7.234375

##############################

Function: A* algorithm (Euclidean heuristic) Prim's algorithm: 4.234375

Prim's algorithm (modified): 4.9375 Binary tree algorithm: 5.8125

60

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