Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Advent of Code - Day 2

2023_day2.livemd

Advent of Code - Day 2

Mix.install([
  {:kino_aoc, "~> 0.1"}
])

Introduction

–> Content

Puzzle

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "2", System.fetch_env!("LB_AOC_SESSION"))

Parser

Code - Parser

defmodule Parser do
  def parse(input) do
  end
end

Tests - Parser

ExUnit.start(autorun: false)

defmodule ParserTest do
  use ExUnit.Case, async: true
  import Parser

  @input ""
  @expected nil

  test "parse test" do
    actual = parse(@input)
    assert actual == @expected
  end
end

ExUnit.run()

Part One

Code - Part 1

defmodule PartOne do
  def solve(input) do
    IO.puts("--- Part One ---")
    IO.puts("Result: #{run(input)}")
  end

  def run(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn game_string ->
      game_hash = input_to_hash(game_string)
      IO.inspect(game_string)
      IO.inspect(game_hash)

      if possible?(game_hash) do
        IO.puts(game_id(game_string))
        IO.puts("")
        game_id(game_string)
      else
        IO.puts("")
        0
      end
    end)
    |> Enum.sum()
  end

  defp input_to_hash(game_string) do
    %{
      red: counts_of(game_string, "red"),
      green: counts_of(game_string, "green"),
      blue: counts_of(game_string, "blue")
    }
  end

  defp counts_of(game_string, color) do
    Regex.scan(~r/(\d+) #{color}/, game_string)
    |> Enum.map(fn matches ->
      Enum.at(matches, 1) |> String.to_integer()
    end)
  end

  defp possible?(bag, red \\ 12, green \\ 13, blue \\ 14) do
    Enum.all?(bag[:red], &amp;(&amp;1 <= red)) &amp;&amp;
      Enum.all?(bag[:green], &amp;(&amp;1 <= green)) &amp;&amp;
      Enum.all?(bag[:blue], &amp;(&amp;1 <= blue))
  end

  defp game_id(game_string) do
    Regex.run(~r/Game (\d+):/, game_string)
    |> List.last()
    |> String.to_integer()
  end
end

Tests - Part 1

ExUnit.start(autorun: false)

defmodule PartOneTest do
  use ExUnit.Case, async: true
  import PartOne

  @input """
  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
  """
  @expected 8

  test "simple example" do
    actual = run(@input)
    assert actual == @expected
  end
end

ExUnit.run()

Solution - Part 1

PartOne.solve(puzzle_input)

Part Two

Code - Part 2

defmodule PartTwo do
  def solve(input) do
    IO.puts("--- Part Two ---")
    IO.puts("Result: #{run(input)}")
  end

  def run(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn game_string ->
      game_hash = input_to_hash(game_string)
      Map.values(game_hash) |> Enum.product()
    end)
    |> Enum.sum()
  end

  defp input_to_hash(game_string) do
    %{
      red: minimum_bag_count_for(game_string, "red"),
      green: minimum_bag_count_for(game_string, "green"),
      blue: minimum_bag_count_for(game_string, "blue")
    }
  end

  defp minimum_bag_count_for(game_string, color) do
    counts_of(game_string, color) |> Enum.max() || 0
  end

  defp counts_of(game_string, color) do
    Regex.scan(~r/(\d+) #{color}/, game_string)
    |> Enum.map(fn matches ->
      Enum.at(matches, 1) |> String.to_integer()
    end)
  end
end

Tests - Part 2

ExUnit.start(autorun: false)

defmodule PartTwoTest do
  use ExUnit.Case, async: true
  import PartTwo

  @input """
  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
  """
  @expected 2286

  test "simple example" do
    actual = run(@input)
    assert actual == @expected
  end
end

ExUnit.run()

Solution - Part 2

PartTwo.solve(puzzle_input)