christhekeele
Advent of Code 2023 - Day 2
Thought I’d kick today’s thread off!
Parsing
Enum rocks, so most of my code was actually in parsing input.
Preprocessing input
Data model is:
%{
id :: integer() => [
pull :: %{
red: integer(),
green: integer(),
blue: integer()
}
]
}
I modeled each pull as a struct rather than a bare map, just so I didn’t have to write any extra code to hydrate my maps with default 0 values where input was empty for a color.
Source available here.
defmodule AoC.Day.Two.Input do
defmodule Pull do
defstruct red: 0, green: 0, blue: 0
end
def parse(input_file \\ System.fetch_env!("INPUT_FILE")) do
input_file
|> File.read!()
|> String.split("\n")
|> Enum.reject(&(&1 == ""))
|> Enum.map(&parse_game/1)
|> Map.new()
end
def parse_game("Game " <> game) do
{id, rest} = Integer.parse(game)
<<": ">> <> pulls = rest
pulls =
pulls
|> String.trim()
|> String.split(";")
|> Enum.map(&String.trim/1)
|> Enum.map(&parse_pull/1)
{id, pulls}
end
def parse_pull(pull) do
result =
pull
|> String.split(",")
|> Enum.map(&String.trim/1)
|> Enum.map(&parse_pull_color/1)
struct!(Pull, result)
end
def parse_pull_color(result) do
case Integer.parse(result) do
{num, " red"} -> {:red, num}
{num, " green"} -> {:green, num}
{num, " blue"} -> {:blue, num}
end
end
end
Part 1
Solution
input |> Enum.filter(Enum.all?) |> Enum.map |> Enum.sum. Source available here.
defmodule AoC.Day.Two.Part.One do
@red_limit 12
@green_limit 13
@blue_limit 14
def solve(input) do
input
|> Enum.filter(fn {_id, pulls} ->
Enum.all?(pulls, fn
%{red: red, green: green, blue: blue}
when red <= @red_limit and green <= @green_limit and blue <= @blue_limit ->
true
_ ->
false
end)
end)
|> Enum.map(fn {id, _} -> id end)
|> Enum.sum()
end
end
Part 2
Solution
Simpler still. Could have avoided iterating over pulls 3 times, but not too fussed about it. Source available here.
defmodule AoC.Day.Two.Part.Two do
def solve(input) do
input
|> Enum.map(fn {id, pulls} ->
min_red = pulls |> Enum.map(&Map.fetch!(&1, :red)) |> Enum.max()
min_green = pulls |> Enum.map(&Map.fetch!(&1, :green)) |> Enum.max()
min_blue = pulls |> Enum.map(&Map.fetch!(&1, :blue)) |> Enum.max()
{id, min_red * min_green * min_blue}
end)
|> Enum.map(fn {_id, power} -> power end)
|> Enum.sum()
end
end
Most Liked
adamu
After seeing part 2, we know that both parts only need the aggregate of all the rounds, so for each game I used a single map representing the maximum number of cubes seen.
|> Enum.reduce(
%{"red" => 0, "green" => 0, "blue" => 0},
&Map.merge(&1, &2, fn _colour, count1, count2 -> max(count1, count2) end
)
https://git.adamu.jp/adam/AdventOfCode/src/branch/main/2023/day2.exs
awerment
Just wanted to say I love the use of Integer.parse/1 here!
stevensonmt
My code here. The only notable thing was I decided to force myself to parse manually doing nothing but pattern matching on binaries. Painful and pointless, but also was kind of a fun challenge. Only worked because I knew the limits of the input size. It would fail if any round included any cubes of more than 99 or if there were more than 100 games. Since I knew neither of those conditions applied it was fine.
Here’s the parsing:
def parse(input) do
input
|> Day2.input()
|> Input.lines()
|> Enum.map(&parse_game/1)
|> Enum.map(fn map ->
[k] = Map.keys(map)
v =
Map.values(map)
|> hd()
|> un_nest()
{k, v}
end)
|> Enum.into(%{})
end
defp parse_game(<<"Game ", rest::binary>>), do: parse_game(rest)
defp parse_game(<<i, j, ": ", rest::binary>>) when i in 49..57 and j in 48..57 do
k = (i - 48) * 10 + (j - 48)
Map.put(%{}, k, parse_game(rest))
end
defp parse_game(<<i, ": ", rest::binary>>) when i in 49..57,
do: %{(i - 48) => parse_game(rest)}
defp parse_game(<<"100: ", rest::binary>>), do: %{100 => parse_game(rest)}
defp parse_game(<<i, " blue", rest::binary>>) when i in 49..57 do
[{"blue", i - 48} | parse_game(rest)]
end
defp parse_game(<<i, " red", rest::binary>>) when i in 49..57 do
[{"red", i - 48} | parse_game(rest)]
end
defp parse_game(<<i, " green", rest::binary>>) when i in 49..57 do
[{"green", i - 48} | parse_game(rest)]
end
defp parse_game(<<i, j, " blue", rest::binary>>) when i in 49..57 and j in 48..57 do
[{"blue", (i - 48) * 10 + (j - 48)} | parse_game(rest)]
end
defp parse_game(<<i, j, " red", rest::binary>>) when i in 49..57 and j in 48..57 do
[{"red", (i - 48) * 10 + (j - 48)} | parse_game(rest)]
end
defp parse_game(<<i, j, " green", rest::binary>>) when i in 49..57 and j in 48..57 do
[{"green", (i - 48) * 10 + (j - 48)} | parse_game(rest)]
end
defp parse_game(<<", ", rest::binary>>), do: parse_game(rest)
defp parse_game(<<"; ", rest::binary>>), do: [parse_game(rest)]
defp parse_game(""), do: []
defp un_nest(nested, m \\ %{})
defp un_nest([], m), do: [m]
defp un_nest([hd], m) when is_list(hd),
do: un_nest(hd) ++ [m]
defp un_nest([{k, v} | tl], m), do: un_nest(tl, Map.put(m, k, v))
Popular in Challenges
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








