Day 4
Part 1
input =
IO.gets("input: ")
|> String.trim()
|> String.split(",")
|> Enum.map(&Integer.parse(&1))
|> Enum.map(fn {number, _rest} -> number end)
boards =
IO.getn("boards: ", 1_000_000)
|> String.split("\n\n")
|> Enum.map(&String.split(&1, "\n", trim: true))
|> Enum.map(fn board ->
Enum.map(board, fn line ->
line
|> String.split(" ", trim: true)
|> Enum.map(&Integer.parse(&1))
|> Enum.map(fn {number, _rest} -> {number, false} end)
end)
end)
defmodule AdventOfCode.DayFour do
def solve_part_one(input, boards) do
solve(input, boards)
end
def solve_part_two(input, boards) do
solve_v2(input, boards)
end
defp solve([], boards), do: boards
defp solve([current | rest], boards) do
case check_boards(boards, current) do
{:ok, solution} -> solution
{:cont, marked_boards} -> solve(rest, marked_boards)
end
end
defp check_boards(boards, current) do
boards =
boards
|> Enum.map(&mark_board(&1, current))
Enum.reduce(boards, {:cont, boards}, fn board, acc ->
if check_board(board) do
{:ok, get_solution(board, current)}
else
acc
end
end)
end
defp solve_v2([], boards), do: boards
defp solve_v2([current | rest], boards) do
case check_boards_v2(boards, current) do
{:ok, solution} -> solution
{:cont, marked_boards} -> solve_v2(rest, marked_boards)
end
end
defp check_boards_v2([board], current) do
board = mark_board(board, current)
if check_board(board) do
{:ok, get_solution(board, current)}
else
{:cont, [board]}
end
end
defp check_boards_v2(boards, current) do
boards =
boards
|> Enum.map(&mark_board(&1, current))
|> Enum.filter(fn board -> !check_board(board) end)
{:cont, boards}
end
defp mark_board(board, current) do
Enum.map(
board,
fn line ->
Enum.map(
line,
fn {line_number, status} ->
case status do
false -> {line_number, line_number == current}
true -> {line_number, true}
end
end
)
end
)
end
defp check_board(board) do
has_winning_col?(board) || has_winning_row?(board)
end
defp has_winning_col?(board) do
Enum.any?(board, fn line -> check_col(line) end)
end
defp has_winning_row?(board) do
board
|> Enum.zip()
|> Enum.map(&Tuple.to_list(&1))
|> has_winning_col?()
end
defp check_col(line) do
line_count = Enum.count(line)
count =
line
|> Enum.filter(&is_marked?(&1))
|> Enum.count()
count == line_count
end
defp get_solution(board, current) do
unmarked_sum =
Enum.reduce(board, 0, fn line, acc ->
line_sum =
line
|> Enum.filter(fn el -> !is_marked?(el) end)
|> Enum.map(fn {number, _status} -> number end)
|> Enum.sum()
line_sum + acc
end)
unmarked_sum * current
end
defp is_marked?({_number, true}), do: true
defp is_marked?({_number, false}), do: false
end
IO.puts(AdventOfCode.DayFour.solve_part_one(input, boards))
IO.puts(AdventOfCode.DayFour.solve_part_two(input, boards))
ExUnit.start(autorun: false)
defmodule DayFourTest do
use ExUnit.Case, async: true
@example_input [
7,
4,
9,
5,
11,
17,
23,
2,
0,
14,
21,
24,
10,
16,
13,
6,
15,
25,
12,
22,
18,
20,
8,
19,
3,
26,
1
]
@example_boards [
[
[{22, false}, {13, false}, {17, false}, {11, false}, {0, false}],
[{8, false}, {2, false}, {23, false}, {4, false}, {24, false}],
[{21, false}, {9, false}, {14, false}, {16, false}, {7, false}],
[{6, false}, {10, false}, {3, false}, {18, false}, {5, false}],
[{1, false}, {12, false}, {20, false}, {15, false}, {19, false}]
],
[
[{3, false}, {15, false}, {0, false}, {2, false}, {22, false}],
[{9, false}, {18, false}, {13, false}, {17, false}, {5, false}],
[{19, false}, {8, false}, {7, false}, {25, false}, {23, false}],
[{20, false}, {11, false}, {10, false}, {24, false}, {4, false}],
[{14, false}, {21, false}, {16, false}, {12, false}, {6, false}]
],
[
[{14, false}, {21, false}, {17, false}, {24, false}, {4, false}],
[{10, false}, {16, false}, {15, false}, {9, false}, {19, false}],
[{18, false}, {8, false}, {23, false}, {26, false}, {20, false}],
[{22, false}, {11, false}, {13, false}, {6, false}, {5, false}],
[{2, false}, {0, false}, {12, false}, {3, false}, {7, false}]
]
]
test "part 1" do
assert 4512 = AdventOfCode.DayFour.solve_part_one(@example_input, @example_boards)
end
test "part 2" do
assert 1924 = AdventOfCode.DayFour.solve_part_two(@example_input, @example_boards)
end
end
ExUnit.run()