aoc/20/lib/days/day11.ex
2023-12-04 10:25:33 +01:00

91 lines
2.2 KiB
Elixir

defmodule AOC.Day11 do
@x_size 95
@y_size 93
def parse_input do
AOC.Util.input_lines(11, 1)
|> Enum.with_index()
|> Enum.reduce(%{}, fn {line, y}, acc ->
line
|> String.split("", trim: true)
|> Enum.with_index()
|> Enum.reject(&(elem(&1, 0) == "."))
|> Enum.map(fn {_, i} -> {false, i} end)
|> Enum.into(acc, fn {c, x} -> {{x, y}, c} end)
end)
end
def count_direct_adj(seats, {x, y}) do
[
{x - 1, y - 1},
{x - 1, y},
{x - 1, y + 1},
{x, y - 1},
{x, y + 1},
{x + 1, y - 1},
{x + 1, y},
{x + 1, y + 1}
]
|> Enum.count(fn coords -> Map.get(seats, coords) end)
end
def look_direction(seats, coords, next_fun) do
{x, y} = coords = next_fun.(coords)
if x not in 0..@x_size or y not in 0..@y_size do
false
else
case Map.get(seats, coords) do
nil -> look_direction(seats, coords, next_fun)
x -> x
end
end
end
def count_sight_adj(seats, {x, y}) do
[
fn {x, y} -> {x + 1, y + 1} end,
fn {x, y} -> {x + 1, y - 1} end,
fn {x, y} -> {x - 1, y + 1} end,
fn {x, y} -> {x - 1, y - 1} end,
fn {x, y} -> {x + 1, y} end,
fn {x, y} -> {x - 1, y} end,
fn {x, y} -> {x, y + 1} end,
fn {x, y} -> {x, y - 1} end
]
|> Enum.count(&look_direction(seats, {x, y}, &1))
end
def simulate_round(seats, count_adj, min) do
Enum.reduce(seats, {%{}, false}, fn {coords, x}, {acc, changed} ->
new =
case {x, count_adj.(seats, coords)} do
{false, 0} -> true
{true, count} when count >= min -> false
_ -> x
end
{Map.put(acc, coords, new), changed or new != x}
end)
end
def find_stable(seats, count_adj, min) do
{seats, changed} = simulate_round(seats, count_adj, min)
if changed do
find_stable(seats, count_adj, min)
else
seats
end
end
def parts do
input = parse_input()
stable = find_stable(input, &count_direct_adj/2, 4)
part1 = Enum.count(stable, &elem(&1, 1))
stable = find_stable(input, &count_sight_adj/2, 5)
part2 = Enum.count(stable, &elem(&1, 1))
{part1, part2}
end
end