Skip to content

Instantly share code, notes, and snippets.

@sasa1977
Last active March 12, 2018 16:14
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sasa1977/7d101a5698edfd6b0dc9 to your computer and use it in GitHub Desktop.
Save sasa1977/7d101a5698edfd6b0dc9 to your computer and use it in GitHub Desktop.
Conway's Game of Life
# Works on Elixir 1.0.x
# Usage: just pust to `iex` shell, or run `elixir conway.ex`
defmodule Sum do
defstruct value: 0
def value(%Sum{value: value}), do: value
def add(%Sum{value: value} = sum, x) do
%Sum{sum | value: value + x}
end
defimpl Collectable do
def into(original) do
{
original,
fn
sum, {:cont, x} -> Sum.add(sum, x)
sum, :done -> sum
_, :halt -> :ok
end
}
end
end
end
defmodule Conway.Grid do
defstruct size: 0, data: HashSet.new
def new(size) when is_integer(size) and size > 0 do
new(random_data(size))
end
def new([[_|_] | _] = data) do
%Conway.Grid{
size: length(data),
data: data |> raw_data_to_points |> points_to_data
}
end
def size(%Conway.Grid{size: size}), do: size
def cell_status(grid, x, y) do
case HashSet.member?(grid.data, {x, y}) do
false -> 0
true -> 1
end
end
def next(grid) do
%Conway.Grid{grid |
data:
alive_plus_neighbours(grid)
|> Stream.map(&{&1, next_cell_status(grid, &1)})
|> points_to_data
}
end
defp random_data(size) do
for _ <- 0..(size - 1) do
for _ <- 0..(size - 1) do
:random.uniform(2) - 1
end
end
end
defp raw_data_to_points(data) do
data
|> Stream.with_index
|> Stream.flat_map(
fn({row, y}) ->
for {status, x} <- Stream.with_index(row),
do: {{x, y}, status}
end
)
end
defp points_to_data(points) do
for {coordinate, 1} <- points,
into: HashSet.new,
do: coordinate
end
defp next_cell_status(grid, {x, y}) do
case {cell_status(grid, x, y), alive_neighbours(grid, x, y)} do
{1, 2} -> 1
{1, 3} -> 1
{0, 3} -> 1
{_, _} -> 0
end
end
defp alive_neighbours(grid, cell_x, cell_y) do
(
for {x, y} <- neighbours(grid, {cell_x, cell_y}),
cell_status(grid, x, y) == 1,
into: %Sum{},
do: 1
)
|> Sum.value
end
defp alive_plus_neighbours(grid) do
grid.data
|> Stream.flat_map(&[&1 | neighbours(grid, &1)])
|> Enum.into(HashSet.new)
end
defp neighbours(grid, {cell_x, cell_y}) do
for x <- (cell_x - 1)..(cell_x + 1),
y <- (cell_y - 1)..(cell_y + 1),
(
x in 0..(size(grid) - 1) and
y in 0..(size(grid) - 1) and
(x != cell_x or y != cell_y)
),
do: {x, y}
end
end
defmodule Conway.TerminalGame do
def play(_grid, _name, 0), do: :ok
def play(grid, name, steps) do
grid
|> print(name)
|> Conway.Grid.next
|> play(name, steps - 1)
end
defp print(grid, name) do
IO.write "\e[2J"
IO.puts("#{name}\n")
for y <- 0..(Conway.Grid.size(grid) - 1) do
for x <- 0..(Conway.Grid.size(grid) - 1) do
case Conway.Grid.cell_status(grid, x, y) do
0 -> IO.write(" ")
1 -> IO.write("*")
end
end
IO.puts("")
end
:timer.sleep(200)
grid
end
end
patterns = %{
blinker: {40, [
[0,0,0,0,0],
[0,0,1,0,0],
[0,0,1,0,0],
[0,0,1,0,0],
[0,0,0,0,0],
]},
beacon: {40, [
[0,0,0,0,0,0],
[0,1,1,0,0,0],
[0,1,0,0,0,0],
[0,0,0,0,1,0],
[0,0,0,1,1,0],
[0,0,0,0,0,0]
]},
pulsar: {60, [
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,1,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,1,0,1,0,1,0,0,0,0,0],
[0,0,0,0,0,1,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
]},
queen_bee: {60, [
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
]},
"random 15x15": {60, 15}
}
:random.seed(:erlang.now)
for {name, {steps, data}} <- patterns do
data
|> Conway.Grid.new
|> Conway.TerminalGame.play(name, steps)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment