AoC 2024, Day 2
Section
defmodule Round do
defstruct red: 0, green: 0, blue: 0
def parse(round) when is_binary(round) do
String.split(round, ", ")
|> Enum.map(fn entry ->
[n, color] = String.split(entry)
{String.to_existing_atom(color), String.to_integer(n)}
end)
|> then(fn fields -> struct!(Round, fields) end)
end
end
{:module, Round, <<70, 79, 82, 49, 0, 0, 11, ...>>, {:parse, 1}}
defmodule Game do
defstruct [:id, :rounds]
def parse(game) when is_binary(game) do
[id, rounds] = String.split(game, ": ")
["Game", id] = String.split(id)
rounds = String.split(rounds, "; ") |> Enum.map(&Round.parse/1)
%Game{id: String.to_integer(id), rounds: rounds}
end
@doc """
Checks if `game` is possible given a max number of cubes of each color
specified in `limit` as a `Round` struct.
"""
def possible?(%__MODULE__{} = game, %Round{} = limit) do
Enum.all?(game.rounds, fn round ->
round.red <= limit.red && round.green <= limit.green && round.blue <= limit.blue
end)
end
@doc """
Returns a `Round` struct containing the minimal required number of cubes
of each color for `game` to be possible.
"""
def required(%__MODULE__{} = game) do
Enum.reduce(game.rounds, %Round{}, fn round, required ->
%Round{
red: max(round.red, required.red),
green: max(round.green, required.green),
blue: max(round.blue, required.blue)
}
end)
end
end
{:module, Game, <<70, 79, 82, 49, 0, 0, 19, ...>>, {:required, 1}}
defmodule D02 do
def read_input() do
Path.join(__DIR__, "inputs/d02")
|> File.read!()
|> String.split("\n", trim: true)
|> Enum.map(&Game.parse/1)
end
@p1_limit %Round{red: 12, green: 13, blue: 14}
def p1(games) do
games
|> Enum.filter(&Game.possible?(&1, @p1_limit))
|> Enum.map(fn g -> g.id end)
|> Enum.sum()
end
def p2(games) do
Enum.map(games, fn game ->
%Round{red: red, green: green, blue: blue} = Game.required(game)
red * green * blue
end)
|> Enum.sum()
end
end
{:module, D02, <<70, 79, 82, 49, 0, 0, 12, ...>>, {:p2, 1}}
ExUnit.start(autorun: false)
defmodule D01Test do
use ExUnit.Case, async: true
test "parser works" do
game = "Game 1: 2 red, 2 green; 6 red, 3 green; 2 red, 1 green, 2 blue; 1 red"
assert Game.parse(game) == %Game{
id: 1,
rounds: [
%Round{red: 2, green: 2},
%Round{red: 6, green: 3},
%Round{red: 2, green: 1, blue: 2},
%Round{red: 1}
]
}
end
@test_games_raw """
Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
"""
@test_games String.split(@test_games_raw, "\n", trim: true) |> Enum.map(&Game.parse/1)
test "part 1 works" do
assert D02.p1(@test_games) == 8
assert D02.read_input() |> D02.p1() == 2169
end
test "part 2 works" do
assert D02.p2(@test_games) == 2286
assert D02.read_input() |> D02.p2() == 60948
end
end
ExUnit.run()
...
Finished in 0.02 seconds (0.02s async, 0.00s sync)
3 tests, 0 failures
Randomized with seed 793685
%{total: 3, failures: 0, excluded: 0, skipped: 0}