AoC20/lib/day11/day11.ex

120 lines
2.8 KiB
Elixir

defmodule Day11 do
@example """
L.LL.LL.LL
LLLLLLL.LL
L.L.L..L..
LLLL.LL.LL
L.LL.LL.LL
L.LLLLL.LL
..L.L.....
LLLLLLLLLL
L.LLLLLL.L
L.LLLLL.LL
"""
def part1(example \\ false) do
parse_input(example)
|> find_final_state(&count_adjacent_occupied/3, 4)
|> count_occupied()
end
def part2(example \\ false) do
parse_input(example)
|> find_final_state(&count_visible_occupied/3, 5)
|> count_occupied()
# |> step(&count_visible_occupied/3, 5)
# |> step(&count_visible_occupied/3, 5)
# |> Enum.each(&IO.puts/1)
end
def parse_input(example) do
if(example, do: @example, else: File.read!("lib/day11/input.txt"))
|> String.trim()
|> String.split()
|> Enum.map(&String.to_charlist/1)
end
def find_final_state(map, count_fn, empty_threshold) do
case step(map, count_fn, empty_threshold) do
^map -> map
next -> find_final_state(next, count_fn, empty_threshold)
end
end
def step(map, count_fn, empty_threshold) do
map
|> Enum.with_index()
|> Enum.map(fn {row, row_index} ->
row
|> Enum.with_index()
|> Enum.map(fn {el, col_index} ->
if el == ?. do
?.
else
count = count_fn.(map, row_index, col_index)
case count do
0 -> ?#
c when c >= empty_threshold -> ?L
_ -> el
end
end
end)
end)
end
def count_adjacent_occupied(map, row, col) do
max(0, row - 1)..min(row + 1, length(map) - 1)
# |> IO.inspect()
|> Enum.map(fn test_row ->
# IO.inspect(test_row)
map_test_row = Enum.at(map, test_row)
max(0, col - 1)..min(col + 1, length(map_test_row))
|> Enum.reject(fn c -> test_row == row && c == col end)
|> Enum.count(fn test_col ->
Enum.at(map_test_row, test_col) == ?#
end)
end)
|> Enum.sum()
end
@directions -1..1
|> Enum.flat_map(fn dr ->
-1..1
|> Enum.map(fn dc -> {dr, dc} end)
end)
|> List.delete({0, 0})
def count_visible_occupied(map, row, col) do
Enum.count(@directions, fn {dr, dc} = dir ->
get_visible_seat(map, row + dr, col + dc, dir) == ?#
end)
end
def get_visible_seat(_, -1, _, _), do: nil
def get_visible_seat(map, row, _, _) when row == length(map), do: nil
def get_visible_seat(_, _, -1, _), do: nil
def get_visible_seat(map, _, col, _) when col == length(hd(map)), do: nil
def get_visible_seat(map, row, col, {dr, dc} = dir) do
map
|> Enum.at(row)
|> Enum.at(col)
|> case do
?. ->
get_visible_seat(map, row + dr, col + dc, dir)
el ->
el
end
end
def count_occupied(map) do
map
|> Enum.flat_map(& &1)
|> Enum.count(&(&1 == ?#))
end
end