diff --git a/ipynb/Life.ipynb b/ipynb/Life.ipynb index 0ac5a58..6bc25a4 100644 --- a/ipynb/Life.ipynb +++ b/ipynb/Life.ipynb @@ -8,7 +8,7 @@ "\n", "# Conway's Game of Life \n", "\n", - "![](https://upload.wikimedia.org/wikipedia/commons/thumb/4/45/Glider.svg/140px-Glider.svg.png)\n", + "![Glider](https://upload.wikimedia.org/wikipedia/commons/9/96/Animated_glider_emblem.gif)\n", "\n", "The cellular automata game *Life*, invented by the mathematician [John H. Conway](https://en.wikipedia.org/wiki/John_Horton_Conway), makes a fun programming exercise. Let's review the [rules](http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life):\n", "\n", @@ -18,10 +18,10 @@ ">+ Any empty cell with exactly three live neighbors becomes live in the next generation.\n", ">+ All other cells are empty in the next generation.\n", "\n", - "For example, in the diagram below, \"`@`\" cells are live. In the transition from Generation 0 to 1, the cell marked \"`,`\" becomes empty (dies off) because it has zero live neighbors. In the next transition, a fourth `@` becomes live, because it has 3 live neighbors. All other cells stay the same. \n", + "For example, in the diagram below, \"`@`\" cells are live. In the transition from Generation 0 to 1, the cell marked \"`:`\" becomes empty (dies off) because it has zero live neighbors. In the next transition, a fourth `@` becomes live, because it has 3 live neighbors. All other cells stay the same. \n", "\n", " . . . . . . . . . . . . . . .\n", - " . . . @ . . . . , . . . . . .\n", + " . . . @ . . . . : . . . . . .\n", " . @ . . . . @ . . . . @ @ . .\n", " . @ @ . . . @ @ . . . @ @ . .\n", " . . . . . . . . . . . . . . .\n", @@ -36,15 +36,10 @@ "\n", "To create a program to play *Life*, go through the inventory of concepts and decide how to implement each one:\n", "\n", - "+ **World** and **Cell**: A state of the world must represent which cells are empty and which are live. That looks like a job for a two-dimensional array with values 1 for live and 0 for empty. The tricky part is that the number of cells is infinite, and we can't store an infinite array in a finite computer. I can think of three ways around this issue:\n", - " - Use a **fixed-size two-dimensional array** but change the rules: cells at the edge have fewer neighbors, or they wrap around.\n", - " - Use a **sparse matrix** that can change size, expanding in any direction to contain all the live cells.\n", - " - Use a **set of live cells**, where a **cell** is represented as an (x, y)-coordinate pair. I think this is the easiest choice.\n", - "
Example: `world = {(3, 1), (1, 2), (1, 3), (2, 3)}; cell = (1, 2)` \n", + "+ **World** and **Cell**: A state of the world must represent which cells are empty and which are live. That looks like a job for a two-dimensional array with values 1 for live and 0 for empty. The tricky part is that the number of cells is infinite, and we can't store an infinite array in a finite computer. We could use a sparse matrix that can change size, but that's complicated. A simpler representation is a **set of live cells**, where a **cell** is represented as an (x, y)-coordinate pair. choice. Example: `world = {(3, 1), (1, 2), (1, 3), (2, 3)}; cell = (1, 2)` \n", "+ **Live** and **Empty**: A cell is live if it is a member of a world, i.e. if `cell in world` is true. \n", - "+ **Neighbors**: The cell `(x, y)` has eight neighbors, formed by adding or subtracting 1 from `x` or `y` or both.\n", - "
Example: `neighbors((1, 2))` → `((0, 1), (1, 1), (2, 1), (0, 2), (2, 2), (0, 3), (1, 3), (2, 3))`\n", - "+ **Next Generation**: The function `next_generation(world)` returns a new world with the new set of live cells according to the rules.
Example: `next_generation({(3, 1), (1, 2), (1, 3), (2, 3)})` → `{(1, 2), (1, 3), (2, 3)}`\n", + "+ **Neighbors**: The cell `(x, y)` has eight neighbors, formed by adding or subtracting 1 from `x` or `y` or both. Example: `neighbors((1, 2))` → `((0, 1), (1, 1), (2, 1), (0, 2), (2, 2), (0, 3), (1, 3), (2, 3))`\n", + "+ **Next Generation**: The function `next_generation(world)` returns a new world with the new set of live cells according to the rules. Example: `next_generation({(3, 1), (1, 2), (1, 3), (2, 3)})` → `{(1, 2), (1, 3), (2, 3)}`\n", "+ **Sequence of Generations**: The generator function `life(world, n)` yields `n` generations starting from the given world. \n", "+ **Display**: We will need some way to display the generations. Let's defer that for now.\n", "+ **Live Neighbor Counts**: To determine the next generation, we need to know how many live neighbors each cell has. A good way to represent this is a mapping of `{cell: count}`. An easy way to produce this mapping is with a `Counter`, passing it every neighbor of every live cell. This may feel like we're doing the counting \"backwards.\" Instead of asking \"for each cell, how many live neighbors does it have?\" we are saying \"for each live cell, increment the count of each of its neighbors.\" The two amount to the same thing because *neighbor* is symmetric—if P is a neighbor of Q, then Q is a neighbor of P. Below we see the neighbor counts for each of the three generations of the example above; in each generation the top diagram gives the neighbor counts for the empty cells, and the bottom diagram gives the counts for the live cells. This is just to make the diagram easier to read; in the code the counts are all in one `Counter`. \n", @@ -68,19 +63,19 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from collections import Counter\n", - "from typing import Set, Tuple, Dict, Iterator, List\n", + "from typing import Iterator\n", "from itertools import islice\n", "from IPython.display import clear_output, display_html\n", "from time import sleep\n", - "import sys\n", + "from sys import maxsize\n", "\n", - "Cell = Tuple[int, int]\n", - "World = Set[Cell] " + "Cell = tuple[int, int]\n", + "World = set[Cell] " ] }, { @@ -92,33 +87,32 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ - "def life(world, n=sys.maxsize) -> Iterator[World]:\n", - " \"\"\"Yield `n` generations, starting from the given world.\"\"\"\n", + "def life(world, n=maxsize) -> Iterator[World]:\n", + " \"\"\"Yield the world and `n` following generations.\"\"\"\n", + " yield world\n", " for g in range(n):\n", - " yield world\n", - " world = next_generation(world)\n", + " yield (world := next_generation(world))\n", "\n", "def next_generation(world) -> World:\n", " \"\"\"The set of live cells in the next generation.\"\"\"\n", " return {cell for cell, count in neighbor_counts(world).items()\n", " if count == 3 or (count == 2 and cell in world)}\n", "\n", - "def neighbor_counts(world) -> Dict[Cell, int]:\n", + "def neighbor_counts(world) -> dict[Cell, int]:\n", " \"\"\"A Counter of the number of live neighbors for each cell.\"\"\"\n", " return Counter(xy for cell in world \n", " for xy in neighbors(cell))\n", "\n", - "def neighbors(cell) -> List[Cell]:\n", + "def neighbors(cell) -> list[Cell]:\n", " \"\"\"All 8 adjacent neighbors of cell.\"\"\"\n", " (x, y) = cell\n", - " return [(x + dx, y + dy) \n", - " for dx in (-1, 0, 1) \n", - " for dy in (-1, 0, 1) \n", - " if not (dx == 0 == dy)]" + " return [(x-1, y-1), (x, y-1), (x+1, y-1),\n", + " (x-1, y), (x+1, y),\n", + " (x-1, y+1), (x, y+1), (x+1, y+1)]" ] }, { @@ -132,21 +126,9 @@ "cell_type": "code", "execution_count": 3, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "{(1, 2), (1, 3), (2, 3)}" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "world = {(3, 1), (1, 2), (1, 3), (2, 3)}\n", - "next_generation(world)" + "world = {(3, 1), (1, 2), (1, 3), (2, 3)}" ] }, { @@ -157,52 +139,29 @@ { "data": { "text/plain": [ - "[{(1, 2), (1, 3), (2, 3), (3, 1)},\n", - " {(1, 2), (1, 3), (2, 3)},\n", - " {(1, 2), (1, 3), (2, 2), (2, 3)},\n", - " {(1, 2), (1, 3), (2, 2), (2, 3)}]" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "list(life(world, 4))" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Counter({(0, 1): 1,\n", - " (0, 2): 2,\n", - " (0, 3): 2,\n", - " (1, 1): 1,\n", - " (1, 3): 2,\n", + "Counter({(2, 2): 4,\n", " (2, 1): 2,\n", - " (2, 2): 4,\n", + " (3, 2): 2,\n", + " (0, 2): 2,\n", + " (1, 2): 2,\n", + " (0, 3): 2,\n", " (2, 3): 2,\n", + " (1, 4): 2,\n", + " (2, 4): 2,\n", + " (1, 3): 2,\n", " (2, 0): 1,\n", " (3, 0): 1,\n", - " (3, 2): 2,\n", " (4, 0): 1,\n", " (4, 1): 1,\n", " (4, 2): 1,\n", - " (1, 2): 2,\n", - " (1, 4): 2,\n", - " (2, 4): 2,\n", + " (0, 4): 1,\n", + " (0, 1): 1,\n", + " (1, 1): 1,\n", " (3, 3): 1,\n", - " (3, 4): 1,\n", - " (0, 4): 1})" + " (3, 4): 1})" ] }, - "execution_count": 5, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -213,16 +172,36 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[(0, 1), (0, 2), (0, 3), (1, 1), (1, 3), (2, 1), (2, 2), (2, 3)]" + "{(1, 2), (1, 3), (2, 3)}" ] }, - "execution_count": 11, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "next_generation(world)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[(0, 1), (1, 1), (2, 1), (0, 2), (2, 2), (0, 3), (1, 3), (2, 3)]" + ] + }, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -231,6 +210,30 @@ "neighbors((1, 2))" ] }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{(1, 2), (1, 3), (2, 3), (3, 1)},\n", + " {(1, 2), (1, 3), (2, 3)},\n", + " {(1, 2), (1, 3), (2, 2), (2, 3)},\n", + " {(1, 2), (1, 3), (2, 2), (2, 3)},\n", + " {(1, 2), (1, 3), (2, 2), (2, 3)}]" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "list(life(world, 4))" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -242,7 +245,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -252,29 +255,8 @@ " \n", "def picture(world, Xs: range, Ys: range) -> str:\n", " \"\"\"Return a picture of the world: a grid of characters representing the cells in this window.\"\"\"\n", - " def row(y): return PAD.join(LIVE if (x, y) in world else EMPTY for x in Xs)\n", - " return '\\n'.join(row(y) for y in Ys)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "({(1, 2), (1, 3), (2, 3), (3, 1)}, {(1, 2), (1, 3), (2, 3)})" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "g = life(world)\n", - "next(g), next(g)" + " def row(y): return PAD.join((LIVE if (x, y) in world else EMPTY) for x in Xs)\n", + " return '\\n'.join(map(row, Ys))" ] }, { @@ -304,7 +286,7 @@ "source": [ "# Animated Display\n", "\n", - "The function `animate_life` displays `n` generations: display `world`, pause for `pause` seconds, then clear the screen and display the next generation." + "The function `animate_life` displays `world`, pauses for `pause` seconds, then clears the screen and displays the next generation, repeating `n` times." ] }, { @@ -321,7 +303,7 @@ " picture(world, Xs, Ys)), raw=True)\n", " sleep(pause)\n", " \n", - "def pre(text) -> str: return f'<{}pre>{text}<{}/pre>'" + "def pre(text) -> str: return f'
{text}
'" ] }, { @@ -332,7 +314,7 @@ { "data": { "text/html": [ - "
Generation:  3, Population:  4\n",
+       "
Generation:  4, Population:  4\n",
        ". . . . .\n",
        ". . . . .\n",
        ". @ @ . .\n",
