92 lines
2.2 KiB
Elixir
92 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
|