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

Day 1

livebook/2024/day_1.livemd

Day 1

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

Input

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2024", "1", System.fetch_env!("LB_SESSION"))

test input

test_input = """
  3   4
  4   3
  2   5
  1   3
  3   9
  3   3
"""

parser

defmodule RowParser do
  def parse(input) do
   input
    |> String.split("\n", trim: true)
    |> Stream.map(&String.trim_leading/1)
    |> Stream.map(fn str -> 
      [left, right] = String.split(str, ~r/\s{3}/)
      [String.to_integer(left), String.to_integer(right)]
    end)
    |> Enum.to_list()
    |> Enum.reduce(%{left: [], right: []}, fn [left, right], acc ->
      acc
      |> Map.put(:left, [ left | acc[:left]])
      |> Map.put(:right, [ right | acc[:right]])
      end)
  end
end

RowParser.parse(test_input)

test parser

ExUnit.start(autorun: false)

defmodule RowParserTest do
  use ExUnit.Case, async: true

  @test_input """
  3   4
  4   3
  2   5
  1   3
  3   9
  3   3
  """

  test "row parse test" do
    assert RowParser.parse(@test_input) == 
      %{left: [3, 3, 1, 2, 4, 3], right: [3, 9, 3, 5, 3, 4]}
  end
end

ExUnit.run()

part 1

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

  def run(input) do
   %{left: left, right: right} = input
    |> RowParser.parse()

    Enum.sort(left) 
    |> Enum.zip_with(Enum.sort(right), &calc_distance/2)
    |> Enum.sum()
  end

    def calc_distance(l, r) do
     abs(l - r)
  end
end

part 1 - test

ExUnit.start(autorun: false)

defmodule PartOneTest do
  use ExUnit.Case, async: true

  @input puzzle_input

  @expected 1388114
  
  test "part one" do
    assert PartOne.run(@input) == @expected
  end
end

ExUnit.run()

part 2

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

  def run(input) do
   %{left: left, right: right} = input
    |> RowParser.parse()

   f_map = Enum.frequencies(right) 

    left
    |> Enum.map(fn num -> 
      num * Map.get(f_map, num, 0)
    end)
    |> Enum.sum()
  end
end

PartTwo.solve(puzzle_input)

part 2 - test

ExUnit.start(autorun: false)

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

  @input puzzle_input
  @expected 23529853

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

ExUnit.run()