85 lines
2.3 KiB
Elixir
85 lines
2.3 KiB
Elixir
defmodule Wireworld do
|
|
@empty " "
|
|
@head "H"
|
|
@tail "t"
|
|
@conductor "."
|
|
@neighbours (for x<- -1..1, y <- -1..1, do: {x,y}) -- [{0,0}]
|
|
|
|
def set_up(string) do
|
|
lines = String.split(string, "\n", trim: true)
|
|
grid = Enum.with_index(lines)
|
|
|> Enum.flat_map(fn {line,i} ->
|
|
String.codepoints(line)
|
|
|> Enum.with_index
|
|
|> Enum.map(fn {char,j} -> {{i, j}, char} end)
|
|
end)
|
|
|> Enum.into(Map.new)
|
|
width = Enum.map(lines, fn line -> String.length(line) end) |> Enum.max
|
|
height = length(lines)
|
|
{grid, width, height}
|
|
end
|
|
|
|
# to string
|
|
defp to_s(grid, width, height) do
|
|
Enum.map_join(0..height-1, fn i ->
|
|
Enum.map_join(0..width-1, fn j -> Map.get(grid, {i,j}, @empty) end) <> "\n"
|
|
end)
|
|
end
|
|
|
|
# transition all cells simultaneously
|
|
defp transition(grid) do
|
|
Enum.into(grid, Map.new, fn {{x, y}, state} ->
|
|
{{x, y}, transition_cell(grid, state, x, y)}
|
|
end)
|
|
end
|
|
|
|
# how to transition a single cell
|
|
defp transition_cell(grid, current, x, y) do
|
|
case current do
|
|
@empty -> @empty
|
|
@head -> @tail
|
|
@tail -> @conductor
|
|
_ -> if neighbours_with_state(grid, x, y) in 1..2, do: @head, else: @conductor
|
|
end
|
|
end
|
|
|
|
# given a position in the grid, find the neighbour cells with a particular state
|
|
def neighbours_with_state(grid, x, y) do
|
|
Enum.count(@neighbours, fn {dx,dy} -> Map.get(grid, {x+dx, y+dy}) == @head end)
|
|
end
|
|
|
|
# run a simulation up to a limit of transitions, or until a recurring
|
|
# pattern is found
|
|
# This will print text to the console
|
|
def run(string, iterations\\25) do
|
|
{grid, width, height} = set_up(string)
|
|
Enum.reduce(0..iterations, {grid, %{}}, fn count,{grd, seen} ->
|
|
IO.puts "Generation : #{count}"
|
|
IO.puts to_s(grd, width, height)
|
|
|
|
if seen[grd] do
|
|
IO.puts "I've seen this grid before... after #{count} iterations"
|
|
exit(:normal)
|
|
else
|
|
{transition(grd), Map.put(seen, grd, count)}
|
|
end
|
|
end)
|
|
IO.puts "ran through #{iterations} iterations"
|
|
end
|
|
end
|
|
|
|
# this is the "2 Clock generators and an XOR gate" example from the wikipedia page
|
|
text = """
|
|
......tH
|
|
. ......
|
|
...Ht... .
|
|
....
|
|
. .....
|
|
....
|
|
tH...... .
|
|
. ......
|
|
...Ht...
|
|
"""
|
|
|
|
Wireworld.run(text)
|