{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Labyrinths and mazes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "```\n", "🐭\n", "┃┣━━━┳━━━━┳━━┓\n", "┃┗┓┏┛┃╻╺━━┛╺┓┃\n", "┣┓┃┗┓┗┻━━━┳╸┃┃\n", "┃┃┣╸┣━━┳━┓┗━┛┃\n", "┃┃┃┏┛┏╸┃╻┣━┳╸┃\n", "┃┗━┫╻┣━━┫┗╸┃┏┫\n", "┃┏━┫┃┃╺┓┗━┓┃┃┃\n", "┃┃┃┃┃┗┓┗━┓┗┻╸┃\n", "┗━┫┏┻━━━━┻━━━┛\n", "γ€€πŸ§€\n", "```\n", "http://xahlee.info/comp/unicode_drawing_shapes.html by [Xah Lee](http://xahlee.org/index.html)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Situationist Times #04: International Labyrinth Edition\n", "\n", "[![](https://vandal.ist/thesituationisttimes/covers/st04.cover.m.jpg)](https://vandal.ist/thesituationisttimes/04/)\n", "\n", "Jacqueline de Jong (one of the makers of the Situationist Times) talking about [printed mazes with overlay](https://vandal.ist/thesituationisttimes/04/index.html#8/-1.869/118.973).\n", "\n", "The PDFs of the Situationist Times are [available on Monoskop](https://monoskop.org/Situationist_Times)! " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 10 PRINT\n", "\n", "Nick Montfort and others compilation of maze generation and other simple scripts from early home computing.\n", "\n", "https://hub.xpub.nl/bootleglibrary/book/583\n", "\n", "Some links to nice parts:\n", "\n", "* Including [what is a maze](https://hub.xpub.nl/bootleglibrary/read/583/pdf#page=47), and the ever useful (controversial?) distinction between maze and labyrinth...\n", "\n", "> The terms β€œmaze” and β€œlabyrinth” are generally synonyms in colloquial English. Still, many scholars and historians have argued over the distinction between these two terms. In the most popular proposed distinction, β€œlabyrinth” refers only to single-path (unicursal) structures, while β€œmaze” refers only to branching-path (multicursal) structures.In this book, the terms β€œmaze” and β€œlabyrinth” are not used to distinguish two different categories of structure or image. Instead, the two terms indicate a single conceptual category, with this book primarily using the term β€œmaze” for both\n", "\n", "* the [map from Atari adventure](https://hub.xpub.nl/bootleglibrary/read/583/pdf#page=62), \n", "* the (missing) [Vera Molnar work](https://hub.xpub.nl/bootleglibrary/read/583/pdf#page=78) but which you can see [online](https://www.centrepompidou.fr/fr/ressources/oeuvre/cez6op), \n", "* the [commodore keyboard](https://hub.xpub.nl/bootleglibrary/read/583/pdf#page=227) and the [PETSCII table in the manual](https://hub.xpub.nl/bootleglibrary/read/583/pdf#page=238)\n", "\n", "AND an interesting link to the conversation about popular culture and the middle-class:\n", "\n", "![](Screenshot-c64-ad.png)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Fun With Python #1: Maze Generator\n", "\n", "This notebook is based on the Medium article \"Fun with Python part 1: Maze Generator\" written by [Orestis Zekai](https://orestiszekai.com/) in 2020. \n", "\n", "It is a nice tutorial which talks you through a Python implementation of a maze generator. \n", "\n", "You can find the tutorial here: https://medium.com/swlh/fun-with-python-1-maze-generator-931639b4fb7e\n", "\n", "The maze generator is based on a **randomized version of Prim's algorithm**, which is one of the [maze algorithms](https://en.wikipedia.org/wiki/Maze_generation_algorithm) that is used to generate a maze:\n", "\n", "* Start with a grid full of walls.\n", "* Pick a cell, mark it as part of the maze. Add the walls of the cell to the wall list.\n", "* While there are walls in the list:\n", " * Pick a random wall from the list. If only one of the cells that the wall divides is visited, then:\n", " * Make the wall a passage and mark the unvisited cell as part of the maze.\n", " * Add the neighboring walls of the cell to the wall list.\n", " * Remove the wall from the list." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Generating mazes on paper\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 1, "metadata": {}, "output_type": "execute_result" } ], "source": [ "from IPython.display import IFrame\n", "IFrame(\"maze-algorithm-on-paper.pdf\", width=600, height=400)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Maze Generator\n", "\n", "The code below is a slightly adapted version of `maze.py` written by Orestis Zekai: https://github.com/OrWestSide/python-scripts/blob/master/maze.py\n", "\n", "You can use the code to generate custom mazes. \n", "\n", "Change the following variables in the code, to make your maze bigger or smaller and to change how your maze looks like!\n", "\n", "```\n", "# --------------------------\n", "wall_tile = \"β–“\"\n", "cell_tile = \"β–‘\"\n", "height = 11\n", "width = 27\n", "# --------------------------\n", "\n", "```\n", "\n", "**NOTE**: The code below is a slightly different version of the code from the tutorial. The variable names are changed in the double for-loops, to make them connect to the other canvas examples in the other notebooks. For example, a for loop to move block by block through the maze is written below in the following way, using `y` and `x` to refer to the coordinates of a block:\n", "\n", "```\n", "for y in range(height):\n", " for x in range(width):\n", " if (maze[y][x] == \"u\"):\n", " print(unvisited, end=\"\")\n", "```" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "β–“β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“\n", "β–“β–‘β–“β–“β–‘β–“β–“β–‘β–“β–‘β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–‘β–‘β–‘β–‘β–‘β–‘β–“\n", "β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–‘β–“β–‘β–“β–‘β–‘β–‘β–“β–‘β–“β–“β–“β–“\n", "β–“β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–‘β–“β–“β–“β–‘β–“β–‘β–“β–“β–“β–‘β–“β–‘β–‘β–‘β–‘β–“\n", "β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–“β–‘β–“β–“β–“β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–“β–“\n", "β–“β–‘β–“β–‘β–“β–“β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–‘β–‘β–‘β–“β–‘β–“β–“β–“β–“β–“\n", "β–“β–‘β–“β–‘β–‘β–“β–“β–“β–“β–“β–“β–“β–‘β–“β–‘β–‘β–“β–‘β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“\n", "β–“β–‘β–“β–“β–‘β–‘β–‘β–‘β–‘β–‘β–“β–“β–‘β–“β–“β–‘β–“β–‘β–“β–‘β–“β–“β–‘β–“β–‘β–“β–“\n", "β–“β–“β–“β–“β–“β–‘β–“β–“β–“β–“β–“β–“β–‘β–‘β–“β–‘β–“β–“β–“β–‘β–‘β–“β–‘β–“β–“β–“β–“\n", "β–“β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–‘β–“β–‘β–“β–“β–‘β–“β–“β–‘β–‘β–“β–“β–‘β–‘β–‘β–‘β–“\n", "β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–“β–‘β–“\n" ] } ], "source": [ "# Maze generator -- Randomized Prim Algorithm\n", "\n", "## Imports\n", "import random\n", "\n", "## Functions\n", "def printMaze(maze):\n", " for y in range(height):\n", " for x in range(width):\n", " if (maze[y][x] == \"u\"):\n", " print(unvisited, end=\"\")\n", " elif (maze[y][x] == \"c\"):\n", " print(cell_tile, end=\"\")\n", " else:\n", " print(wall_tile, end=\"\")\n", " \n", " print(\"\")\n", "\n", "# Find number of surrounding cells\n", "def surroundingCells(rand_wall):\n", " s_cells = 0\n", " if (maze[rand_wall[0]-1][rand_wall[1]] == \"c\"):\n", " s_cells += 1\n", " if (maze[rand_wall[0]+1][rand_wall[1]] == \"c\"):\n", " s_cells += 1\n", " if (maze[rand_wall[0]][rand_wall[1]-1] == \"c\"):\n", " s_cells +=1\n", " if (maze[rand_wall[0]][rand_wall[1]+1] == \"c\"):\n", " s_cells += 1\n", "\n", " return s_cells\n", "\n", "## Main code\n", "\n", "# Init variables\n", "# --------------------------\n", "wall_tile = \"β–“\"\n", "cell_tile = \"β–‘\"\n", "height = 11\n", "width = 27\n", "# --------------------------\n", "wall = \"w\"\n", "cell = \"c\"\n", "unvisited = \"u\"\n", "maze = []\n", "\n", "# Denote all cells as unvisited\n", "for y in range(height):\n", " line = []\n", " for x in range(width):\n", " line.append(unvisited)\n", " maze.append(line)\n", "\n", "# Randomize starting point and set it a cell\n", "starting_height = int(random.random()*height)\n", "starting_width = int(random.random()*width)\n", "if (starting_height == 0):\n", " starting_height += 1\n", "if (starting_height == height-1):\n", " starting_height -= 1\n", "if (starting_width == 0):\n", " starting_width += 1\n", "if (starting_width == width-1):\n", " starting_width -= 1\n", "\n", "# Mark it as cell and add surrounding walls to the list\n", "maze[starting_height][starting_width] = cell\n", "walls = []\n", "walls.append([starting_height - 1, starting_width])\n", "walls.append([starting_height, starting_width - 1])\n", "walls.append([starting_height, starting_width + 1])\n", "walls.append([starting_height + 1, starting_width])\n", "\n", "# Denote walls in maze\n", "maze[starting_height-1][starting_width] = \"w\"\n", "maze[starting_height][starting_width - 1] = \"w\"\n", "maze[starting_height][starting_width + 1] = \"w\"\n", "maze[starting_height + 1][starting_width] = \"w\"\n", "\n", "while (walls):\n", " # Pick a random wall\n", " rand_wall = walls[int(random.random()*len(walls))-1]\n", "\n", " # Check if it is a left wall\n", " if (rand_wall[1] != 0):\n", " if (maze[rand_wall[0]][rand_wall[1]-1] == \"u\" and maze[rand_wall[0]][rand_wall[1]+1] == \"c\"):\n", " # Find the number of surrounding cells\n", " s_cells = surroundingCells(rand_wall)\n", "\n", " if (s_cells < 2):\n", " # Denote the new path\n", " maze[rand_wall[0]][rand_wall[1]] = \"c\"\n", "\n", " # Mark the new walls\n", " # Upper cell\n", " if (rand_wall[0] != 0):\n", " if (maze[rand_wall[0]-1][rand_wall[1]] != \"c\"):\n", " maze[rand_wall[0]-1][rand_wall[1]] = \"w\"\n", " if ([rand_wall[0]-1, rand_wall[1]] not in walls):\n", " walls.append([rand_wall[0]-1, rand_wall[1]])\n", "\n", " # Bottom cell\n", " if (rand_wall[0] != height-1):\n", " if (maze[rand_wall[0]+1][rand_wall[1]] != \"c\"):\n", " maze[rand_wall[0]+1][rand_wall[1]] = \"w\"\n", " if ([rand_wall[0]+1, rand_wall[1]] not in walls):\n", " walls.append([rand_wall[0]+1, rand_wall[1]])\n", "\n", " # Leftmost cell\n", " if (rand_wall[1] != 0): \n", " if (maze[rand_wall[0]][rand_wall[1]-1] != \"c\"):\n", " maze[rand_wall[0]][rand_wall[1]-1] = \"w\"\n", " if ([rand_wall[0], rand_wall[1]-1] not in walls):\n", " walls.append([rand_wall[0], rand_wall[1]-1])\n", "\n", " # Delete wall\n", " for wall in walls:\n", " if (wall[0] == rand_wall[0] and wall[1] == rand_wall[1]):\n", " walls.remove(wall)\n", "\n", " continue\n", "\n", " # Check if it is an upper wall\n", " if (rand_wall[0] != 0):\n", " if (maze[rand_wall[0]-1][rand_wall[1]] == \"u\" and maze[rand_wall[0]+1][rand_wall[1]] == \"c\"):\n", "\n", " s_cells = surroundingCells(rand_wall)\n", " if (s_cells < 2):\n", " # Denote the new path\n", " maze[rand_wall[0]][rand_wall[1]] = \"c\"\n", "\n", " # Mark the new walls\n", " # Upper cell\n", " if (rand_wall[0] != 0):\n", " if (maze[rand_wall[0]-1][rand_wall[1]] != \"c\"):\n", " maze[rand_wall[0]-1][rand_wall[1]] = \"w\"\n", " if ([rand_wall[0]-1, rand_wall[1]] not in walls):\n", " walls.append([rand_wall[0]-1, rand_wall[1]])\n", "\n", " # Leftmost cell\n", " if (rand_wall[1] != 0):\n", " if (maze[rand_wall[0]][rand_wall[1]-1] != \"c\"):\n", " maze[rand_wall[0]][rand_wall[1]-1] = \"w\"\n", " if ([rand_wall[0], rand_wall[1]-1] not in walls):\n", " walls.append([rand_wall[0], rand_wall[1]-1])\n", "\n", " # Rightmost cell\n", " if (rand_wall[1] != width-1):\n", " if (maze[rand_wall[0]][rand_wall[1]+1] != \"c\"):\n", " maze[rand_wall[0]][rand_wall[1]+1] = \"w\"\n", " if ([rand_wall[0], rand_wall[1]+1] not in walls):\n", " walls.append([rand_wall[0], rand_wall[1]+1])\n", "\n", " # Delete wall\n", " for wall in walls:\n", " if (wall[0] == rand_wall[0] and wall[1] == rand_wall[1]):\n", " walls.remove(wall)\n", "\n", " continue\n", "\n", " # Check the bottom wall\n", " if (rand_wall[0] != height-1):\n", " if (maze[rand_wall[0]+1][rand_wall[1]] == \"u\" and maze[rand_wall[0]-1][rand_wall[1]] == \"c\"):\n", "\n", " s_cells = surroundingCells(rand_wall)\n", " if (s_cells < 2):\n", " # Denote the new path\n", " maze[rand_wall[0]][rand_wall[1]] = \"c\"\n", "\n", " # Mark the new walls\n", " if (rand_wall[0] != height-1):\n", " if (maze[rand_wall[0]+1][rand_wall[1]] != \"c\"):\n", " maze[rand_wall[0]+1][rand_wall[1]] = \"w\"\n", " if ([rand_wall[0]+1, rand_wall[1]] not in walls):\n", " walls.append([rand_wall[0]+1, rand_wall[1]])\n", " if (rand_wall[1] != 0):\n", " if (maze[rand_wall[0]][rand_wall[1]-1] != \"c\"):\n", " maze[rand_wall[0]][rand_wall[1]-1] = \"w\"\n", " if ([rand_wall[0], rand_wall[1]-1] not in walls):\n", " walls.append([rand_wall[0], rand_wall[1]-1])\n", " if (rand_wall[1] != width-1):\n", " if (maze[rand_wall[0]][rand_wall[1]+1] != \"c\"):\n", " maze[rand_wall[0]][rand_wall[1]+1] = \"w\"\n", " if ([rand_wall[0], rand_wall[1]+1] not in walls):\n", " walls.append([rand_wall[0], rand_wall[1]+1])\n", "\n", " # Delete wall\n", " for wall in walls:\n", " if (wall[0] == rand_wall[0] and wall[1] == rand_wall[1]):\n", " walls.remove(wall)\n", "\n", " continue\n", "\n", " # Check the right wall\n", " if (rand_wall[1] != width-1):\n", " if (maze[rand_wall[0]][rand_wall[1]+1] == \"u\" and maze[rand_wall[0]][rand_wall[1]-1] == \"c\"):\n", "\n", " s_cells = surroundingCells(rand_wall)\n", " if (s_cells < 2):\n", " # Denote the new path\n", " maze[rand_wall[0]][rand_wall[1]] = \"c\"\n", "\n", " # Mark the new walls\n", " if (rand_wall[1] != width-1):\n", " if (maze[rand_wall[0]][rand_wall[1]+1] != \"c\"):\n", " maze[rand_wall[0]][rand_wall[1]+1] = \"w\"\n", " if ([rand_wall[0], rand_wall[1]+1] not in walls):\n", " walls.append([rand_wall[0], rand_wall[1]+1])\n", " if (rand_wall[0] != height-1):\n", " if (maze[rand_wall[0]+1][rand_wall[1]] != \"c\"):\n", " maze[rand_wall[0]+1][rand_wall[1]] = \"w\"\n", " if ([rand_wall[0]+1, rand_wall[1]] not in walls):\n", " walls.append([rand_wall[0]+1, rand_wall[1]])\n", " if (rand_wall[0] != 0): \n", " if (maze[rand_wall[0]-1][rand_wall[1]] != \"c\"):\n", " maze[rand_wall[0]-1][rand_wall[1]] = \"w\"\n", " if ([rand_wall[0]-1, rand_wall[1]] not in walls):\n", " walls.append([rand_wall[0]-1, rand_wall[1]])\n", "\n", " # Delete wall\n", " for wall in walls:\n", " if (wall[0] == rand_wall[0] and wall[1] == rand_wall[1]):\n", " walls.remove(wall)\n", "\n", " continue\n", "\n", " # Delete the wall from the list anyway\n", " for wall in walls:\n", " if (wall[0] == rand_wall[0] and wall[1] == rand_wall[1]):\n", " walls.remove(wall)\n", " \n", "# Mark the remaining unvisited cells as walls\n", "for y in range(height):\n", " for x in range(width):\n", " if (maze[y][x] == \"u\"):\n", " maze[y][x] = \"w\"\n", "\n", "# Set entrance and exit\n", "for x in range(width):\n", " if (maze[1][x] == \"c\"):\n", " maze[0][x] = \"c\"\n", " break\n", "\n", "for x in range(width-1, 0, -1):\n", " if (maze[height-2][x] == \"c\"):\n", " maze[height-1][x] = \"c\"\n", " break\n", "\n", "# Print final maze\n", "printMaze(maze)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "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.3" } }, "nbformat": 4, "nbformat_minor": 4 }