AoC 2023 - Day 02
Mix.install([
{:kino, "~> 0.11.0"}
])
Section
input_p1t =
"day02-p1t.txt"
|> Kino.FS.file_path()
|> File.read!()
|> String.trim()
input =
"day02.txt"
|> Kino.FS.file_path()
|> File.read!()
|> String.trim()
defmodule Game do
defstruct id: nil, rounds: []
end
defmodule Game.Round do
defstruct red: 0, green: 0, blue: 0
end
defmodule PartA do
def solve(input) do
input
|> String.split("\n")
|> Enum.map(fn game ->
[[_game, id, data]] = Regex.scan(~r/Game (\d+): (.*)/, game)
%Game{
id: String.to_integer(id),
rounds:
data
|> String.split(";")
|> Enum.map(&parse_round/1)
}
end)
|> Enum.filter(&valid?/1)
|> Enum.map(&Map.get(&1, :id))
|> Enum.sum()
end
def solve_2(input) do
input
|> String.split("\n")
|> Enum.map(fn line ->
[part_1, part_2] = String.split(line, ":")
{"Game ", game_id} = String.split_at(part_1, 5)
rounds =
part_2
|> String.trim()
|> String.split(";")
|> Enum.map(&parse_round_match/1)
%Game{
id: String.to_integer(game_id),
rounds: rounds
}
end)
|> Enum.filter(&valid?/1)
|> Enum.map(&Map.get(&1, :id))
|> Enum.sum()
end
def parse_round(data) do
~r/(\d+) (red|green|blue)/
|> Regex.scan(data)
|> Enum.reduce(%Game.Round{}, &parse_round_item/2)
end
def parse_round_match(data) do
data
|> String.split(",")
|> Enum.reduce(%Game.Round{}, fn item, acc ->
[count, color] = item |> String.trim() |> String.split(" ")
parse_round_item([nil, count, color], acc)
end)
end
def parse_round_item([_element, count, "red"], %Game.Round{red: r, green: g, blue: b}) do
%Game.Round{red: r + String.to_integer(count), green: g, blue: b}
end
def parse_round_item([_element, count, "green"], %Game.Round{red: r, green: g, blue: b}) do
%Game.Round{red: r, green: g + String.to_integer(count), blue: b}
end
def parse_round_item([_element, count, "blue"], %Game.Round{red: r, green: g, blue: b}) do
%Game.Round{red: r, green: g, blue: b + String.to_integer(count)}
end
def valid?(%Game{rounds: rounds}) do
rounds |> Enum.map(&valid?/1) |> Enum.all?()
end
def valid?(%Game.Round{red: red, green: green, blue: blue})
when red <= 12 and green <= 13 and blue <= 14,
do: true
def valid?(_), do: false
end
[
{8, PartA.solve(input_p1t)},
{2265, PartA.solve(input), PartA.solve_2(input)}
]
defmodule PartB do
def solve(input) do
input
|> String.split("\n")
|> Enum.map(fn line ->
[_part_1, part_2] = String.split(line, ":")
part_2
|> String.trim()
|> String.split(";")
|> Enum.flat_map(&String.split(&1, ","))
|> Enum.map(&(&1 |> String.trim() |> String.split(" ")))
|> Enum.reduce({0, 0, 0}, &parse_item/2)
end)
|> Enum.map(fn {red, green, blue} ->
red * green * blue
end)
|> Enum.sum()
end
def solve_2(input) do
input
|> String.split("\n")
|> Enum.map(fn line ->
[_part_1, part_2] = String.split(line, ":")
part_2
|> String.trim()
|> String.replace(~r/(;\s|,\s)/, " ")
|> String.split(" ")
|> Enum.chunk_every(2)
|> Enum.reduce({0, 0, 0}, &parse_item/2)
end)
|> Enum.map(fn {red, green, blue} ->
red * green * blue
end)
|> Enum.sum()
end
def parse_item([count, "red"], {r, g, b}) do
{max(r, String.to_integer(count)), g, b}
end
def parse_item([count, "green"], {r, g, b}) do
{r, max(g, String.to_integer(count)), b}
end
def parse_item([count, "blue"], {r, g, b}) do
{r, g, max(b, String.to_integer(count))}
end
end
[
{2286, PartB.solve(input_p1t)},
{64097, PartB.solve(input), PartB.solve_2(input)}
]