Conway’s Game of Life
Mix.install([
{:kino, "~> 0.14.1"}
])
import IEx.Helpers
Rules of CGL
-
Life is a game played on a grid
-
Grids are made up of cells
-
The number of neighbors for a cell (includes above/below/left/right and corners)
-
If a cell has fewer than 2 neighbors, it will die of loneliness
-
If a cell has more than 3 neighbors, it will die of overcrowding
-
If a cell has 2 neighbors, it will stay the same.
-
If a cell has 3 neighbors, conditions are perfect and it will spring to life
Plan
-
Cell (create: new, reduce: evolve, convert: show, data: x, y, width, alive)
-
Board (create: new, reduce: evolve, convert: show, data: width, height, cells)
nil
Cell
defmodule Cell do
defstruct [x: 0, y: 0, width: 10, alive: true]
def new(opts \\ []) do
__struct__(opts)
end
def evolve(cell, neighbor_count) do
next_cell_state =
cond do
neighbor_count < 2 ->
false
neighbor_count > 3 ->
false
neighbor_count == 2 ->
cell.alive
neighbor_count == 3 ->
true
end
%{cell| alive: next_cell_state}
end
def show(cell) do
x = cell.x * cell.width
y = cell.y * cell.width
"""
"""
end
defp color(cell) do
case cell.alive do
true -> "green"
false -> "black"
end
end
end
box = Cell.new([x: 2, y: 8])
|> Cell.show()
svg =
"""
#{box}
"""
|> Kino.Image.new(:svg)
Cell.new([x: 10, y: 10, alive: false]) |> Cell.evolve(2)
Grid
defmodule Grid do
defstruct [width: 10, height: 10, cells: %{}]
def new(width, height, live_cell_locations) do
cells =
for x <- 0..(width-1), y <- 0..(height-1), into: %{} do
{{x, y}, Cell.new([x: x, y: y, alive: {x, y} in live_cell_locations])}
end
%__MODULE__{width: width, height: height, cells: cells}
end
def evolve(grid) do
cells =
for x <- 0..(grid.width-1), y <- 0..(grid.height-1), into: %{} do
old_cell = grid.cells[{x, y}]
new_cell = Cell.evolve(old_cell, neighbor_count(grid, old_cell))
{{x, y}, new_cell}
end
%{grid | cells: cells}
end
defp neighbor_count(grid, cell) do
for x <- (cell.x - 1)..(cell.x + 1),
y <- (cell.y - 1)..(cell.y + 1), {x, y} != {cell.x, cell.y} do
Map.get(grid.cells, {x, y}, Cell.new([alive: false]))
end
|> Enum.count(fn x -> x.alive end)
end
def show(grid) do
cells =
for x <- 0..(grid.width-1), y <- 0..(grid.height-1) do
Cell.show(grid.cells[{x, y}])
end
"""
#{cells}
"""
end
end
grid = Grid.new(3, 3, [{0, 1}, {1, 1}, {2, 1}])
grid
|> Grid.evolve
|> Grid.evolve
|> Grid.show
|> Kino.Image.new(:svg)
Grid.neighbor_count(grid, grid.cells[{1, 1}])