christhekeele

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

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

awerment

Just wanted to say I love the use of Integer.parse/1 here!

stevensonmt

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))

Where Next?

Popular in Challenges Top

bjorng
Note: This topic is to talk about Day 9 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can join...
New
bjorng
Note: This topic is to talk about Day 25 of the Advent of Code 2019. There is a private leaderboard for elixirforum members. You can joi...
New
DmitriyChernyavskiy
Hello everyone, I’m a new in elexir and functional language. I’m trying to implement Websocket interraction with server. On first layer...
New
bjorng
This topic is about Day 18 of the Advent of Code 2021. We have a private leaderboard (shared with users of Erlang Forums): https://adve...
New
groovyda
Today’s challenge for me was about using reduce: defmodule Prob5 do def move([[h1 | rest] = _list1, list2]) do [rest, [h1 | list2]...
New
maennchen
Ok, that was a rough one today. I haven’t found a way to improve the algorithm further. Part 1 runs in .5 seconds, Part 2 in ~ 5 minutes...
New
shritesh
This was way too easy after the last few days. Simple map, filter and count. https://github.com/shritesh/advent/blob/main/2023/06.livemd
New
antoine-duchenet
Everything went smoothly today. Nothing to change to solve part 2 because I already used memoization for part 1 (it looked like an AoC e...
New
lud
At first I was scared but I found is a simple way to compute the sides. defmodule AdventOfCode.Solutions.Y24.Day12 do alias AdventOfCo...
New
adamu
I said I was on a break, but I took a sneak peak and it looked fun so… Part 1 completes in half a millisecond with a single pass of the ...
New

Other popular topics Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
Lily
In templates/appointment/index.html.eex: &lt;%= for appointment &lt;- @appointments do %&gt; &lt;tr&gt; &lt;td&gt;&lt;%= appoi...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement