148 lines
3.4 KiB
Elixir
148 lines
3.4 KiB
Elixir
import AOC
|
|
|
|
aoc 2021, 11 do
|
|
use AOCHelpers
|
|
@h 10
|
|
@w 10
|
|
|
|
def input() do
|
|
lists =
|
|
input_string()
|
|
|> String.split("\n", trim: true)
|
|
|> Enum.map(fn line ->
|
|
line
|
|
|> String.split("", trim: true)
|
|
|> Enum.map(&String.to_integer/1)
|
|
end)
|
|
|
|
map =
|
|
for {row, i} <- Enum.with_index(lists, 1), {cell, j} <- Enum.with_index(row, 1) do
|
|
{{i, j}, cell}
|
|
end
|
|
|> Map.new()
|
|
|
|
map
|
|
end
|
|
|
|
def n({i, j}), do: {i - 1, j}
|
|
def e({i, j}), do: {i, j + 1}
|
|
def s({i, j}), do: {i + 1, j}
|
|
def w({i, j}), do: {i, j - 1}
|
|
def ne(pos), do: pos |> n() |> e()
|
|
def se(pos), do: pos |> s() |> e()
|
|
def sw(pos), do: pos |> s() |> w()
|
|
def nw(pos), do: pos |> n() |> w()
|
|
|
|
def neighbor_positions(map, pos) do
|
|
[&n/1, &ne/1, &e/1, &se/1, &s/1, &sw/1, &w/1, &nw/1]
|
|
|> Enum.map(&apply(&1, [pos]))
|
|
|> Enum.filter(&Map.has_key?(map, &1))
|
|
end
|
|
|
|
def neighbor_cells(map, pos) do
|
|
neighbor_positions(map, pos)
|
|
|> Enum.map(&Map.get(map, &1))
|
|
end
|
|
|
|
def tick(map) do
|
|
for {pos, cell} <- map, reduce: map do
|
|
acc -> Map.put(acc, pos, cell + 1)
|
|
end
|
|
|> simulate()
|
|
|> then(fn {map, flashes} ->
|
|
map =
|
|
for {pos, cell} <- map, reduce: map do
|
|
map ->
|
|
if cell > 9 do
|
|
Map.put(map, pos, 0)
|
|
else
|
|
map
|
|
end
|
|
end
|
|
|
|
{map, flashes}
|
|
end)
|
|
end
|
|
|
|
def simulate(map) do
|
|
flashed =
|
|
Enum.filter(map, fn {_, cell} -> cell > 9 end)
|
|
|> Enum.map(&elem(&1, 0))
|
|
|> MapSet.new()
|
|
|
|
{map, flashed} =
|
|
for pos <- flashed, neighbor <- neighbor_positions(map, pos), reduce: {map, flashed} do
|
|
{map, flashed} -> increase_power_level(map, flashed, neighbor)
|
|
end
|
|
|
|
{map, MapSet.size(flashed)}
|
|
end
|
|
|
|
def increase_power_level(map, flashed, pos) do
|
|
cell = Map.get(map, pos) + 1
|
|
map = Map.put(map, pos, cell)
|
|
|
|
if cell > 9 and not MapSet.member?(flashed, pos) do
|
|
flashed = MapSet.put(flashed, pos)
|
|
|
|
for neighbor <- neighbor_positions(map, pos), reduce: {map, flashed} do
|
|
{map, flashed} -> increase_power_level(map, flashed, neighbor)
|
|
end
|
|
else
|
|
{map, flashed}
|
|
end
|
|
end
|
|
|
|
def render_cell(0), do: " "
|
|
def render_cell(1), do: "\u2581"
|
|
def render_cell(2), do: "\u2582"
|
|
def render_cell(3), do: "\u2583"
|
|
def render_cell(4), do: "\u2584"
|
|
def render_cell(5), do: "\u2585"
|
|
def render_cell(6), do: "\u2586"
|
|
def render_cell(7), do: "\u2587"
|
|
def render_cell(8), do: "\u2588"
|
|
def render_cell(9), do: "\u2592"
|
|
|
|
def render(map) do
|
|
for i <- 1..@h do
|
|
for j <- 1..@w, cell = Map.get(map, {i, j}), into: "" do
|
|
render_cell(cell)
|
|
end
|
|
end
|
|
|> Enum.join("\n")
|
|
end
|
|
|
|
def p1 do
|
|
start = input()
|
|
|
|
{_, flashes} =
|
|
for i <- 1..100, reduce: {start, 0} do
|
|
{curr, flashes} ->
|
|
IO.puts(IO.ANSI.clear())
|
|
IO.puts("p1: step #{i}, flashed: #{flashes}")
|
|
IO.puts(render(curr))
|
|
Process.sleep(50)
|
|
{next, new_flashes} = tick(curr)
|
|
{next, flashes + new_flashes}
|
|
end
|
|
|
|
flashes
|
|
end
|
|
|
|
def p2 do
|
|
start = input()
|
|
|
|
Stream.unfold({start, 1}, fn {curr, step} ->
|
|
IO.puts(IO.ANSI.clear())
|
|
IO.puts("p2: step #{step}")
|
|
IO.puts(render(curr))
|
|
Process.sleep(10)
|
|
|
|
{next, flashes} = tick(curr)
|
|
|
|
if flashes == 100, do: nil, else: {{curr, step}, {next, step + 1}}
|
|
end)
|
|
|> Stream.run()
|
|
end
|
|
end
|