{"id":"game-of-life","title":"The Game of Life","summary":"A grid of cells that live and die by four simple rules, kept in a list of lists. Print the grid, count each cell's neighbors, work out the next generation, type in a starting pattern, and watch it change until it dies out or settles down.","after":"more-lists","intro":"The **Game of Life** was invented by the mathematician John Horton Conway in 1970. Nobody plays it: you set up a pattern of cells and watch what happens to it. The board is a grid of **cells**, and each cell is either alive or dead. Every cell has eight **neighbors**, the cells that touch it: above, below, left, right and the four diagonals. From one **generation** to the next, every cell follows the same rules, all at the same time:\n\n- A live cell with 2 or 3 live neighbors stays alive.\n- A live cell with fewer than 2 live neighbors dies, as if of loneliness, and one with more than 3 dies of overcrowding.\n- A dead cell with exactly 3 live neighbors comes alive.\n- Any other dead cell stays dead.\n\nThat's all. Yet some patterns blink forever, some freeze, some die out, and some even crawl across the board.\n\nIn this project the grid is a **list of lists**, as in module 19: one list per row, with `1` for a live cell and `0` for a dead one. On the screen, a live cell is `#` and a dead one is `.`.\n\nWhen it's finished, the program asks for a starting pattern, typed one row at a time and ended with an empty line, and for the number of generations to show. With this input:\n\n```text\n......\n.###..\n.#....\n......\n......\n\n9\n```\n\nit prints:\n\n```text\nType the grid, one row per line, and an empty line at the end:\n......\n.###..\n.#....\n......\n......\n\nGenerations: 9\nGeneration 0: 4 alive\n......\n.###..\n.#....\n......\n......\n\nGeneration 1: 4 alive\n..#...\n.##...\n.#....\n......\n......\n\nGeneration 2: 6 alive\n.##...\n.##...\n.##...\n......\n......\n\nGeneration 3: 6 alive\n.##...\n#..#..\n.##...\n......\n......\n\nNothing changes any more.\n```\n\nNine generations were asked for, but after the third one the pattern has settled into a shape that never changes, so the program stops there.\n\n### What you need\n\nEverything up to module 19: functions that return values, with the program's own code under `if __name__ == \"__main__\":` (modules 13 and 14), lists and `for` loops with `range` (modules 15 and 16), f-strings (module 17), and above all lists of lists, read with two indexes such as `grid[row][column]` (module 19).\n\n### How you build it\n\nEach milestone starts from your own code of the milestone before, and adds a function or two:\n\n1. **The grid:** print it, and count its live cells.\n2. **Neighbors:** count the live cells around a cell, and decide whether it lives on.\n3. **The next generation:** a whole new grid, worked out from the old one.\n4. **A start from input:** read the starting pattern that the user types.\n5. **Several generations:** run as many generations as the user asks for.\n6. **Stopping early:** stop when everything is dead or nothing changes any more.\n\nThe checks of the first milestones call your functions directly, as in the lessons. From milestone 4 on, others type into the whole program and compare everything it prints.\n","milestones":[{"id":"grid","title":"The grid","task":"The starting code has a grid called `glider`: a list of 5 rows, each a list of 5 cells, where `1` is a live cell and `0` a dead one. Its main block uses two functions that aren't written yet. Write them:\n\n1. `print_grid(grid)` prints the grid, one row per line: `#` for a live cell and `.` for a dead one, with nothing between the cells.\n2. `count_alive(grid)` returns how many cells of the grid are alive.\n\nBoth work for a grid of any size: any number of rows, each with the same number of cells. The check runs:\n\n```python\ngrid = [[0, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 1]]\nprint_grid(grid)\nprint(count_alive(grid))\n```\n\nand expects:\n\n```\n.#..\n.##.\n...#\n4\n```\n","kind":"code","seed":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\n# Write print_grid and count_alive here.\n\n\nif __name__ == \"__main__\":\n    glider = [\n        [0, 1, 0, 0, 0],\n        [0, 0, 1, 0, 0],\n        [1, 1, 1, 0, 0],\n        [0, 0, 0, 0, 0],\n        [0, 0, 0, 0, 0],\n    ]\n    print_grid(glider)\n    print(count_alive(glider), \"cells are alive\")\n","solution":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\nif __name__ == \"__main__\":\n    glider = [\n        [0, 1, 0, 0, 0],\n        [0, 0, 1, 0, 0],\n        [1, 1, 1, 0, 0],\n        [0, 0, 0, 0, 0],\n        [0, 0, 0, 0, 0],\n    ]\n    print_grid(glider)\n    print(count_alive(glider), \"cells are alive\")\n","hints":["Both functions go through every cell with two loops, one inside the other: `for row in grid:` gives each row, a list, and `for cell in row:` each cell of it.","In `print_grid`, build one line of text per row: start it as `\"\"` before the inner loop, add `\"#\"` or `\".\"` for each cell, and print it after the inner loop.","In `count_alive`, keep a count that starts at 0. A live cell is 1 and a dead one 0, so adding every cell to the count gives the number of live cells."],"tests":[{"name":"A small grid","stdin":"","call":"grid = [[0, 1, 0, 0], [0, 1, 1, 0], [0, 0, 0, 1]]\nprint_grid(grid)\nprint(count_alive(grid))\n","expect":".#..\n.##.\n...#\n4","hidden":false},{"name":"An empty grid","stdin":"","call":"grid = [[0, 0], [0, 0], [0, 0]]\nprint_grid(grid)\nprint(count_alive(grid))\n","expect":"..\n..\n..\n0","hidden":true},{"name":"One row and one column","stdin":"","call":"print_grid([[1, 1, 0, 1, 1, 1]])\nprint(count_alive([[1, 1, 0, 1, 1, 1]]))\nprint_grid([[1], [0], [1]])\nprint(count_alive([[1], [0], [1]]))\n","expect":"##.###\n5\n#\n.\n#\n2","hidden":true},{"name":"The glider in the main block","stdin":"","expect":".#...\n..#..\n###..\n.....\n.....\n5 cells are alive","hidden":true}],"require":[],"forbid":[]},{"id":"neighbors","title":"Neighbors","task":"Whether a cell lives on depends on its eight neighbors. Add two functions:\n\n1. `count_neighbors(grid, row, column)` returns how many of the eight cells around the cell `grid[row][column]` are alive. The cell itself doesn't count. A cell on the edge of the grid has fewer neighbors: a place outside the grid counts as a dead cell.\n2. `will_live(grid, row, column)` returns `True` when the cell is alive in the next generation, and `False` when it isn't, by the rules of the game:\n   - a live cell stays alive with 2 or 3 live neighbors, and dies otherwise;\n   - a dead cell comes alive with exactly 3 live neighbors, and stays dead otherwise.\n\nWatch out at the edges. Rows and columns are counted from 0, so the cell above row 0 would be in row -1, and Python reads `grid[-1]` without any error: it is the **last** row. The edges would then wrap around to the other side of the grid without you noticing. Check that a row and a column are inside the grid before you read the cell.\n\nThe check runs:\n\n```python\ngrid = [\n    [0, 1, 1, 0, 0],\n    [1, 1, 0, 0, 1],\n    [0, 0, 0, 1, 1],\n    [0, 0, 0, 0, 1],\n]\nprint(count_neighbors(grid, 1, 1), count_neighbors(grid, 1, 2), count_neighbors(grid, 0, 0), count_neighbors(grid, 0, 4), count_neighbors(grid, 3, 0))\nprint(will_live(grid, 1, 1), will_live(grid, 1, 2), will_live(grid, 0, 0), will_live(grid, 0, 4), will_live(grid, 3, 4))\n```\n\nand expects:\n\n```\n3 4 3 1 0\nTrue False True False True\n```\n","kind":"code","seed":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\nif __name__ == \"__main__\":\n    glider = [\n        [0, 1, 0, 0, 0],\n        [0, 0, 1, 0, 0],\n        [1, 1, 1, 0, 0],\n        [0, 0, 0, 0, 0],\n        [0, 0, 0, 0, 0],\n    ]\n    print_grid(glider)\n    print(count_alive(glider), \"cells are alive\")\n","solution":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\ndef count_neighbors(grid, row, column):\n    count = 0\n    for r in range(row - 1, row + 2):\n        for c in range(column - 1, column + 2):\n            if r == row and c == column:\n                continue\n            if r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0]):\n                count += grid[r][c]\n    return count\n\n\ndef will_live(grid, row, column):\n    neighbors = count_neighbors(grid, row, column)\n    if grid[row][column] == 1:\n        return neighbors == 2 or neighbors == 3\n    return neighbors == 3\n\n\nif __name__ == \"__main__\":\n    glider = [\n        [0, 1, 0, 0, 0],\n        [0, 0, 1, 0, 0],\n        [1, 1, 1, 0, 0],\n        [0, 0, 0, 0, 0],\n        [0, 0, 0, 0, 0],\n    ]\n    print_grid(glider)\n    print(count_alive(glider), \"cells are alive\")\n","hints":["The neighbors are in the rows from `row - 1` to `row + 1` and the columns from `column - 1` to `column + 1`. Two loops over `range(row - 1, row + 2)` and `range(column - 1, column + 2)` go through that block of 3 by 3 cells; skip the one in the middle with `continue`.","A place `r`, `c` is inside the grid when `r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0])`. Add `grid[r][c]` to the count only then.","In `will_live`, count the neighbors once and keep the number in a variable. For a live cell, return `neighbors == 2 or neighbors == 3`; for a dead one, `neighbors == 3`."],"tests":[{"name":"Cells inside and on the edges","stdin":"","call":"grid = [\n    [0, 1, 1, 0, 0],\n    [1, 1, 0, 0, 1],\n    [0, 0, 0, 1, 1],\n    [0, 0, 0, 0, 1],\n]\nprint(count_neighbors(grid, 1, 1), count_neighbors(grid, 1, 2), count_neighbors(grid, 0, 0), count_neighbors(grid, 0, 4), count_neighbors(grid, 3, 0))\nprint(will_live(grid, 1, 1), will_live(grid, 1, 2), will_live(grid, 0, 0), will_live(grid, 0, 4), will_live(grid, 3, 4))\n","expect":"3 4 3 1 0\nTrue False True False True","hidden":false},{"name":"A full grid","stdin":"","call":"grid = [[1, 1, 1], [1, 1, 1], [1, 1, 1]]\nprint(count_neighbors(grid, 1, 1), count_neighbors(grid, 0, 0), count_neighbors(grid, 2, 1))\nprint(will_live(grid, 1, 1), will_live(grid, 0, 0), will_live(grid, 2, 1))\n","expect":"8 3 5\nFalse True False","hidden":true},{"name":"The edges don't wrap around","stdin":"","call":"grid = [[1, 0, 1], [0, 0, 0], [1, 0, 1]]\nprint(count_neighbors(grid, 0, 0), count_neighbors(grid, 2, 2), count_neighbors(grid, 1, 1), count_neighbors(grid, 0, 1))\nprint(will_live(grid, 0, 0), will_live(grid, 1, 1), will_live(grid, 0, 1))\n","expect":"0 0 4 2\nFalse False False","hidden":true},{"name":"A single row","stdin":"","call":"grid = [[1, 1, 0, 1]]\nprint(count_neighbors(grid, 0, 2), count_neighbors(grid, 0, 0), count_neighbors(grid, 0, 3))\nprint(will_live(grid, 0, 0), will_live(grid, 0, 2))\n","expect":"2 1 0\nFalse False","hidden":true}],"require":[],"forbid":[]},{"id":"next-generation","title":"The next generation","task":"Now the whole grid takes a step. Every cell changes **at the same time**: the next generation is worked out from the current grid alone. So you can't change the cells of `grid` one by one, because a cell you have already changed would give its neighbors a wrong count. Build the next generation as a new grid instead.\n\nWrite `next_generation(grid)`, which returns a **new** grid of the same size, where each cell is 1 when `will_live` says it lives and 0 otherwise. The grid it was given stays as it was.\n\nTo watch it in your own program, add `print()` and `print_grid(next_generation(glider))` to the end of the main block. The check runs:\n\n```python\ngrid = [\n    [0, 0, 0, 0, 0],\n    [0, 0, 1, 0, 0],\n    [0, 0, 1, 0, 0],\n    [0, 0, 1, 0, 0],\n    [0, 0, 0, 0, 0],\n]\nprint_grid(next_generation(grid))\nprint()\nprint_grid(grid)\n```\n\nand expects the next generation, then the grid it was given, unchanged:\n\n```\n.....\n.....\n.###.\n.....\n.....\n\n.....\n..#..\n..#..\n..#..\n.....\n```\n\nThis pattern is called a **blinker**: it turns from standing to lying and back again, forever.\n","kind":"code","seed":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\ndef count_neighbors(grid, row, column):\n    count = 0\n    for r in range(row - 1, row + 2):\n        for c in range(column - 1, column + 2):\n            if r == row and c == column:\n                continue\n            if r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0]):\n                count += grid[r][c]\n    return count\n\n\ndef will_live(grid, row, column):\n    neighbors = count_neighbors(grid, row, column)\n    if grid[row][column] == 1:\n        return neighbors == 2 or neighbors == 3\n    return neighbors == 3\n\n\nif __name__ == \"__main__\":\n    glider = [\n        [0, 1, 0, 0, 0],\n        [0, 0, 1, 0, 0],\n        [1, 1, 1, 0, 0],\n        [0, 0, 0, 0, 0],\n        [0, 0, 0, 0, 0],\n    ]\n    print_grid(glider)\n    print(count_alive(glider), \"cells are alive\")\n","solution":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\ndef count_neighbors(grid, row, column):\n    count = 0\n    for r in range(row - 1, row + 2):\n        for c in range(column - 1, column + 2):\n            if r == row and c == column:\n                continue\n            if r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0]):\n                count += grid[r][c]\n    return count\n\n\ndef will_live(grid, row, column):\n    neighbors = count_neighbors(grid, row, column)\n    if grid[row][column] == 1:\n        return neighbors == 2 or neighbors == 3\n    return neighbors == 3\n\n\ndef next_generation(grid):\n    new_grid = []\n    for row in range(len(grid)):\n        new_row = []\n        for column in range(len(grid[0])):\n            if will_live(grid, row, column):\n                new_row.append(1)\n            else:\n                new_row.append(0)\n        new_grid.append(new_row)\n    return new_grid\n\n\nif __name__ == \"__main__\":\n    glider = [\n        [0, 1, 0, 0, 0],\n        [0, 0, 1, 0, 0],\n        [1, 1, 1, 0, 0],\n        [0, 0, 0, 0, 0],\n        [0, 0, 0, 0, 0],\n    ]\n    print_grid(glider)\n    print(count_alive(glider), \"cells are alive\")\n    print()\n    print_grid(next_generation(glider))\n","hints":["Build the new grid the way you build any list: start from `new_grid = []`, and for each row of the old grid append a new row, which you build from `[]` too, one cell at a time.","Loop over the indexes, as `will_live` needs them: `for row in range(len(grid)):` outside, and `for column in range(len(grid[0])):` inside.","Inside, `if will_live(grid, row, column):` appends 1 to the new row, and `else` appends 0. After the inner loop, append the new row to `new_grid`, and return `new_grid` at the very end."],"tests":[{"name":"A blinker turns","stdin":"","call":"grid = [\n    [0, 0, 0, 0, 0],\n    [0, 0, 1, 0, 0],\n    [0, 0, 1, 0, 0],\n    [0, 0, 1, 0, 0],\n    [0, 0, 0, 0, 0],\n]\nprint_grid(next_generation(grid))\nprint()\nprint_grid(grid)\n","expect":".....\n.....\n.###.\n.....\n.....\n\n.....\n..#..\n..#..\n..#..\n.....","hidden":false},{"name":"A glider crawls","stdin":"","call":"grid = [\n    [0, 1, 0, 0, 0, 0],\n    [0, 0, 1, 0, 0, 0],\n    [1, 1, 1, 0, 0, 0],\n    [0, 0, 0, 0, 0, 0],\n    [0, 0, 0, 0, 0, 0],\n]\nfor step in range(4):\n    grid = next_generation(grid)\n    print_grid(grid)\n    print(count_alive(grid))\n","expect":"......\n#.#...\n.##...\n.#....\n......\n5\n......\n..#...\n#.#...\n.##...\n......\n5\n......\n.#....\n..##..\n.##...\n......\n5\n......\n..#...\n...#..\n.###..\n......\n5","hidden":true},{"name":"A block stays a block","stdin":"","call":"grid = [[0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0]]\nprint_grid(next_generation(grid))\nprint(next_generation(grid) == grid)\n","expect":"....\n.##.\n.##.\n....\nTrue","hidden":true},{"name":"A lonely cell dies","stdin":"","call":"grid = [[1]]\nprint(next_generation(grid))\nprint(grid)\n","expect":"[[0]]\n[[1]]","hidden":true}],"require":[],"forbid":[]},{"id":"from-input","title":"A start from input","task":"Typing a pattern into the code as a list of lists is slow. From now on, the user types it, one row per line, as it looks on the screen: `#` for a live cell and `.` for a dead one. An empty line ends the grid.\n\n1. Write `read_grid()`, which reads the rows with `input()` (with no prompt) until it reads an empty line, and returns the grid as a list of lists of 1s and 0s. Every character other than `#` is a dead cell.\n2. Write `print_generation(grid, number)`, which prints a line such as `Generation 0: 5 alive` (the number it's given, and how many cells are alive), then the grid, and then an empty line.\n3. Write a function `main()`, and replace everything in the main block with a call to it. `main` prints `Type the grid, one row per line, and an empty line at the end:`, reads the grid, and prints it as generation 0 and its next generation as generation 1.\n\nWhen the user types a glider and an empty line:\n\n```text\n.....\n..#..\n...#.\n.###.\n.....\n```\n\nthe program prints (the typed rows show up in the output, as on a terminal):\n\n```\nType the grid, one row per line, and an empty line at the end:\n.....\n..#..\n...#.\n.###.\n.....\n\nGeneration 0: 5 alive\n.....\n..#..\n...#.\n.###.\n.....\n\nGeneration 1: 5 alive\n.....\n.....\n.#.#.\n..##.\n..#..\n```\n","kind":"code","seed":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\ndef count_neighbors(grid, row, column):\n    count = 0\n    for r in range(row - 1, row + 2):\n        for c in range(column - 1, column + 2):\n            if r == row and c == column:\n                continue\n            if r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0]):\n                count += grid[r][c]\n    return count\n\n\ndef will_live(grid, row, column):\n    neighbors = count_neighbors(grid, row, column)\n    if grid[row][column] == 1:\n        return neighbors == 2 or neighbors == 3\n    return neighbors == 3\n\n\ndef next_generation(grid):\n    new_grid = []\n    for row in range(len(grid)):\n        new_row = []\n        for column in range(len(grid[0])):\n            if will_live(grid, row, column):\n                new_row.append(1)\n            else:\n                new_row.append(0)\n        new_grid.append(new_row)\n    return new_grid\n\n\nif __name__ == \"__main__\":\n    glider = [\n        [0, 1, 0, 0, 0],\n        [0, 0, 1, 0, 0],\n        [1, 1, 1, 0, 0],\n        [0, 0, 0, 0, 0],\n        [0, 0, 0, 0, 0],\n    ]\n    print_grid(glider)\n    print(count_alive(glider), \"cells are alive\")\n    print()\n    print_grid(next_generation(glider))\n","solution":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\ndef count_neighbors(grid, row, column):\n    count = 0\n    for r in range(row - 1, row + 2):\n        for c in range(column - 1, column + 2):\n            if r == row and c == column:\n                continue\n            if r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0]):\n                count += grid[r][c]\n    return count\n\n\ndef will_live(grid, row, column):\n    neighbors = count_neighbors(grid, row, column)\n    if grid[row][column] == 1:\n        return neighbors == 2 or neighbors == 3\n    return neighbors == 3\n\n\ndef next_generation(grid):\n    new_grid = []\n    for row in range(len(grid)):\n        new_row = []\n        for column in range(len(grid[0])):\n            if will_live(grid, row, column):\n                new_row.append(1)\n            else:\n                new_row.append(0)\n        new_grid.append(new_row)\n    return new_grid\n\n\ndef read_grid():\n    grid = []\n    while True:\n        line = input()\n        if line == \"\":\n            break\n        row = []\n        for character in line:\n            if character == \"#\":\n                row.append(1)\n            else:\n                row.append(0)\n        grid.append(row)\n    return grid\n\n\ndef print_generation(grid, number):\n    print(f\"Generation {number}: {count_alive(grid)} alive\")\n    print_grid(grid)\n    print()\n\n\ndef main():\n    print(\"Type the grid, one row per line, and an empty line at the end:\")\n    grid = read_grid()\n    print_generation(grid, 0)\n    print_generation(next_generation(grid), 1)\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["`read_grid` is a `while True:` loop that reads a line, and leaves the loop with `break` when `line == \"\"`. The grid starts as `[]` before the loop.","For each line that isn't empty, build a row: `for character in line:` goes through its characters, and each `#` appends 1, anything else 0. Then append the row to the grid.","`print_generation` prints `f\"Generation {number}: {count_alive(grid)} alive\"`, calls `print_grid(grid)`, and ends with `print()`. Then `main` needs only four lines: print the instruction, `grid = read_grid()`, and two calls to `print_generation`."],"tests":[{"name":"A glider typed in","stdin":".....\n..#..\n...#.\n.###.\n.....\n\n","expect":"Type the grid, one row per line, and an empty line at the end:\n.....\n..#..\n...#.\n.###.\n.....\n\nGeneration 0: 5 alive\n.....\n..#..\n...#.\n.###.\n.....\n\nGeneration 1: 5 alive\n.....\n.....\n.#.#.\n..##.\n..#..","hidden":false},{"name":"Reading a grid","stdin":"#..#\n.##.\n\n","call":"grid = read_grid()\nprint(grid)\nprint(count_alive(grid))\n","expect":"#..#\n.##.\n\n[[1, 0, 0, 1], [0, 1, 1, 0]]\n4","hidden":false},{"name":"A pattern that grows","stdin":"......\n..##..\n.##...\n..#...\n......\n\n","expect":"Type the grid, one row per line, and an empty line at the end:\n......\n..##..\n.##...\n..#...\n......\n\nGeneration 0: 5 alive\n......\n..##..\n.##...\n..#...\n......\n\nGeneration 1: 6 alive\n......\n.###..\n.#....\n.##...\n......","hidden":true},{"name":"Other characters are dead cells","stdin":"#x#\n#-#\n\n","call":"grid = read_grid()\nprint(grid)\n","expect":"#x#\n#-#\n\n[[1, 0, 1], [1, 0, 1]]","hidden":true},{"name":"Printing one generation","stdin":"","call":"print_generation([[1, 1], [1, 0]], 7)\nprint(\"end\")\n","expect":"Generation 7: 3 alive\n##\n#.\n\nend","hidden":true}],"require":[],"forbid":[]},{"id":"generations","title":"Several generations","task":"Two generations show only the beginning of the story. After reading the grid, the program now asks how many generations to show, with the prompt `Generations: `. Then it prints generation 0, the grid as it was typed, and after it each new generation up to the number asked for, each worked out from the one before.\n\nWith the input\n\n```text\n.....\n.....\n.###.\n.....\n.....\n\n2\n```\n\nthe program prints:\n\n```\nType the grid, one row per line, and an empty line at the end:\n.....\n.....\n.###.\n.....\n.....\n\nGenerations: 2\nGeneration 0: 3 alive\n.....\n.....\n.###.\n.....\n.....\n\nGeneration 1: 3 alive\n.....\n..#..\n..#..\n..#..\n.....\n\nGeneration 2: 3 alive\n.....\n.....\n.###.\n.....\n.....\n```\n\nWith 0 generations, the program prints only generation 0.\n","replaces":["A glider typed in"],"kind":"code","seed":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\ndef count_neighbors(grid, row, column):\n    count = 0\n    for r in range(row - 1, row + 2):\n        for c in range(column - 1, column + 2):\n            if r == row and c == column:\n                continue\n            if r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0]):\n                count += grid[r][c]\n    return count\n\n\ndef will_live(grid, row, column):\n    neighbors = count_neighbors(grid, row, column)\n    if grid[row][column] == 1:\n        return neighbors == 2 or neighbors == 3\n    return neighbors == 3\n\n\ndef next_generation(grid):\n    new_grid = []\n    for row in range(len(grid)):\n        new_row = []\n        for column in range(len(grid[0])):\n            if will_live(grid, row, column):\n                new_row.append(1)\n            else:\n                new_row.append(0)\n        new_grid.append(new_row)\n    return new_grid\n\n\ndef read_grid():\n    grid = []\n    while True:\n        line = input()\n        if line == \"\":\n            break\n        row = []\n        for character in line:\n            if character == \"#\":\n                row.append(1)\n            else:\n                row.append(0)\n        grid.append(row)\n    return grid\n\n\ndef print_generation(grid, number):\n    print(f\"Generation {number}: {count_alive(grid)} alive\")\n    print_grid(grid)\n    print()\n\n\ndef main():\n    print(\"Type the grid, one row per line, and an empty line at the end:\")\n    grid = read_grid()\n    print_generation(grid, 0)\n    print_generation(next_generation(grid), 1)\n\n\nif __name__ == \"__main__\":\n    main()\n","solution":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\ndef count_neighbors(grid, row, column):\n    count = 0\n    for r in range(row - 1, row + 2):\n        for c in range(column - 1, column + 2):\n            if r == row and c == column:\n                continue\n            if r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0]):\n                count += grid[r][c]\n    return count\n\n\ndef will_live(grid, row, column):\n    neighbors = count_neighbors(grid, row, column)\n    if grid[row][column] == 1:\n        return neighbors == 2 or neighbors == 3\n    return neighbors == 3\n\n\ndef next_generation(grid):\n    new_grid = []\n    for row in range(len(grid)):\n        new_row = []\n        for column in range(len(grid[0])):\n            if will_live(grid, row, column):\n                new_row.append(1)\n            else:\n                new_row.append(0)\n        new_grid.append(new_row)\n    return new_grid\n\n\ndef read_grid():\n    grid = []\n    while True:\n        line = input()\n        if line == \"\":\n            break\n        row = []\n        for character in line:\n            if character == \"#\":\n                row.append(1)\n            else:\n                row.append(0)\n        grid.append(row)\n    return grid\n\n\ndef print_generation(grid, number):\n    print(f\"Generation {number}: {count_alive(grid)} alive\")\n    print_grid(grid)\n    print()\n\n\ndef main():\n    print(\"Type the grid, one row per line, and an empty line at the end:\")\n    grid = read_grid()\n    generations = int(input(\"Generations: \"))\n    print_generation(grid, 0)\n    for number in range(1, generations + 1):\n        grid = next_generation(grid)\n        print_generation(grid, number)\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["Read the number with `int(input(\"Generations: \"))` right after `read_grid()`, and print generation 0 before a loop.","Loop with `for number in range(1, generations + 1):`. In each round, work out the next generation and print it with its number.","Each new generation is worked out from the one before, not from the first one: `grid = next_generation(grid)` replaces the old grid with the new one, ready for the next round."],"tests":[{"name":"A blinker for two generations","stdin":".....\n.....\n.###.\n.....\n.....\n\n2\n","expect":"Type the grid, one row per line, and an empty line at the end:\n.....\n.....\n.###.\n.....\n.....\n\nGenerations: 2\nGeneration 0: 3 alive\n.....\n.....\n.###.\n.....\n.....\n\nGeneration 1: 3 alive\n.....\n..#..\n..#..\n..#..\n.....\n\nGeneration 2: 3 alive\n.....\n.....\n.###.\n.....\n.....","hidden":false},{"name":"A glider for four generations","stdin":".#....\n..#...\n###...\n......\n......\n......\n\n4\n","expect":"Type the grid, one row per line, and an empty line at the end:\n.#....\n..#...\n###...\n......\n......\n......\n\nGenerations: 4\nGeneration 0: 5 alive\n.#....\n..#...\n###...\n......\n......\n......\n\nGeneration 1: 5 alive\n......\n#.#...\n.##...\n.#....\n......\n......\n\nGeneration 2: 5 alive\n......\n..#...\n#.#...\n.##...\n......\n......\n\nGeneration 3: 5 alive\n......\n.#....\n..##..\n.##...\n......\n......\n\nGeneration 4: 5 alive\n......\n..#...\n...#..\n.###..\n......\n......","hidden":true},{"name":"No generations at all","stdin":"##\n#.\n\n0\n","expect":"Type the grid, one row per line, and an empty line at the end:\n##\n#.\n\nGenerations: 0\nGeneration 0: 3 alive\n##\n#.","hidden":true},{"name":"A toad","stdin":"......\n......\n..###.\n.###..\n......\n......\n\n3\n","expect":"Type the grid, one row per line, and an empty line at the end:\n......\n......\n..###.\n.###..\n......\n......\n\nGenerations: 3\nGeneration 0: 6 alive\n......\n......\n..###.\n.###..\n......\n......\n\nGeneration 1: 6 alive\n......\n...#..\n.#..#.\n.#..#.\n..#...\n......\n\nGeneration 2: 6 alive\n......\n......\n..###.\n.###..\n......\n......\n\nGeneration 3: 6 alive\n......\n...#..\n.#..#.\n.#..#.\n..#...\n......","hidden":true}],"require":[],"forbid":[]},{"id":"stopping-early","title":"Stopping early","task":"Many patterns die out or freeze after a while, and printing the same empty grid fifty times helps nobody. Before it works out each new generation, the program now looks at the generation it printed last:\n\n- When it has no live cells, the program prints `Everything is dead.` and stops.\n- When the next generation would be exactly the same as it, the program prints `Nothing changes any more.` and stops.\n\nOtherwise it goes on as before, up to the number of generations asked for. Comparing two grids takes no loops: `==` compares two lists item by item, and the lists inside them as well, so `new_grid == grid` is `True` when every cell is the same.\n\nWith the input\n\n```text\n#...\n.#..\n....\n\n5\n```\n\nthe two lonely cells die at once, and the program prints:\n\n```\nType the grid, one row per line, and an empty line at the end:\n#...\n.#..\n....\n\nGenerations: 5\nGeneration 0: 2 alive\n#...\n.#..\n....\n\nGeneration 1: 0 alive\n....\n....\n....\n\nEverything is dead.\n```\n","kind":"code","seed":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\ndef count_neighbors(grid, row, column):\n    count = 0\n    for r in range(row - 1, row + 2):\n        for c in range(column - 1, column + 2):\n            if r == row and c == column:\n                continue\n            if r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0]):\n                count += grid[r][c]\n    return count\n\n\ndef will_live(grid, row, column):\n    neighbors = count_neighbors(grid, row, column)\n    if grid[row][column] == 1:\n        return neighbors == 2 or neighbors == 3\n    return neighbors == 3\n\n\ndef next_generation(grid):\n    new_grid = []\n    for row in range(len(grid)):\n        new_row = []\n        for column in range(len(grid[0])):\n            if will_live(grid, row, column):\n                new_row.append(1)\n            else:\n                new_row.append(0)\n        new_grid.append(new_row)\n    return new_grid\n\n\ndef read_grid():\n    grid = []\n    while True:\n        line = input()\n        if line == \"\":\n            break\n        row = []\n        for character in line:\n            if character == \"#\":\n                row.append(1)\n            else:\n                row.append(0)\n        grid.append(row)\n    return grid\n\n\ndef print_generation(grid, number):\n    print(f\"Generation {number}: {count_alive(grid)} alive\")\n    print_grid(grid)\n    print()\n\n\ndef main():\n    print(\"Type the grid, one row per line, and an empty line at the end:\")\n    grid = read_grid()\n    generations = int(input(\"Generations: \"))\n    print_generation(grid, 0)\n    for number in range(1, generations + 1):\n        grid = next_generation(grid)\n        print_generation(grid, number)\n\n\nif __name__ == \"__main__\":\n    main()\n","solution":"# Conway's Game of Life on a grid of 0s and 1s: 1 is a live cell, 0 a dead one.\n\n\ndef print_grid(grid):\n    for row in grid:\n        line = \"\"\n        for cell in row:\n            if cell == 1:\n                line += \"#\"\n            else:\n                line += \".\"\n        print(line)\n\n\ndef count_alive(grid):\n    count = 0\n    for row in grid:\n        for cell in row:\n            count += cell\n    return count\n\n\ndef count_neighbors(grid, row, column):\n    count = 0\n    for r in range(row - 1, row + 2):\n        for c in range(column - 1, column + 2):\n            if r == row and c == column:\n                continue\n            if r >= 0 and r < len(grid) and c >= 0 and c < len(grid[0]):\n                count += grid[r][c]\n    return count\n\n\ndef will_live(grid, row, column):\n    neighbors = count_neighbors(grid, row, column)\n    if grid[row][column] == 1:\n        return neighbors == 2 or neighbors == 3\n    return neighbors == 3\n\n\ndef next_generation(grid):\n    new_grid = []\n    for row in range(len(grid)):\n        new_row = []\n        for column in range(len(grid[0])):\n            if will_live(grid, row, column):\n                new_row.append(1)\n            else:\n                new_row.append(0)\n        new_grid.append(new_row)\n    return new_grid\n\n\ndef read_grid():\n    grid = []\n    while True:\n        line = input()\n        if line == \"\":\n            break\n        row = []\n        for character in line:\n            if character == \"#\":\n                row.append(1)\n            else:\n                row.append(0)\n        grid.append(row)\n    return grid\n\n\ndef print_generation(grid, number):\n    print(f\"Generation {number}: {count_alive(grid)} alive\")\n    print_grid(grid)\n    print()\n\n\ndef main():\n    print(\"Type the grid, one row per line, and an empty line at the end:\")\n    grid = read_grid()\n    generations = int(input(\"Generations: \"))\n    print_generation(grid, 0)\n    for number in range(1, generations + 1):\n        if count_alive(grid) == 0:\n            print(\"Everything is dead.\")\n            break\n        new_grid = next_generation(grid)\n        if new_grid == grid:\n            print(\"Nothing changes any more.\")\n            break\n        grid = new_grid\n        print_generation(grid, number)\n\n\nif __name__ == \"__main__\":\n    main()\n","hints":["Both checks go at the start of each round of the `for` loop, before the program prints the next generation, and both leave the loop with `break` after their message.","`count_alive(grid) == 0` tells you everything is dead. For the other check, keep the next generation in a variable of its own, `new_grid = next_generation(grid)`, so you can compare it with `grid` before it replaces it.","When `new_grid == grid`, print the message and `break`. Otherwise `grid = new_grid` and print it as before."],"tests":[{"name":"Dying out","stdin":"#...\n.#..\n....\n\n5\n","expect":"Type the grid, one row per line, and an empty line at the end:\n#...\n.#..\n....\n\nGenerations: 5\nGeneration 0: 2 alive\n#...\n.#..\n....\n\nGeneration 1: 0 alive\n....\n....\n....\n\nEverything is dead.","hidden":false},{"name":"A block never changes","stdin":"....\n.##.\n.##.\n....\n\n10\n","expect":"Type the grid, one row per line, and an empty line at the end:\n....\n.##.\n.##.\n....\n\nGenerations: 10\nGeneration 0: 4 alive\n....\n.##.\n.##.\n....\n\nNothing changes any more.","hidden":true},{"name":"Settling into a beehive","stdin":"......\n.###..\n.#....\n......\n......\n\n9\n","expect":"Type the grid, one row per line, and an empty line at the end:\n......\n.###..\n.#....\n......\n......\n\nGenerations: 9\nGeneration 0: 4 alive\n......\n.###..\n.#....\n......\n......\n\nGeneration 1: 4 alive\n..#...\n.##...\n.#....\n......\n......\n\nGeneration 2: 6 alive\n.##...\n.##...\n.##...\n......\n......\n\nGeneration 3: 6 alive\n.##...\n#..#..\n.##...\n......\n......\n\nNothing changes any more.","hidden":true},{"name":"A blinker never stops","stdin":"...\n###\n...\n\n3\n","expect":"Type the grid, one row per line, and an empty line at the end:\n...\n###\n...\n\nGenerations: 3\nGeneration 0: 3 alive\n...\n###\n...\n\nGeneration 1: 3 alive\n.#.\n.#.\n.#.\n\nGeneration 2: 3 alive\n...\n###\n...\n\nGeneration 3: 3 alive\n.#.\n.#.\n.#.","hidden":true},{"name":"Nothing alive from the start","stdin":"...\n...\n\n4\n","expect":"Type the grid, one row per line, and an empty line at the end:\n...\n...\n\nGenerations: 4\nGeneration 0: 0 alive\n...\n...\n\nEverything is dead.","hidden":true}],"require":[],"forbid":[]}]}