solve 2021 day 4
This commit is contained in:
parent
3203a0387d
commit
c630a96444
2 changed files with 156 additions and 1 deletions
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
| S | M | T | W | T | F | S |
|
| S | M | T | W | T | F | S |
|
||||||
| :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
| :-: | :-: | :-: | :-: | :-: | :-: | :-: |
|
||||||
| | | | [1] | [2] | [3] | 4 |
|
| | | | [1] | [2] | [3] | [4] |
|
||||||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
|
||||||
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
|
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
|
||||||
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
|
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
|
||||||
|
@ -24,3 +24,4 @@
|
||||||
[1]: ./lib/2021/1.ex
|
[1]: ./lib/2021/1.ex
|
||||||
[2]: ./lib/2021/2.ex
|
[2]: ./lib/2021/2.ex
|
||||||
[3]: ./lib/2021/3.ex
|
[3]: ./lib/2021/3.ex
|
||||||
|
[4]: ./lib/2021/4.ex
|
||||||
|
|
154
2021/lib/2021/4.ex
Normal file
154
2021/lib/2021/4.ex
Normal file
|
@ -0,0 +1,154 @@
|
||||||
|
import AOC
|
||||||
|
|
||||||
|
defmodule BingoPlayer do
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
defstruct [:board, :uncalled_numbers, :positions]
|
||||||
|
|
||||||
|
@impl GenServer
|
||||||
|
def init(board) do
|
||||||
|
numbers =
|
||||||
|
board
|
||||||
|
|> List.flatten()
|
||||||
|
|> MapSet.new()
|
||||||
|
|
||||||
|
{board, positions} =
|
||||||
|
for {row, x} <- Enum.with_index(board), {cell, y} <- Enum.with_index(row) do
|
||||||
|
{{{x, y}, {cell, false}}, {cell, {x, y}}}
|
||||||
|
end
|
||||||
|
|> Enum.unzip()
|
||||||
|
|
||||||
|
board = Map.new(board)
|
||||||
|
positions = Map.new(positions)
|
||||||
|
|
||||||
|
state = %__MODULE__{
|
||||||
|
board: board,
|
||||||
|
uncalled_numbers: numbers,
|
||||||
|
positions: positions
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
def call_number(pid, n) do
|
||||||
|
GenServer.call(pid, {:number_called, n})
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl GenServer
|
||||||
|
def handle_call(
|
||||||
|
{:number_called, n},
|
||||||
|
_from,
|
||||||
|
%__MODULE__{board: board, uncalled_numbers: uncalled_numbers, positions: positions} =
|
||||||
|
state
|
||||||
|
) do
|
||||||
|
if MapSet.member?(uncalled_numbers, n) do
|
||||||
|
uncalled_numbers = MapSet.delete(uncalled_numbers, n)
|
||||||
|
board = Map.put(board, Map.get(positions, n), {n, true})
|
||||||
|
|
||||||
|
state = %{state | uncalled_numbers: uncalled_numbers, board: board}
|
||||||
|
|
||||||
|
if is_winning_board?(board) do
|
||||||
|
# Prevent winning boards from continuing to play
|
||||||
|
uncalled_numbers = MapSet.new()
|
||||||
|
state = %{state | uncalled_numbers: uncalled_numbers}
|
||||||
|
{:reply, {:win, score_winning_board(board, n)}, state}
|
||||||
|
else
|
||||||
|
{:reply, :hit, state}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{:reply, :noop, state}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp is_winning_board?(board) do
|
||||||
|
Enum.any?(0..4, fn x ->
|
||||||
|
Enum.all?(0..4, fn y ->
|
||||||
|
Map.get(board, {x, y}) |> then(&elem(&1, 1))
|
||||||
|
end)
|
||||||
|
end) or
|
||||||
|
Enum.any?(0..4, fn y ->
|
||||||
|
Enum.all?(0..4, fn x ->
|
||||||
|
Map.get(board, {x, y}) |> then(&elem(&1, 1))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp score_winning_board(board, last_called) do
|
||||||
|
sum =
|
||||||
|
board
|
||||||
|
|> Map.values()
|
||||||
|
|> Enum.reject(&elem(&1, 1))
|
||||||
|
|> Enum.map(&elem(&1, 0))
|
||||||
|
|> Enum.sum()
|
||||||
|
|
||||||
|
sum * last_called
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
aoc 2021, 4 do
|
||||||
|
def parse_input() do
|
||||||
|
[calls | boards] = input_string() |> String.split("\n\n", trim: true)
|
||||||
|
|
||||||
|
calls = String.split(calls, ",", trim: true) |> Enum.map(&String.to_integer/1)
|
||||||
|
|
||||||
|
boards =
|
||||||
|
for board <- boards do
|
||||||
|
for row <- String.split(board, "\n") do
|
||||||
|
String.split(row, ~r/\W+/, trim: true)
|
||||||
|
|> Enum.map(&String.to_integer/1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
{calls, boards}
|
||||||
|
end
|
||||||
|
|
||||||
|
def p1 do
|
||||||
|
{calls, boards} = parse_input()
|
||||||
|
|
||||||
|
players =
|
||||||
|
for board <- boards do
|
||||||
|
{:ok, pid} = GenServer.start_link(BingoPlayer, board)
|
||||||
|
pid
|
||||||
|
end
|
||||||
|
|
||||||
|
{:halt, score} =
|
||||||
|
for call <- calls, player <- players, reduce: :cont do
|
||||||
|
{:halt, _} = resp ->
|
||||||
|
resp
|
||||||
|
|
||||||
|
:cont ->
|
||||||
|
case BingoPlayer.call_number(player, call) do
|
||||||
|
{:win, score} -> {:halt, score}
|
||||||
|
_ -> :cont
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Enum.each(players, &GenServer.stop/1)
|
||||||
|
|
||||||
|
score
|
||||||
|
end
|
||||||
|
|
||||||
|
def p2 do
|
||||||
|
{calls, boards} = parse_input()
|
||||||
|
|
||||||
|
players =
|
||||||
|
for board <- boards do
|
||||||
|
{:ok, pid} = GenServer.start_link(BingoPlayer, board)
|
||||||
|
pid
|
||||||
|
end
|
||||||
|
|
||||||
|
[last_winning_score | _scores] =
|
||||||
|
for call <- calls, player <- players, reduce: [] do
|
||||||
|
acc ->
|
||||||
|
case BingoPlayer.call_number(player, call) do
|
||||||
|
{:win, 0} -> acc
|
||||||
|
{:win, score} -> [score | acc]
|
||||||
|
_ -> acc
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
Enum.each(players, &GenServer.stop/1)
|
||||||
|
|
||||||
|
last_winning_score
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue