Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Conway's Game of Life

life.livemd

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}])