diff --git a/2021/README.md b/2021/README.md index ae40510..c63d455 100644 --- a/2021/README.md +++ b/2021/README.md @@ -15,7 +15,7 @@ | S | M | T | W | T | F | S | | :-: | :-: | :-: | :-: | :-: | :-: | :-: | | | | | [1] | [2] | [3] | [4] | -| [5] | [6] | [7] | [8] | [9] | [10] | 11 | +| [5] | [6] | [7] | [8] | [9] | [10] | [11]| | 12 | 13 | 14 | 15 | 16 | 17 | 18 | | 19 | 20 | 21 | 22 | 23 | 24 | 25 | @@ -31,3 +31,4 @@ [8]: ./lib/2021/8.ex [9]: ./lib/2021/9.ex [10]: ./lib/2021/10.ex +[11]: ./lib/2021/11.ex diff --git a/2021/lib/2021/11.ex b/2021/lib/2021/11.ex new file mode 100644 index 0000000..5b28ddc --- /dev/null +++ b/2021/lib/2021/11.ex @@ -0,0 +1,148 @@ +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