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

Advent of Code - Day 6

elixir-livebook/day6.livemd

Advent of Code - Day 6

Mix.install([
  {:kino_aoc, "~> 0.1"},
  # {:benchee, "~> 1.0", only: :dev}
  {:kino_benchee, github: "akoutmos/kino_benchee", branch: "initial_release_prep"}
])

Introduction

Puzzle

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

Parser

Code - Parser

defmodule Parser do
  def parse(input, :part1) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      [_ | rounds] = line |> String.split([" ", ":"], trim: true)
      rounds |> Enum.map(&String.to_integer/1)
    end)
    |> Enum.zip()
  end

  def parse(input, :part2) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      [_ | rounds] = line |> String.split([" ", ":"], trim: true)
      rounds |> Enum.join("") |> String.to_integer()
    end)
    |> List.to_tuple()
  end
end

Tests - Parser

ExUnit.start(autorun: false)

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

  @input """
  Time:      7  15   30
  Distance:  9  40  200
  """
  @expected_1 [{7, 9}, {15, 40}, {30, 200}]
  @expected_2 {71530, 940_200}

  test "parse test part1" do
    actual = parse(@input, :part1)
    assert actual == @expected_1
  end

  test "parse test part2" do
    actual = parse(@input, :part2)
    assert actual == @expected_2
  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
    |> Parser.parse(:part1)
    |> Enum.map(fn {duration, record} ->
      1..(duration - 1)
      |> Stream.map(fn press_duration ->
        speed = press_duration
        remaining_duration = duration - press_duration

        speed * remaining_duration
      end)
      |> Stream.reject(&amp;(&amp;1 <= record))
      |> Enum.count()
    end)
    |> Enum.product()
  end
end

Tests - Part 1

ExUnit.start(autorun: false)

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

  @input """
  Time:      7  15   30
  Distance:  9  40  200
  """
  @expected 288

  test "part one" 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
    {duration, record} =
      input
      |> Parser.parse(:part2)

    1..(duration - 1)
    |> Stream.map(fn press_duration ->
      speed = press_duration
      remaining_duration = duration - press_duration

      speed * remaining_duration
    end)
    |> Stream.reject(&amp;(&amp;1 <= record))
    |> Enum.count()
  end
end

Tests - Part 2

ExUnit.start(autorun: false)

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

  @input """
  Time:      7  15   30
  Distance:  9  40  200
  """
  @expected 71503

  test "part two" do
    actual = run(@input)
    assert actual == @expected
  end
end

ExUnit.run()

Solution - Part 2

PartTwo.solve(puzzle_input)

Benchmarking

defmodule Benchmarks do
  def part1(input) do
    PartOne.run(input)
  end

  def part2(input) do
    PartTwo.run(input)
  end
end

Benchee.run(
  %{
    "day06.part1" => &amp;Benchmarks.part1/1,
    "day06.part2" => &amp;Benchmarks.part2/1
  },
  inputs: %{
    "puzzle" => puzzle_input
  },
  time: 1,
  memory_time: 1,
  reduction_time: 1
)