Day 11
Setup
Mix.install([
{:kino, "~> 0.4.1"},
{:vega_lite, "~> 0.1.1"}
])
Example input:
5483143223
2745854711
5264556173
6141336146
6357385478
4167524645
2176841721
6882881134
4846848554
5283751526
Real input:
6617113584
6544218638
5457331488
1135675587
1221353216
1811124378
1387864368
4427637262
6778645486
3682146745
input = Kino.Input.textarea("Puzzle Input")
Process Input
lines =
input
|> Kino.Input.read()
|> String.split("\n", trim: true)
grid =
for {line, row} <- Enum.with_index(lines),
{number, col} <- Enum.with_index(String.split(line, "", trim: true)),
into: %{} do
{{row, col}, {String.to_integer(number), false}}
end
""
Part 1 Module First Try
defmodule Octopuses do
def run(grid, steps \\ 1) do
grow(grid, steps, 0)
end
defp grow(_grid, 0, count) do
count
end
defp grow(grid, steps, count) do
{new_grid, new_count} =
grid
|> Map.map(fn {_key, {val, flashed}} -> {val + 1, flashed} end)
|> process()
grow(new_grid, steps - 1, count + new_count)
end
defp process(grid) do
Enum.filter(grid, fn {_key, {val, flashed}} -> val > 9 and !flashed end)
|> flash(grid)
end
defp flash([], grid) do
flashcount =
for {_point, status} <- grid,
{_val, true} <- [status] do
1
end
|> Enum.sum()
grid =
Map.map(grid, fn {_point, {_val, flashed} = status} ->
if flashed, do: {0, false}, else: status
end)
{grid, flashcount}
end
defp flash(flashes, grid) do
grid =
Enum.reduce(flashes, grid, fn {point, {val, _flashed}}, acc ->
Map.put(acc, point, {val, true})
end)
flashpoints =
for {{x, y}, _status} <- flashes do
[
{x - 1, y - 1},
{x, y - 1},
{x + 1, y - 1},
{x - 1, y},
{x + 1, y},
{x - 1, y + 1},
{x, y + 1},
{x + 1, y + 1}
]
end
|> List.flatten()
Enum.reduce(flashpoints, grid, fn point, acc ->
Map.get_and_update(acc, point, fn status ->
if is_nil(status) do
:pop
else
{val, flashed} = status
{status, {val + 1, flashed}}
end
end)
|> elem(1)
end)
|> process()
end
end
Part 1 exec
grid
|> Octopuses.run(100)
Part 2 Module First Try
defmodule Octopus do
def run(grid) do
grow(grid, 1)
end
defp grow(grid, step) do
{new_grid, count} =
grid
|> Map.map(fn {_key, {val, flashed}} -> {val + 1, flashed} end)
|> process()
if count == Enum.count(grid) do
step
else
grow(new_grid, step + 1)
end
end
defp process(grid) do
Enum.filter(grid, fn {_key, {val, flashed}} -> val > 9 and !flashed end)
|> flash(grid)
end
defp flash([], grid) do
flashcount =
for {_point, status} <- grid,
{_val, true} <- [status] do
1
end
|> Enum.sum()
grid =
Map.map(grid, fn {_point, {_val, flashed} = status} ->
if flashed, do: {0, false}, else: status
end)
{grid, flashcount}
end
defp flash(flashes, grid) do
grid =
Enum.reduce(flashes, grid, fn {point, {val, _flashed}}, acc ->
Map.put(acc, point, {val, true})
end)
flashpoints =
for {{x, y}, _status} <- flashes do
[
{x - 1, y - 1},
{x, y - 1},
{x + 1, y - 1},
{x - 1, y},
{x + 1, y},
{x - 1, y + 1},
{x, y + 1},
{x + 1, y + 1}
]
end
|> List.flatten()
Enum.reduce(flashpoints, grid, fn point, acc ->
Map.get_and_update(acc, point, fn status ->
if is_nil(status) do
:pop
else
{val, flashed} = status
{status, {val + 1, flashed}}
end
end)
|> elem(1)
end)
|> process()
end
end
Part 2 exec
Octopus.run(grid)
Process Input Jose Way
lines = input |> Kino.Input.read() |> String.split("\n", trim: true)
grid =
for {line, row} <- Enum.with_index(lines),
{energy, col} <- Enum.with_index(String.to_charlist(line)),
into: %{},
do: {{row, col}, energy - ?0}
""
Module Jose Way
defmodule Recursion do
def step(grid) do
flash(Map.keys(grid), grid, MapSet.new())
end
defp flash([{row, col} = key | keys], grid, flashed) do
value = grid[key]
cond do
is_nil(value) or key in flashed ->
flash(keys, grid, flashed)
grid[key] >= 9 ->
keys = [
{row - 1, col - 1},
{row - 1, col},
{row - 1, col + 1},
{row, col - 1},
{row, col + 1},
{row + 1, col - 1},
{row + 1, col},
{row + 1, col + 1} | keys
]
flash(keys, Map.put(grid, key, 0), MapSet.put(flashed, key))
true ->
flash(keys, Map.put(grid, key, value + 1), flashed)
end
end
defp flash([], grid, flashed) do
{grid, MapSet.size(flashed)}
end
def inspect({grid, flashes}) do
for row <- 0..9 do
IO.puts(for(col <- 0..9, do: grid[{row, col}] + ?0))
end
IO.puts("flashes: #{flashes}")
grid
end
end
Part 1 Jose Way
1..100
|> Enum.map_reduce(grid, fn _, grid ->
{grid, flashes} = Recursion.step(grid)
{flashes, grid}
end)
|> elem(0)
|> Enum.sum()
Part 2 Jose Way
Stream.iterate(1, &(&1 + 1))
|> Enum.reduce_while(grid, fn i, grid ->
case Recursion.step(grid) do
{grid, flashes} when map_size(grid) == flashes -> {:halt, i}
{grid, _flashes} -> {:cont, grid}
end
end)
VegaLite
Source: https://gist.github.com/SteffenDE/342dac043af8130cedc78e23b20a8a55
Source: https://github.com/josevalim/aoc/blob/main/2021/day-11.livemd
alias VegaLite, as: Vl
graph =
Vl.new(height: 200, width: 200)
|> Vl.mark(:circle, opacity: 0.8)
|> Vl.encode_field(:x, "x", type: :quantitative, axis: false)
|> Vl.encode_field(:y, "y", type: :quantitative, axis: false)
|> Vl.encode_field(:color, "h",
type: :quantitative,
scale: [domain: [0, 9], range: ["purple", "blue", "yellow"]]
)
|> Vl.encode_field(:size, "h",
type: :quantitative,
scale: [domain: [0, 9], range: [20, 100]]
)
|> Kino.VegaLite.new()
|> Kino.render()
Kino.VegaLite.periodically(
graph,
100,
grid,
fn grid ->
{grid, flashes} = Recursion.step(grid)
data =
Enum.map(grid, fn {{x, y}, h} ->
%{"x" => x, "y" => y, "h" => if(h == 0, do: 9, else: h - 1)}
end)
Kino.VegaLite.clear(graph)
Kino.VegaLite.push_many(graph, data)
if map_size(grid) == flashes do
:halt
else
{:cont, grid}
end
end
)