Day 02
Mix.install([
{:kino, "~> 0.11"},
{:nimble_parsec, "~> 1.4"}
])
example_input =
Kino.Input.textarea("example input:")
|> Kino.render()
real_input = Kino.Input.textarea("real input:")
Common
parse = fn input ->
for {line, line_idx} <- input |> Kino.Input.read() |> String.split("\n") |> Enum.with_index(1),
[_, line] = String.split(line, ": "),
{set, set_idx} <- String.split(line, "; ") |> Enum.with_index(1),
count_and_color <- String.split(set, ", "),
[count, color] = String.split(count_and_color, " "),
count = String.to_integer(count),
reduce: {%{}, %{}} do
{acc, maxes} ->
{Map.update(acc, {line_idx, set_idx, color}, count, &(&1 + count)),
Map.put(maxes, line_idx, set_idx)}
end
end
Part 1
{games, maxes} =
real_input
|> then(parse)
game_max = Map.keys(maxes) |> Enum.max()
for {game, set_count} <- maxes,
set <- 1..set_count,
{color, cutoff} <- [{"red", 12}, {"green", 13}, {"blue", 14}],
reduce: Map.new(1..game_max, &{&1, true}) do
acc ->
case Map.get(games, {game, set, color}, 0) do
count when count > cutoff -> Map.delete(acc, game)
_ -> acc
end
end
|> Map.keys()
|> Enum.sum()
Part 2
{games, _maxes} =
real_input
|> then(parse)
games
|> Enum.reduce(%{}, fn {{game, _, color}, count}, acc ->
Map.update(acc, {game, color}, count, &max(&1, count))
end)
|> Enum.reduce(%{}, fn {{game, _}, min}, acc -> Map.update(acc, game, min, &(&1 * min)) end)
|> Map.values()
|> Enum.sum()
NimbleParsec
defmodule Parser do
import NimbleParsec
game_id = ignore(string("Game ")) |> integer(min: 1, max: 4) |> unwrap_and_tag(:id)
red = replace(string("red"), :red)
green = replace(string("green"), :green)
blue = replace(string("blue"), :blue)
color = choice([red, green, blue])
cube_separator = optional(string(", "))
cube =
integer(min: 1, max: 4)
|> ignore(string(" "))
|> concat(color)
|> ignore(optional(cube_separator))
round_separator = optional(string("; "))
round =
times(cube, min: 1)
|> ignore(round_separator)
|> reduce(:reduce_round)
rounds = times(round, min: 1) |> tag(:rounds)
game_separator = ascii_string([?\n], 1)
game =
game_id
|> ignore(string(": "))
|> concat(rounds)
|> ignore(game_separator)
|> wrap()
|> map({Map, :new, []})
games = times(game, min: 1)
defparsec(:parse, games |> eos())
defp reduce_round(cubes) do
cubes
|> Enum.chunk_every(2)
|> Enum.reduce(%{}, fn [count, color], acc ->
Map.update(acc, color, count, &(&1 + count))
end)
end
end
Parser.parse("""
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
""")