@@ -345,7 +327,7 @@
     }
    ],
    "source": [
-    "animate_life(world, 4, range(5), range(5), 1)"
+    "animate_life(world, 4, range(5), range(5), pause=1)"
    ]
   },
   {
@@ -354,7 +336,7 @@
    "source": [
     "# Interesting Worlds\n",
     "\n",
-    "Now let's take a look at some configurations of cells that *Life* enthusiasts have discovered. It would be tedious to write out a set of `(x, y)` coordinates, so we will define the function `shape` that takes a picture as input and returns a world; `shape` and `picture` are more-or-less inverses. "
+    "Now let's take a look at some configurations of cells that *Life* enthusiasts have discovered. It would be tedious to keep writing out  sets of `(x, y)` coordinates, so we will define the function `shape` that takes a picture as input and returns a world; `shape` and `picture` are more-or-less inverses. "
    ]
   },
   {
@@ -365,11 +347,10 @@
    "source": [
     "def shape(picture, dx=3, dy=3) -> World:\n",
     "    \"\"\"Convert a graphical picture (e.g. '@ @ .\\n. @ @') into a world (set of cells).\"\"\"\n",
-    "    cells = {(x, y) \n",
-    "             for (y, row) in enumerate(picture.splitlines())\n",
-    "             for (x, c) in enumerate(row.replace(PAD, ''))\n",
-    "             if c == LIVE}\n",
-    "    return slide(cells, dx, dy)\n",
+    "    return {(x + dx, y + dy) \n",
+    "            for (y, row) in enumerate(picture.splitlines())\n",
+    "            for (x, c) in enumerate(row.replace(PAD, ''))\n",
+    "            if c == LIVE}\n",
     "\n",
     "def slide(cells, dx, dy):\n",
     "    \"\"\"Translate/slide a set of cells by a (dx, dy) offset.\"\"\"\n",
@@ -419,29 +400,6 @@
    "cell_type": "code",
    "execution_count": 14,
    "metadata": {},
-   "outputs": [
-    {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      ". . . . . . .\n",
-      ". . . . . . .\n",
-      ". . . . . . .\n",
-      ". . . @ @ . .\n",
-      ". . . . @ @ .\n",
-      ". . . . . . .\n",
-      ". . . . . . .\n"
-     ]
-    }
-   ],
-   "source": [
-    "print(picture(_, range(7), range(7)))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": 15,
-   "metadata": {},
    "outputs": [
     {
      "data": {
@@ -449,7 +407,7 @@
        "{(3, 3), (3, 4), (4, 3), (4, 4)}"
       ]
      },
-     "execution_count": 15,
+     "execution_count": 14,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -460,7 +418,7 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 16,
+   "execution_count": 15,
    "metadata": {},
    "outputs": [
     {
@@ -469,7 +427,7 @@
        "{(103, 203), (103, 204), (104, 203), (104, 204)}"
       ]
      },
-     "execution_count": 16,
+     "execution_count": 15,
      "metadata": {},
      "output_type": "execute_result"
     }
@@ -487,18 +445,18 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 17,
+   "execution_count": 16,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/html": [
-       "
Generation:  9, Population:  3\n",
+       "
Generation: 10, Population:  3\n",
        ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
-       ". . . . @ . . . . .\n",
-       ". . . . @ . . . . .\n",
-       ". . . . @ . . . . .\n",
+       ". . . . . . . . . .\n",
+       ". . . @ @ @ . . . .\n",
+       ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
@@ -511,24 +469,24 @@
     }
    ],
    "source": [
-    "animate_life(blinker)"
+    "animate_life(blinker, pause=1/2)"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": 18,
+   "execution_count": 17,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/html": [
-       "
Generation:  9, Population:  6\n",
+       "
Generation: 10, Population:  8\n",
        ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
        ". . . @ @ . . . . .\n",
-       ". . . @ . . . . . .\n",
-       ". . . . . . @ . . .\n",
+       ". . . @ @ . . . . .\n",
+       ". . . . . @ @ . . .\n",
        ". . . . . @ @ . . .\n",
        ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
@@ -545,19 +503,19 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 19,
+   "execution_count": 18,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/html": [
-       "
Generation:  9, Population:  6\n",
+       "
Generation: 10, Population:  6\n",
        ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
-       ". . . . . @ . . . .\n",
-       ". . . @ . . @ . . .\n",
-       ". . . @ . . @ . . .\n",
-       ". . . . @ . . . . .\n",
+       ". . . . . . . . . .\n",
+       ". . . . @ @ @ . . .\n",
+       ". . . @ @ @ . . . .\n",
+       ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
        ". . . . . . . . . .\n",
@@ -574,23 +532,28 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 20,
+   "execution_count": 19,
    "metadata": {},
    "outputs": [
     {
      "data": {
       "text/html": [
-       "
Generation: 19, Population:  5\n",
-       ". . . . . . . . . .\n",
-       ". . . . . . . . . .\n",
-       ". . . . . . . . . .\n",
-       ". . . . . . . . . .\n",
-       ". . . . . . . . . .\n",
-       ". . . . . . . . . .\n",
-       ". . . . . . . . . .\n",
-       ". . . . . . . . . .\n",
-       ". . . . . . . . @ .\n",
-       ". . . . . . . . . @
" + "
Generation: 32, Population:  5\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . . . .\n",
+       ". . . . . . . . . . . . @ . .\n",
+       ". . . . . . . . . . . . . @ .\n",
+       ". . . . . . . . . . . @ @ @ .\n",
+       ". . . . . . . . . . . . . . .
" ] }, "metadata": {}, @@ -598,7 +561,66 @@ } ], "source": [ - "animate_life(glider, 20)" + "animate_life(glider, 32, Xs=range(15), Ys=range(15))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
Generation: 130, Population: 178\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",
+       ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "animate_life(rpentomino, 130, range(55), range(40))" ] }, { @@ -609,47 +631,47 @@ { "data": { "text/html": [ - "
Generation: 129, Population: 163\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",
-       ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
" + "
Generation: 160, Population: 111\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",
+       ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
" ] }, "metadata": {}, @@ -657,7 +679,7 @@ } ], "source": [ - "animate_life(rpentomino, 130, range(48), range(40))" + "animate_life(zoo, 160, range(55), range(40))" ] }, { @@ -668,91 +690,32 @@ { "data": { "text/html": [ - "
Generation: 159, Population: 105\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",
-       ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "animate_life(zoo, 160, range(48), range(40))" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
Generation: 199, Population: 100\n",
+       "
Generation: 200, Population: 94\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",
@@ -803,21 +766,21 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 24,
+   "execution_count": 23,
    "metadata": {},
    "outputs": [],
    "source": [
     "def next_generation(world):\n",
     "    \"\"\"The set of live cells in the next generation.\"\"\"\n",
     "    counts = neighbor_counts(world)\n",
-    "    def live(cell): return counts[cell] == 3 or (counts[cell] == 2 and cell in world)\n",
+    "    def live(cell) -> bool: return counts[cell] == 3 or (counts[cell] == 2 and cell in world)\n",
     "    return set(filter(live, counts))"
    ]
   }
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3",
+   "display_name": "Python 3 (ipykernel)",
    "language": "python",
    "name": "python3"
   },
@@ -831,9 +794,9 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.7.7"
+   "version": "3.13.3"
   }
  },
  "nbformat": 4,
- "nbformat_minor": 1
+ "nbformat_minor": 4
 }