{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.4"
},
"colab": {
"name": "kl_python_labirintus.ipynb",
"provenance": [],
"collapsed_sections": [],
"toc_visible": true,
"include_colab_link": true
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
""
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "rGY8IlGNNFA6",
"colab_type": "text"
},
"source": [
"# Labirintus\n",
"\n",
"##Labirintus készítés\n",
"12 klasszikus algoritmus választható generálására „tökéletes” mazes. \n",
"\n",
"\n",
"\n",
"http://weblog.jamisbuck.org/2011/1/3/maze-generation-kruskal-s-algorithm\n",
"http://weblog.jamisbuck.org/2011/1/10/maze-generation-prim-s-algorithm\n",
"http://weblog.jamisbuck.org/2010/12/27/maze-generation-recursive-backtracking\n",
"http://weblog.jamisbuck.org/2011/1/17/maze-generation-aldous-broder-algorithm\n",
"http://weblog.jamisbuck.org/2011/1/27/maze-generation-growing-tree-algorithm\n",
"http://weblog.jamisbuck.org/2011/1/24/maze-generation-hunt-and-kill-algorithm\n",
"http://weblog.jamisbuck.org/2011/1/20/maze-generation-wilson-s-algorithm\n",
"http://weblog.jamisbuck.org/2010/12/29/maze-generation-eller-s-algorithm\n",
"https://en.wikipedia.org/wiki/Maze_generation_algorithm#Cellular_automaton_algorithms\n",
"http://weblog.jamisbuck.org/2011/1/12/maze-generation-recursive-division-algorithm\n",
"http://weblog.jamisbuck.org/2011/2/3/maze-generation-sidewinder-algorithm\n",
"\n",
"\n",
"A labirintus tökéletes, ha van megoldása, és csak egy megoldás létezik. \n",
"\n",
"\n",
"Íme WEB-es algoritmus megvalósításra:\n",
"\n",
"http://www.astrolog.org/labyrnth/algrithm.htm\n",
"\n",
"Online python oktatás:\n",
"https://trinket.io/features/python3\n",
"https://repl.it/@prasanththangavel/Maze-Game\n",
"https://runestone.academy/runestone/books/published/pythonds/index.html"
]
},
{
"cell_type": "code",
"metadata": {
"id": "XKEgsO4XNFA8",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"outputId": "ce8b5ab5-6ff2-47b5-a15d-c996103b41e5"
},
"source": [
"import random\n",
"\n",
"# Ötlet \n",
"# https://scipython.com/blog/making-a-maze/ \n",
"\n",
"class Cell:\n",
" \"\"\"A cell in the maze.\n",
"\n",
" A maze \"Cell\" is a point in the grid which may be surrounded by walls to\n",
" the north, east, south or west.\n",
"\n",
" \"\"\"\n",
"\n",
" # A wall separates a pair of cells in the N-S or W-E directions.\n",
" wall_pairs = {'N': 'S', 'S': 'N', 'E': 'W', 'W': 'E'}\n",
"\n",
" def __init__(self, x, y):\n",
" \"\"\"Initialize the cell at (x,y). At first it is surrounded by walls.\"\"\"\n",
"\n",
" self.x, self.y = x, y\n",
" self.walls = {'N': True, 'S': True, 'E': True, 'W': True}\n",
"\n",
" def has_all_walls(self):\n",
" \"\"\"Does this cell still have all its walls?\"\"\"\n",
"\n",
" return all(self.walls.values())\n",
"\n",
" def knock_down_wall(self, other, wall):\n",
" \"\"\"Knock down the wall between cells self and other.\"\"\"\n",
"\n",
" self.walls[wall] = False\n",
" other.walls[Cell.wall_pairs[wall]] = False\n",
"\n",
"class Maze:\n",
" \"\"\"A Maze, represented as a grid of cells.\"\"\"\n",
"\n",
" def __init__(self, nx, ny, ix=0, iy=0):\n",
" \"\"\"Initialize the maze grid.\n",
" The maze consists of nx x ny cells and will be constructed starting\n",
" at the cell indexed at (ix, iy).\n",
"\n",
" \"\"\"\n",
"\n",
" self.nx, self.ny = nx, ny\n",
" self.ix, self.iy = ix, iy\n",
" self.maze_map = [[Cell(x, y) for y in range(ny)] for x in range(nx)]\n",
"\n",
" def cell_at(self, x, y):\n",
" \"\"\"Return the Cell object at (x,y).\"\"\"\n",
"\n",
" return self.maze_map[x][y]\n",
"\n",
" def __str__(self):\n",
" \"\"\"Return a (crude) string representation of the maze.\"\"\"\n",
"\n",
" maze_rows = ['-' * nx*2]\n",
" for y in range(ny):\n",
" maze_row = ['|']\n",
" for x in range(nx):\n",
" if self.maze_map[x][y].walls['E']:\n",
" maze_row.append(' |')\n",
" else:\n",
" maze_row.append(' ')\n",
" maze_rows.append(''.join(maze_row))\n",
" maze_row = ['|']\n",
" for x in range(nx):\n",
" if self.maze_map[x][y].walls['S']:\n",
" maze_row.append('-+')\n",
" else:\n",
" maze_row.append(' +')\n",
" maze_rows.append(''.join(maze_row))\n",
" return '\\n'.join(maze_rows)\n",
"\n",
" def write_svg(self, filename):\n",
" \"\"\"Write an SVG image of the maze to filename.\"\"\"\n",
"\n",
" aspect_ratio = self.nx / self.ny\n",
" # Pad the maze all around by this amount.\n",
" padding = 10\n",
" # Height and width of the maze image (excluding padding), in pixels\n",
" height = 500\n",
" width = int(height * aspect_ratio)\n",
" # Scaling factors mapping maze coordinates to image coordinates\n",
" scy, scx = height / ny, width / nx\n",
"\n",
" def write_wall(f, x1, y1, x2, y2):\n",
" \"\"\"Write a single wall to the SVG image file handle f.\"\"\"\n",
"\n",
" print(''\n",
" .format(x1, y1, x2, y2), file=f)\n",
"\n",
" # Write the SVG image file for maze\n",
" with open(filename, 'w') as f:\n",
" # SVG preamble and styles.\n",
" print('', file=f)\n",
" print('', file=f)\n",
"\n",
" def find_valid_neighbours(self, cell):\n",
" \"\"\"Return a list of unvisited neighbours to cell.\"\"\"\n",
"\n",
" delta = [('W', (-1,0)),\n",
" ('E', (1,0)),\n",
" ('S', (0,1)),\n",
" ('N', (0,-1))]\n",
" neighbours = []\n",
" for direction, (dx,dy) in delta:\n",
" x2, y2 = cell.x + dx, cell.y + dy\n",
" if (0 <= x2 < nx) and (0 <= y2 < ny):\n",
" neighbour = maze.cell_at(x2, y2)\n",
" if neighbour.has_all_walls():\n",
" neighbours.append((direction, neighbour))\n",
" return neighbours\n",
"\n",
" def make_maze(self):\n",
" # Total number of cells.\n",
" n = self.nx * self.ny\n",
" cell_stack = []\n",
" current_cell = self.cell_at(ix, iy)\n",
" # Total number of visited cells during maze construction.\n",
" nv = 1\n",
"\n",
" while nv < n:\n",
" neighbours = self.find_valid_neighbours(current_cell)\n",
"\n",
" if not neighbours:\n",
" # We've reached a dead end: backtrack.\n",
" current_cell = cell_stack.pop()\n",
" continue\n",
"\n",
" # Choose a random neighbouring cell and move to it.\n",
" direction, next_cell = random.choice(neighbours)\n",
" current_cell.knock_down_wall(next_cell, direction)\n",
" cell_stack.append(current_cell)\n",
" current_cell = next_cell\n",
" nv += 1\n",
"\n",
"# Maze dimensions (ncols, nrows)\n",
"nx, ny = 40, 40\n",
"# Maze entry position\n",
"ix, iy = 0, 0\n",
"\n",
"maze = Maze(nx, ny, ix, iy)\n",
"maze.make_maze()\n",
"\n",
"print(maze)\n",
"maze.write_svg('maze.svg')"
],
"execution_count": 1,
"outputs": [
{
"output_type": "stream",
"text": [
"--------------------------------------------------------------------------------\n",
"| | | | | | | | |\n",
"|-+ + +-+ + +-+ +-+-+-+-+ +-+-+ + +-+-+ +-+-+ + +-+-+-+ +-+-+ + + +-+-+-+ +-+ + +\n",
"| | | | | | | | | | | | | | | | | | | | | |\n",
"| +-+-+ +-+-+ +-+ + + + + +-+-+ +-+ + +-+-+ +-+ + +-+-+ + +-+-+ + + +-+-+-+ +-+ +\n",
"| | | | | | | | | | | | | | | | | | |\n",
"|-+-+-+-+ +-+-+ +-+-+-+ +-+-+ + + +-+ + +-+-+ +-+-+ +-+-+ +-+-+ + + + +-+-+-+ +-+\n",
"| | | | | | | | | | | | | | | | | | |\n",
"|-+-+ +-+-+-+ +-+ + + +-+-+ + +-+ +-+-+-+-+ +-+ + +-+ + + + +-+ + + +-+-+ +-+-+ +\n",
"| | | | | | | | | | | | | | | | | | |\n",
"| +-+ + +-+-+-+ +-+-+-+-+ + + +-+-+ +-+-+ +-+ +-+-+-+-+-+-+ + +-+-+-+ + +-+ + +-+\n",
"| | | | | | | | | | | | | | | | | | | |\n",
"| +-+-+-+-+-+ +-+-+ +-+ +-+ + + +-+-+ +-+ + +-+-+ + + + +-+-+-+ + + +-+-+ + +-+ +\n",
"| | | | | | | | | | | | | | | | | | | | |\n",
"| +-+ + + + + + + + + +-+ + +-+-+-+ + +-+-+ + +-+-+-+-+ + +-+-+-+-+-+-+ +-+ + +-+\n",
"| | | | | | | | | | | | | | | | | | | | | | | |\n",
"|-+-+ + + + +-+-+-+-+ +-+ + + +-+ + +-+ + +-+ +-+ + + + + + +-+ +-+-+ + + +-+-+ +\n",
"| | | | | | | | | | | | | | | | | | | | | | | | |\n",
"| +-+-+ + + + + + +-+ + +-+-+-+ +-+-+ + + +-+-+ + + + + + +-+ +-+ +-+ + + +-+-+-+\n",
"| | | | | | | | | | | | | | | | | | | | | |\n",
"| +-+ +-+-+-+ + + +-+-+ +-+-+-+-+-+ + + +-+-+ + +-+ + +-+-+ +-+ +-+ +-+-+-+-+-+ +\n",
"| | | | | | | | | | | | | | | | | | |\n",
"| + +-+ + +-+ + +-+-+ +-+-+ + + +-+-+ +-+ +-+ + +-+-+ + +-+-+ +-+-+-+-+ +-+ + +-+\n",
"| | | | | | | | | | | | | | | | | | | | |\n",
"| + + + + +-+-+ + + +-+-+-+-+ +-+ +-+-+ + + +-+-+-+-+ +-+-+ + +-+-+ +-+-+ + + + +\n",
"| | | | | | | | | | | | | | | | | | | | | | |\n",
"| +-+ + + + + + +-+ + +-+ +-+ + + + + +-+-+-+-+-+-+-+-+ + +-+-+ + +-+-+-+ + + + +\n",
"| | | | | | | | | | | | | | | | | | | | | | | | |\n",
"| + + + +-+-+-+-+ +-+ + +-+-+-+ +-+-+-+-+-+ + +-+ + + +-+ + +-+-+-+ + + +-+ +-+ +\n",
"| | | | | | | | | | | | | | | | | |\n",
"| + +-+-+-+-+-+ +-+-+-+ +-+ + + + +-+-+ + + + +-+-+-+-+-+ + + +-+-+-+-+ + +-+ + +\n",
"| | | | | | | | | | | | | | | | | | |\n",
"| +-+-+-+ + +-+ + +-+-+ + +-+ + +-+-+ + +-+-+-+ +-+-+-+-+-+ + + +-+-+-+-+ + +-+ +\n",
"| | | | | | | | | | | | | | | | | | | |\n",
"|-+-+-+-+ + +-+ + + +-+ +-+ +-+-+ + + +-+-+-+-+-+ + +-+ + + + +-+-+-+-+-+ + +-+-+\n",
"| | | | | | | | | | | | | | | | | | | |\n",
"| + +-+-+-+-+ +-+-+ +-+-+ +-+ + + + +-+-+ + +-+ + +-+ +-+ + + + +-+-+-+ + + +-+ +\n",
"| | | | | | | | | | | | | | | | | | | | | | | | | |\n",
"| + + + + +-+-+ + + + +-+-+ + +-+ + + + +-+ +-+-+-+ +-+ + +-+ + +-+-+ + + +-+ + +\n",
"| | | | | | | | | | | | | | | | | | | | | | | |\n",
"| +-+-+-+-+ +-+-+ + + + + + + + +-+ + +-+ +-+ +-+ +-+-+ + + +-+-+-+-+ +-+-+ +-+-+\n",
"| | | | | | | | | | | | | | | | | |\n",
"| + + +-+-+-+-+ + +-+-+-+-+-+ + +-+-+-+ +-+-+-+ +-+-+-+-+ + +-+-+ + +-+ +-+-+-+ +\n",
"| | | | | | | | | | | | | | | | | |\n",
"| + +-+ +-+ + +-+ + + + + +-+-+-+-+-+ + +-+ + +-+-+ + +-+-+-+-+ + +-+ +-+-+ + +-+\n",
"| | | | | | | | | | | | | | | | | | | | | | | |\n",
"|-+-+-+-+ +-+ + +-+ + +-+ + +-+ +-+ + + + +-+-+ + +-+-+ +-+ +-+-+ +-+ +-+ + + + +\n",
"| | | | | | | | | | | | | | | | | | | | | | | |\n",
"| +-+-+-+-+ +-+ +-+ +-+ +-+-+ + + +-+ + + + + +-+ +-+ +-+ +-+ + +-+ +-+-+-+ +-+ +\n",
"| | | | | | | | | | | | | | | | | | | | |\n",
"| + +-+ + + + +-+ +-+-+ +-+ + +-+ + +-+-+ + +-+ +-+ + + +-+ +-+-+-+-+ +-+ +-+ + +\n",
"| | | | | | | | | | | | | | | | | | | | | | | | |\n",
"|-+-+ + + + + + +-+ +-+-+ + + + +-+-+-+ +-+-+ +-+ + + + +-+-+ + +-+-+-+ +-+-+-+ +\n",
"| | | | | | | | | | | | | | | | | | | | |\n",
"| +-+-+ +-+ + + + +-+-+ + + + +-+-+-+ +-+-+-+-+ + +-+ +-+ +-+ + +-+-+ + + +-+-+ +\n",
"| | | | | | | | | | | | | | | | | | | | |\n",
"| + + + + +-+-+-+-+-+ + + + +-+ +-+-+-+ +-+-+-+ +-+ +-+ +-+ +-+-+-+-+-+ +-+ +-+ +\n",
"| | | | | | | | | | | | | | | | | |\n",
"| +-+-+-+ + + + +-+-+ + +-+ +-+-+ +-+ +-+-+-+-+ + +-+ +-+ +-+ +-+-+ +-+-+ + +-+-+\n",
"| | | | | | | | | | | | | | | | | | |\n",
"| +-+-+ +-+-+ +-+-+-+-+-+ +-+ + + + +-+-+-+ + +-+-+ + +-+-+ +-+ + +-+ +-+ +-+-+ +\n",
"| | | | | | | | | | | | | | | | | | | |\n",
"| + + +-+-+-+-+ +-+ +-+ + + +-+-+-+ +-+ +-+ + +-+ + +-+ +-+-+-+-+ + +-+ +-+-+-+ +\n",
"| | | | | | | | | | | | | | | | | | | |\n",
"| +-+-+-+-+ +-+ + +-+-+-+ +-+-+ +-+-+ +-+ +-+-+ + + + +-+ +-+ + +-+-+-+ + + + +-+\n",
"| | | | | | | | | | | | | | | | | | | | | | |\n",
"| + + + + + + +-+-+-+ + + + + +-+-+ + + +-+ + +-+ +-+-+-+-+ + + + +-+ + +-+-+-+ +\n",
"| | | | | | | | | | | | | | | | | | | | | | | | |\n",
"|-+ + +-+ +-+-+ + + +-+-+-+ + + + + + +-+ + +-+ +-+ +-+ +-+-+ + + +-+ + + + +-+-+\n",
"| | | | | | | | | | | | | | | | | | | | | | |\n",
"| +-+-+ + + + +-+-+-+ +-+-+-+ +-+-+ + + +-+-+ +-+-+-+ +-+ + +-+-+-+ + +-+ +-+-+ +\n",
"| | | | | | | | | | | | | | | | | | | |\n",
"| + + +-+-+-+-+-+ + +-+ +-+ + + + +-+-+ + + +-+-+ + + + + +-+ +-+-+-+-+ +-+-+ + +\n",
"| | | | | | | | | | | | | | | | | | | | | |\n",
"| + + +-+ +-+-+ + +-+-+-+ + +-+-+-+-+-+-+ +-+-+-+-+-+-+ +-+ +-+ +-+-+ + + + +-+ +\n",
"| | | | | | | | | | | | | | | |\n",
"| +-+ +-+-+ + +-+-+ + +-+-+-+-+ + + + +-+-+-+ + + +-+-+-+ + +-+-+-+ +-+-+ + + +-+\n",
"| | | | | | | | | | | | | | | | | | | | | | | |\n",
"| + +-+ + +-+-+-+-+-+ + +-+ + +-+ + + + +-+ +-+ +-+ + + + + + +-+-+-+ + + +-+-+ +\n",
"| | | | | | | | | | | | |\n",
"|-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "fsNyh8Q7NFBA",
"colab_type": "code",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 306
},
"outputId": "2cde9ade-bfe2-43da-d8f3-7bfdba0865d7"
},
"source": [
"import numpy\n",
"from numpy.random import randint as rand\n",
"import matplotlib.pyplot as pyplot\n",
"\n",
"def generate_maze(width=81, height=51, complexity=.75, density=.75):\n",
" \"\"\"Generate a maze using a maze generation algorithm.\"\"\"\n",
" # Only odd shapes\n",
" shape = ((height // 2) * 2 + 1, (width // 2) * 2 + 1)\n",
" # Adjust complexity and density relative to maze size\n",
" complexity = int(complexity * (5 * (shape[0] + shape[1]))) # Number of components\n",
" density = int(density * ((shape[0] // 2) * (shape[1] // 2))) # Size of components\n",
" # Build actual maze\n",
" Z = numpy.zeros(shape, dtype=bool)\n",
" # Fill borders\n",
" Z[0, :] = Z[-1, :] = 1\n",
" Z[:, 0] = Z[:, -1] = 1\n",
" # Make aisles\n",
" for i in range(density):\n",
" x, y = rand(0, shape[1] // 2) * 2, rand(0, shape[0] // 2) * 2 # Pick a random position\n",
" Z[y, x] = 1\n",
" for j in range(complexity):\n",
" neighbours = []\n",
" if x > 1: neighbours.append((y, x - 2))\n",
" if x < shape[1] - 2: neighbours.append((y, x + 2))\n",
" if y > 1: neighbours.append((y - 2, x))\n",
" if y < shape[0] - 2: neighbours.append((y + 2, x))\n",
" if len(neighbours):\n",
" y_, x_ = neighbours[rand(0, len(neighbours) - 1)]\n",
" if Z[y_, x_] == 0:\n",
" Z[y_, x_] = 1\n",
" Z[y_ + (y - y_) // 2, x_ + (x - x_) // 2] = 1\n",
" x, y = x_, y_\n",
" return Z\n",
"\n",
"pyplot.figure(figsize=(10, 5))\n",
"pyplot.imshow(generate_maze(80, 40), cmap=pyplot.cm.binary, interpolation='nearest')\n",
"pyplot.xticks([]), pyplot.yticks([])\n",
"pyplot.show()"
],
"execution_count": 2,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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\nIEtQAQCy/gtbr3BRcnuKJQAAAABJRU5ErkJggg==\n",
"text/plain": [
"