{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<div style=\"text-align: right\">Peter Norvig<br>13 March 2018</div> \n",
    "\n",
    "# Maze Generation\n",
    "\n",
    "Let's make some mazes! I'm thinking of mazes like this one, which is  a rectangular grid of squares, with walls on some of the sides of squares, and openings on other sides:\n",
    "\n",
    "![Wikipedia](https://upload.wikimedia.org/wikipedia/commons/thumb/8/88/Maze_simple.svg/475px-Maze_simple.svg.png)\n",
    "\n",
    "The main constraint is that there should be a path from entrance to exit, and it should be ***fun*** to solve the maze with pencil, paper, and brain power&mdash;not too easy, but also not impossible.  \n",
    "\n",
    "As I think about how to model a maze on the computer, it seems like a **graph** is the right model: the nodes of\n",
    "the graph are the squares of the grid, and the edges of the graph are the openings between adjacent squares. So what properties of a graph make a good maze?\n",
    "- There must be a path from entrance to exit.\n",
    "- There must not be too such many paths; maybe it is best if there is only one. \n",
    "- Probably the graph should be *singly connected*&mdash;there shouldn't be islands of squares that are unreachable from the start. And maybe we want exactly one path between any two squares.\n",
    "- The path should have many twists; it would be too easy if it was mostly straight.\n",
    "\n",
    "I know that a **tree** has all these properties except the last one. So my goal has become: *Superimpose a tree over the grid, covering every square, and make sure the paths are twisty.* Here's how I'll do it:\n",
    "\n",
    "- Start with a grid with no edges (every square is surrounded by walls on all sides). \n",
    "- Add edges (that is, knock down walls) for the entrance at upper left and exit at lower right.\n",
    "- Place the root of the tree in some square.\n",
    "- Then repeat until the tree covers the whole grid:\n",
    "  * Select some node already in the tree.\n",
    "  * Randomly select a neighbor that hasn't been added to the tree yet.\n",
    "  * Add an edge (knock down the wall) from the node to the neighbor.\n",
    "  \n",
    "In the example below, the root, `A`, has been placed in the upper-left corner, and  two branches,\n",
    "`A-B-C-D` and `A-b-c-d`, have been randomly chosen (well, not actually random; they are starting to create the same maze as in the diagram above):\n",
    "\n",
    "     o  o--o--o--o--o--o--o--o--o--o\n",
    "     | A  b  c|  |  |  |  |  |  |  |\n",
    "     o  o--o  o--o--o--o--o--o--o--o\n",
    "     | B|  | d|  |  |  |  |  |  |  |\n",
    "     o  o--o--o--o--o--o--o--o--o--o\n",
    "     | C  D|  |  |  |  |  |  |  |  |\n",
    "     o--o--o--o--o--o--o--o--o--o--o\n",
    "     |  |  |  |  |  |  |  |  |  |  |\n",
    "     o--o--o--o--o--o--o--o--o--o--o\n",
    "     |  |  |  |  |  |  |  |  |  |  |\n",
    "     o--o--o--o--o--o--o--o--o--o  o\n",
    "    \n",
    "Next I select node `d` and extend it to `e` (at which point there are no available neighbors, so `e` will not be selected in the future), and then I select `D` and extend from there all the way to `N`, at each step selecting the node I just added:\n",
    "\n",
    "     o  o--o--o--o--o--o--o--o--o--o\n",
    "     | A  b  c|  |  |  |  |  |  |  |\n",
    "     o  o--o  o--o--o--o--o--o--o--o\n",
    "     | B| e  d|  | N|  |  |  |  |  |\n",
    "     o  o--o--o--o  o--o--o--o--o--o\n",
    "     | C  D|  |  | M|  |  |  |  |  |\n",
    "     o--o  o--o--o  o--o--o--o--o--o\n",
    "     | F  E|  | K  L|  |  |  |  |  |\n",
    "     o  o--o--o  o--o--o--o--o--o--o\n",
    "     | G  H  I  J|  |  |  |  |  |  |\n",
    "     o--o--o--o--o--o--o--o--o--o  o\n",
    "     \n",
    "Continue like this until every square in the grid has been added to the tree. \n",
    "\n",
    "\n",
    "## Implementing Random Trees\n",
    "\n",
    "I'll make the following implementation choices:\n",
    "\n",
    "- A tree will be represented as a list of edges.\n",
    "- An `Edge` is a tuple of two nodes. I'll keep them sorted so that `Edge(A, B)` is the same as `Edge(B, A)`.\n",
    "- A node in a tree can be anything: a number, a letter, a square, ...\n",
    "- The algorithm for `random_tree(nodes, neighbors, pop)` is as follows:\n",
    "  * We will keep track of three collections:\n",
    "    - `tree`: a list of edges that constitutes the tree.\n",
    "    - `nodes`: the set of nodes that have not yet been added to the tree, but will be.\n",
    "    - `frontier`: a queue of nodes in the tree that are eligible to have an edge added.\n",
    "  * On each iteration:\n",
    "    - Use `pop` to pick a `node` from the frontier, and find the neighbors that are not already in the tree.\n",
    "    - If there are any neighbors, randomly pick one (`nbr`), add `Edge(node, nbr)` to `tree`, remove the\n",
    "      neighbor from `nodes`, and keep both the node and the neighbor on the frontier. If there are no neighbors,\n",
    "      drop the node from the frontier.\n",
    "  * When no `nodes` remain, return `tree`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "import random\n",
    "from collections import deque, namedtuple\n",
    "\n",
    "def Edge(node1, node2): return tuple(sorted([node1, node2]))\n",
    "\n",
    "def random_tree(nodes: set, neighbors: callable, pop: callable) -> [Edge]:\n",
    "    \"Repeat: pop a node and add Edge(node, nbr) until all nodes have been added to tree.\"\n",
    "    tree = []\n",
    "    root = nodes.pop()\n",
    "    frontier = deque([root])\n",
    "    while nodes:\n",
    "        node = pop(frontier)\n",
    "        nbrs = neighbors(node) & nodes\n",
    "        if nbrs:\n",
    "            nbr = random.choice(list(nbrs))\n",
    "            tree.append(Edge(node, nbr))\n",
    "            nodes.remove(nbr)\n",
    "            frontier.extend([node, nbr])\n",
    "    return tree"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Implementing  Random Mazes\n",
    "\n",
    "Now let's use `random_tree` to implement `random_maze`.  Some more choices:\n",
    "\n",
    "* A `Maze` is a named tuple with three fields: the `width` and `height` of the grid, and a list of  `edges` between squares. \n",
    "* A square is denoted by an `(x, y)` tuple of integer coordinates.\n",
    "* The function `neighbors4` gives the four surrounding squares."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "Maze = namedtuple('Maze', 'width, height, edges')\n",
    "\n",
    "def neighbors4(square):\n",
    "    \"The 4 neighbors of an (x, y) square.\"\n",
    "    (x, y) = square\n",
    "    return {(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)}\n",
    "\n",
    "def squares(width, height): \n",
    "    \"All squares in a grid of these dimensions.\"\n",
    "    return {(x, y) for x in range(width) for y in range(height)}\n",
    "\n",
    "def random_maze(width, height, pop=deque.pop):\n",
    "    \"Use random_tree to generate a random maze.\"\n",
    "    nodes = squares(width, height)\n",
    "    tree = random_tree(nodes, neighbors4, pop)\n",
    "    return Maze(width, height, tree)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Maze(width=10, height=5, edges=[((6, 3), (7, 3)), ((6, 3), (6, 4)), ((6, 4), (7, 4)), ((7, 4), (8, 4)), ((8, 3), (8, 4)), ((8, 2), (8, 3)), ((7, 2), (8, 2)), ((7, 1), (7, 2)), ((7, 0), (7, 1)), ((7, 0), (8, 0)), ((8, 0), (8, 1)), ((8, 1), (9, 1)), ((9, 0), (9, 1)), ((9, 1), (9, 2)), ((9, 2), (9, 3)), ((9, 3), (9, 4)), ((6, 0), (7, 0)), ((5, 0), (6, 0)), ((5, 0), (5, 1)), ((5, 1), (6, 1)), ((6, 1), (6, 2)), ((5, 2), (6, 2)), ((4, 2), (5, 2)), ((3, 2), (4, 2)), ((3, 2), (3, 3)), ((2, 3), (3, 3)), ((2, 2), (2, 3)), ((2, 1), (2, 2)), ((2, 0), (2, 1)), ((1, 0), (2, 0)), ((0, 0), (1, 0)), ((0, 0), (0, 1)), ((0, 1), (1, 1)), ((1, 1), (1, 2)), ((0, 2), (1, 2)), ((0, 2), (0, 3)), ((0, 3), (1, 3)), ((1, 3), (1, 4)), ((0, 4), (1, 4)), ((1, 4), (2, 4)), ((2, 4), (3, 4)), ((3, 4), (4, 4)), ((4, 3), (4, 4)), ((4, 3), (5, 3)), ((5, 3), (5, 4)), ((2, 0), (3, 0)), ((3, 0), (4, 0)), ((4, 0), (4, 1)), ((3, 1), (4, 1))])"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "random_maze(10,5)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's not very pretty to look at. I'm going to need a way to visualize a maze.\n",
    "\n",
    "# Printing a maze"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "o  o--o--o--o--o--o--o--o--o--o\n",
      "|              |     |        |\n",
      "o--o--o--o  o--o  o  o  o--o  o\n",
      "|           |     |        |  |\n",
      "o  o  o--o--o  o--o--o--o--o  o\n",
      "|  |  |     |     |     |     |\n",
      "o  o--o  o  o--o  o  o--o  o--o\n",
      "|  |     |        |  |  |     |\n",
      "o  o  o--o--o--o--o  o  o--o  o\n",
      "|                    |        |\n",
      "o--o--o--o--o--o--o--o--o--o  o\n"
     ]
    }
   ],
   "source": [
    "def print_maze(maze, dot='o', lin='--', bar='|', sp='  '):\n",
    "    \"Print maze in ASCII.\"\n",
    "    exit = Edge((maze.width-1, maze.height-1), (maze.width-1, maze.height))\n",
    "    edges = set(maze.edges) | {exit}\n",
    "    print(dot + sp + lin.join(dot * maze.width)) # Top line, including entrance\n",
    "    def vert_wall(x, y): return (' ' if Edge((x, y), (x+1, y)) in edges else bar)\n",
    "    def horz_wall(x, y): return (sp  if Edge((x, y), (x, y+1)) in edges else lin)\n",
    "    for y in range(maze.height):\n",
    "        print(bar + cat(sp + vert_wall(x, y) for x in range(maze.width)))\n",
    "        print(dot + cat(horz_wall(x, y) + dot for x in range(maze.width)))\n",
    "        \n",
    "cat = ''.join\n",
    "        \n",
    "print_maze(random_maze(10, 5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "*Much better!* But can I do better still?\n",
    "\n",
    "# Plotting a maze\n",
    "\n",
    "I'll use `matplotlib` to plot lines where the edges aren't:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def plot_maze(maze):\n",
    "    \"Plot a maze by drawing lines between adjacent squares, except for pairs in maze.edges\"\n",
    "    plt.figure(figsize=(8, 4))\n",
    "    plt.axis('off')\n",
    "    plt.gca().invert_yaxis()\n",
    "    w, h  = maze.width, maze.height\n",
    "    exits = {Edge((0, 0), (0, -1)), Edge((w-1, h-1), (w-1, h))}\n",
    "    edges = set(maze.edges) | exits\n",
    "    for sq in squares(w, h):\n",
    "        for nbr in neighbors4(sq):\n",
    "            if Edge(sq, nbr) not in edges:\n",
    "                plot_wall(sq, nbr)\n",
    "    plt.show()\n",
    "\n",
    "def plot_wall(s1, s2):\n",
    "    \"Plot a thick black line between squares s1 and s2.\"\n",
    "    (x1, y1), (x2, y2) = s1, s2\n",
    "    if x1 == x2: # horizontal wall\n",
    "        y = max(y1, y2)\n",
    "        X, Y = [x1, x1+1], [y, y]\n",
    "    else: # vertical wall\n",
    "        x = max(x1, x2)\n",
    "        X, Y = [x, x], [y1, y1+1]\n",
    "    plt.plot(X, Y, 'k-', linewidth=4.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's compare the two visualization functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAd0AAAD8CAYAAAAyun5JAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAABNxJREFUeJzt2sFuIjEUAMF4xf//svcaJSsIAdr2UnXj9uSZUct6jDnn\nBwDwen9WDwAA70J0ASAiugAQEV0AiIguAEREFwAiogsAEdEFgIjoAkBEdAEgIroAEBFdAIiILgBE\nRBcAIqILABHRBYCI6AJA5LJ6gGvGGPPz7znnWDXLv3ydD4Cz1F1x0wWAiOgCQER0ASCy9U53d7vt\nmHlfu///AXax+r84broAEBFdAIiILgBERBcAIqILABHRBYCI6AJARHQBICK6ABARXQCIiC4AREQX\nACKiCwAR0QWAiOgCQER0ASAiugAQEV0AiIguAEREFwAiogsAEdEFgIjoAkBEdAEgIroAEBFdAIiI\nLgBERBcAIqILABHRBYCI6AJARHQBICK6ABARXQCIiC4ARC6rBzjZGGOunuFec86xeoavTjvHHc9w\nd6c9Y57Dt/Kdmy4AREQXACKiCwARO90n2nF/ccIubcdz++yEMzzN7s+c3/Gt3OamCwAR0QWAiOgC\nQER0ASAiugAQEV0AiIguAEREFwAiogsAEdEFgIjoAkBEdAEgIroAEBFdAIiILgBERBcAIqILABHR\nBYCI6AJARHQBICK6ABARXQCIiC4AREQXACKiCwAR0QWAiOgCQER0ASAiugAQEV0AiIguAEREFwAi\nogsAEdEFgMhl9QC0xhhz9Qy3zDnH6hl4Le/h75xwblznpgsAEdEFgIjoAkDETvfN7Lin4v14D5/D\nOZ7HTRcAIqILABHRBYCI6AJARHQBICK6ABARXQCIiC4AREQXACKiCwAR0QWAiOgCQER0ASAiugAQ\nEV0AiIguAEREFwAiogsAEdEFgIjoAkBEdAEgIroAEBFdAIiILgBERBcAIqILABHRBYCI6AJARHQB\nICK6ABARXQCIiC4AREQXACKiCwAR0QWAyGX1ADDGmKtnuMdp8/IznuvjnOFtbroAEBFdAIiILgBE\n7HQfMOccq2f4HzjHx9mlPZ/38nE7nuHqb8VNFwAiogsAEdEFgIjoAkBEdAEgIroAEBFdAIiILgBE\nRBcAIqILABHRBYCI6AJARHQBICK6ABARXQCIiC4AREQXACKiCwAR0QWAiOgCQER0ASAiugAQEV0A\niIguAEREFwAiogsAEdEFgIjoAkBEdAEgIroAEBFdAIiILgBERBcAIqILAJHL6gHuMcaYq2e4Zs45\nVs9wy+5neIITnjOP863wCm66ABARXQCIiC4ARI7a6dql3c+ZPe6E3d7uz3n3+T4+zpiR87npAkBE\ndAEgIroAEBFdAIiILgBERBcAIqILABHRBYCI6AJARHQBICK6ABARXQCIiC4AREQXACKiCwAR0QWA\niOgCQER0ASAiugAQEV0AiIguAEREFwAiogsAEdEFgIjoAkBEdAEgIroAEBFdAIiILgBERBcAIqIL\nABHRBYCI6AJARHQBICK6ABC5rB7gHmOMuXoG8B7Cz8w5x+oZduOmCwAR0QWAiOgCQGTMaT0FAAU3\nXQCIiC4AREQXACKiCwAR0QWAiOgCQER0ASAiugAQEV0AiIguAEREFwAiogsAEdEFgIjoAkBEdAEg\nIroAEBFdAIiILgBERBcAIqILABHRBYCI6AJARHQBICK6ABARXQCIiC4AREQXACKiCwAR0QWAiOgC\nQER0ASDyFyUScgM3r2rXAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x10dd4dd68>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "o  o--o--o--o--o--o--o--o--o--o\n",
      "|              |              |\n",
      "o  o--o  o--o  o  o--o--o--o  o\n",
      "|  |  |     |  |  |        |  |\n",
      "o  o  o--o  o  o  o--o  o--o  o\n",
      "|  |  |     |  |  |     |     |\n",
      "o  o  o  o--o--o  o  o--o  o--o\n",
      "|     |        |  |  |     |  |\n",
      "o--o  o--o--o  o  o  o--o--o  o\n",
      "|           |                 |\n",
      "o--o--o--o--o--o--o--o--o--o  o\n"
     ]
    }
   ],
   "source": [
    "M = random_maze(10, 5)\n",
    "plot_maze(M)  \n",
    "print_maze(M)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# `pop` strategies\n",
    "\n",
    "Now I want to compare how the maze varies based on theree different choices for the `pop` parameter. \n",
    "\n",
    "(1) The default is `deque.pop`\n",
    "which means that the tree is created **depth-first**; we always select the `node` at the end of the `frontier`, so the tree follows a single branch along a randomly-twisted path until the path doubles back on itself and there are no more neighbors; at that point we select the most recent square for which there are neighbors:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1176e5e80>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_maze(random_maze(40, 20, deque.pop))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The maze with `deque.pop` looks pretty good. Reminds me of those [cyber brain](https://www.vectorstock.com/royalty-free-vector/cyber-brain-vector-3071965) images.\n",
    "\n",
    "(2) An alternative is `queue.popleft`, which creates the maze roughly **breadth-first**&mdash;we start at some root square , add an edge to it, and from then on we always select first a parent edge before we select a child edge. The net result is a design that appears to radiate out in concentric layers from the root (which is chosen by `random_tree` and is not necessarily the top-left square; below it looks like the root is in the upper-left quadrant). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x1176fcf60>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_maze(random_maze(40, 20, deque.popleft))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `deque.popleft` maze is interesting as a design, but to me it doesn't work well as a maze.\n",
    "\n",
    "(3) We can select a cell at random by shuffling the frontier  before popping an element off of it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nxnjoO1WPSQgAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x117710550>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def poprandom(seq):\n",
    "    \"Shuffle a mutable sequence (deque or list) and then pop an element.\"\n",
    "    random.shuffle(seq)\n",
    "    return seq.pop()\n",
    "\n",
    "plot_maze(random_maze(40, 20, poprandom))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is an interesting compromise: it has some structure, but still works nicely as a maze, in my opinion.\n",
    "\n",
    "What other variations can you come up with to generate interesting mazes?"
   ]
  }
 ],
 "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.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
