Advent of Code 2021 day 04
Setup
Mix.install([
{:kino, github: "livebook-dev/kino"}
])
input = Kino.Input.textarea("Please paste your input file:")
Part 1
defmodule Day4 do
def build_board(line) do
list = flatten(line)
for i <- 0..4, j <- 0..4, into: %{} do
{[i, j], {Enum.at(list, i * 5 + j), false}}
end
end
defp flatten(line) do
line
|> String.split("\n")
|> Enum.flat_map(&String.split(&1, " ", trim: true))
end
def col_marks(board) do
for i <- 0..4, do: for(j <- 0..4, do: board |> Map.get([j, i]) |> then(&elem(&1, 1)))
end
def row_marks(board) do
for i <- 0..4, do: for(j <- 0..4, do: board |> Map.get([i, j]) |> then(&elem(&1, 1)))
end
def mark(board, draw) do
board
|> Enum.find(fn {_k, {str, _}} -> str == draw end)
|> then(&maybe_update(board, &1))
end
defp maybe_update(board, nil), do: board
defp maybe_update(board, {key, {draw, _}}) do
Map.replace(board, key, {draw, true})
end
def bingo?(board) do
lines = row_marks(board) ++ col_marks(board)
Enum.any?(lines, &Enum.all?/1)
end
def unmarked_points(board) do
board
|> Map.values()
|> Enum.reject(&elem(&1, 1))
|> Enum.map(&elem(&1, 0))
|> Enum.map(&String.to_integer/1)
|> Enum.sum()
end
def score({board, draw}) do
board
|> unmarked_points()
|> then(fn i -> i * String.to_integer(draw) end)
end
end
[draw_line | board_str] =
input
|> Kino.Input.read()
|> String.split("\n\n", trim: true)
draws = String.split(draw_line, ",", trim: true)
boards = Enum.map(board_str, &Day4.build_board/1)
Enum.reduce_while(draws, boards, fn draw, boards ->
boards = Enum.map(boards, &Day4.mark(&1, draw))
if board = Enum.find(boards, &Day4.bingo?/1) do
{:halt, {board, draw}}
else
{:cont, boards}
end
end)
|> then(&Day4.score/1)
Part 2
Enum.reduce_while(draws, boards, fn draw, boards ->
boards = Enum.map(boards, &Day4.mark(&1, draw))
case Enum.reject(boards, &Day4.bingo?/1) do
[] ->
[board] = boards
{:halt, {board, draw}}
boards ->
{:cont, boards}
end
end)
|> Day4.score()