Advent of Code 2021 - Day 4
Utils
defmodule Utils do
def read_textarea(name) do
Stream.iterate("", fn _ -> IO.gets(name) end)
|> Stream.take_while(&(&1 != :eof))
|> Enum.join("\r\n")
end
end
{:module, Utils, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:read_textarea, 1}}
Part 1
defmodule Board do
def mark(board, drawn_number) do
for row <- board do
for number <- row do
if drawn_number == number do
-1
else
number
end
end
end
end
def score(board, last_number) do
board
|> List.flatten()
|> Enum.filter(fn number -> number != -1 end)
|> Enum.sum()
|> then(fn sum -> sum * last_number end)
end
def bingo?(board) do
cond do
row_bingo?(board) -> true
col_bingo?(board) -> true
true -> false
end
end
defp row_bingo?(board) do
Enum.any?(board, fn row ->
Enum.all?(row, fn number ->
number == -1
end)
end)
end
defp col_bingo?(board) do
Enum.any?(0..4, fn col_num ->
Enum.all?(0..4, fn row_num ->
number =
board
|> Enum.at(row_num)
|> Enum.at(col_num)
number == -1
end)
end)
end
end
defmodule Day4 do
def solve(input) do
{drawn_numbers, boards} = parse_input(input)
{winner, last_number} = find_winning_board(drawn_numbers, boards)
Board.score(winner, last_number)
end
defp find_winning_board([number | drawn_numbers], boards) do
boards = Enum.map(boards, &Board.mark(&1, number))
case Enum.find(boards, &Board.bingo?/1) do
nil -> find_winning_board(drawn_numbers, boards)
winner -> {winner, number}
end
end
def parse_input(input) do
{moves, input} = parse_moves(input)
boards = parse_boards(input)
{moves, boards}
end
defp parse_moves(input) do
[moves_str, input] = String.split(input, ~r{\s}, trim: true, parts: 2)
moves =
moves_str
|> String.split(",")
|> Enum.map(&String.to_integer/1)
{moves, String.trim(input)}
end
defp parse_boards(input) do
input
|> String.split(~r{\s}, trim: true)
|> Enum.map(&String.to_integer/1)
|> Enum.chunk_every(5)
|> Enum.chunk_every(5)
end
end
Day4.solve(Utils.read_textarea("example_input"))
Day4.solve(Utils.read_textarea("my_input"))
89001
Part2
defmodule Day4.Part2 do
def solve(input) do
{drawn_numbers, boards} = Day4.parse_input(input)
{winner, last_number} = find_last_winning_board(drawn_numbers, boards)
Board.score(winner, last_number)
end
defp find_last_winning_board([number | drawn_numbers], boards) do
{winning_boards, boards} =
boards
|> Enum.map(&Board.mark(&1, number))
|> Enum.split_with(&Board.bingo?/1)
case boards do
[] -> {Enum.at(winning_boards, 0), number}
_ -> find_last_winning_board(drawn_numbers, boards)
end
end
end
Day4.Part2.solve(Utils.read_textarea("example_input"))
Day4.Part2.solve(Utils.read_textarea("my_input"))
7296