Day 4: Giant Squid
Section
defmodule Setup do
def get_input(prompt) do
case IO.gets(prompt) do
:eof -> ""
line -> line <> get_input(prompt)
end
end
def parse_input(lines) do
[numbers | boards] = String.split(lines, "\n", trim: true)
numbers = String.split(numbers, ",", trim: true) |> Enum.map(&String.to_integer/1)
boards =
boards
|> Enum.map(fn board_input ->
String.split(board_input, " ", trim: true)
|> Enum.map(&String.to_integer/1)
end)
|> Enum.chunk_every(5)
{numbers,
Enum.zip(boards, Enum.map(boards, &transpose/1)) |> Enum.map(fn {b1, b2} -> b1 ++ b2 end)}
end
def transpose(board) do
board
|> Enum.zip()
|> Enum.map(&Tuple.to_list/1)
end
end
{numbers, boards} = Setup.get_input("Input") |> Setup.parse_input()
defmodule Bingo do
def winner(numbers, boards) do
numbers
|> Enum.reduce_while(boards, fn n, boards ->
boards = Enum.map(boards, &play_board(&1, n))
if board = Enum.find(boards, &wins?/1) do
{:halt, score(board, n)}
else
{:cont, boards}
end
end)
end
def last_winner(numbers, boards) do
numbers
|> Enum.reduce_while(boards, fn n, boards ->
boards = Enum.map(boards, &play_board(&1, n))
case Enum.reject(boards, &wins?/1) do
[] ->
[board] = boards
{:halt, score(board, n)}
boards ->
{:cont, boards}
end
end)
end
def play_board(board, number), do: Enum.map(board, &List.delete(&1, number))
def wins?(board), do: [] in board
def score(board, number), do: div(Enum.sum(List.flatten(board)), 2) * number
end
Bingo.winner(numbers, boards)
Bingo.last_winner(numbers, boards)
Processes
{:ok, _} = Registry.start_link(keys: :duplicate, name: BingoMachine)
defmodule BingoRow do
use Agent
def start_link(numbers) do
Agent.start_link(fn -> numbers end)
end
def stamp(pid, number) do
Agent.update(fn
[] -> )
end
end
ExUnit.start()
defmodule Test do
use ExUnit.Case
@test_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
22 13 17 11 0
8 2 23 4 24
21 9 14 16 7
6 10 3 18 5
1 12 20 15 19
3 15 0 2 22
9 18 13 17 5
19 8 7 25 23
20 11 10 24 4
14 21 16 12 6
14 21 17 24 4
10 16 15 9 19
18 8 23 26 20
22 11 13 6 5
2 0 12 3 7
"""
setup do
{:ok, input: Setup.parse_input(@test_input)}
end
test "first winner", %{input: {number, boards}} do
assert Bingo.winner(number, boards) == 4512
end
test "last winner", %{input: {number, boards}} do
assert Bingo.last_winner(number, boards) == 1924
end
end
ExUnit.run()