— Day 11: Dumbo Octopus —
Mix.install([{:kino, "~> 0.9.4"}])
test
https://www.twitch.tv/videos/1231618729?collection=k_DLnk2tvBa-fQ
https://adventofcode.com/2021/day/11
https://en.wikipedia.org/wiki/Cellular_automaton
Takeaway: reduce problem dimensionality (dont solve 2D when you can solve a 1D problem)
test = Kino.Input.textarea("test")
puzzle1 = Kino.Input.textarea("step1")
lines_to_grid = fn lines ->
lines =
lines
|> Kino.Input.read()
|> String.split("\n")
|> Enum.map(&(String.split(&1, "", trim: true) |> Enum.map(fn s -> String.to_integer(s) end)))
for {line, row} <- Enum.with_index(lines),
{position, col} <- Enum.with_index(line),
into: %{},
do: {{row, col}, position}
end
lines =
test
|> Kino.Input.read()
|> String.split("\n", trim: true)
grid =
for {line, row} <- Enum.with_index(lines),
{position, col} <- Enum.with_index(String.to_charlist(line)),
into: %{},
do: {{row, col}, position - ?0}
defmodule Flash do
def step(grid, steps), do: step(grid, steps, 0)
def step(grid, 0, flashes), do: {grid, flashes}
def step(grid, steps, flashes) do
grid = Enum.map(grid, &incr_octo/1) |> Enum.into(%{})
# their neibs get +1, do flash(grid)
flashers = get_flashers(grid)
new_grid = spread(grid, flashers)
# part2
# sync? = Map.values(new_grid) |> Enum.sum() |> Kernel.==(0)
# |> IO.inspect()
# if sync?,
# do: 1000 - steps + 1,
# else:
step(new_grid, steps - 1, flashes + Enum.count(get_flashers(new_grid)))
end
def spread(grid, []), do: grid
def spread(grid, [{{row, col}, _} = flasher | flashers]) do
# IO.inspect(flasher)
# aka Moore neighborhood https://en.wikipedia.org/wiki/Moore_neighborhood
neibs =
[
{row - 1, col - 1},
{row - 1, col},
{row - 1, col + 1},
{row, col + 1},
{row + 1, col + 1},
{row + 1, col},
{row + 1, col - 1},
{row, col - 1}
]
|> Enum.map(&{&1, Map.get(grid, &1)})
|> Enum.filter(&(elem(&1, 1) == 0))
|> Enum.map(&incr_octo/1)
new_grid = Map.merge(grid, Enum.into(neibs, %{}))
spread(new_grid, get_flashers(neibs) ++ flashers)
end
def incr_octo({pos, 0}), do: {pos, 0}
def incr_octo({pos, 9}), do: {pos, 0}
def incr_octo({pos, val}), do: {pos, val + 1}
def get_flashers(grid), do: Enum.filter(grid, fn {_, val} -> val == 0 end)
end
# 1647?
# 348
puzzle1
|> lines_to_grid.()
|> Flash.step(100)
Part 1 José
jose_test = Kino.Input.textarea("test")
lines =
jose_test
|> Kino.Input.read()
|> String.split("\n", trim: true)
grid =
for {line, row} <- Enum.with_index(lines),
{position, col} <- Enum.with_index(String.to_charlist(line)),
into: %{},
do: {{row, col}, position - ?0}
:ok
defmodule Jose2 do
def step(grid) do
flash(grid, Map.keys(grid), MapSet.new())
end
def flash(grid, [{row, col} = key | keys], flashed) do
value = grid[key]
cond do
is_nil(value) or key in flashed ->
flash(grid, keys, flashed)
grid[key] >= 9 ->
new_keys = [
{row - 1, col - 1},
{row - 1, col},
{row - 1, col + 1},
{row, col + 1},
{row + 1, col + 1},
{row + 1, col},
{row + 1, col - 1},
{row, col - 1}
| keys
]
flash(Map.put(grid, key, 0), new_keys, MapSet.put(flashed, key))
value ->
flash(Map.put(grid, key, value + 1), keys, flashed)
end
end
def flash(grid, [], flashed) do
{grid, MapSet.size(flashed)}
end
end
1..10
|> Enum.map_reduce(grid, fn _, grid ->
{grid, flashes} = Jose2.step(grid)
{flashes, grid}
end)
|> elem(0)
|> Enum.sum()
Part 2 José
Stream.iterate(1, &(&1 + 1))
|> Enum.reduce_while(grid, fn i, grid ->
case Jose2.step(grid) do
{grid, flashes} when map_size(grid) == flashes -> {:halt, i}
{grid, _flashes} -> {:cont, grid}
end
end)