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

Advent of Code 2024: Day 1

days/Day1.livemd

Advent of Code 2024: Day 1

Mix.install([
  {:req, "~> 0.5.8"}
])

Data

require Integer
opts = [headers: [{"cookie", "session=#{System.fetch_env!("AOC_SESSION_COOKIE")}"}]]
input = Req.get!("https://adventofcode.com/2024/day/1/input", opts).body
test_input = "3   4
4   3
2   5
1   3
3   9
3   3
"

Parser

To parse the numbers we’ll simply split on all whitespace, trim and convert to integers. We then have all the numbers in on long list, so we split into two lists on odd/even indices.

defmodule Parser do
  def parse input do
    nums = String.split(input,["\r\n","\n","   "],trim: true) |> Enum.map(&String.to_integer/1) |> Enum.with_index

    {
      Enum.filter(nums, fn {_,idx} -> Integer.is_even(idx) end) |> Enum.map(fn {num, _} -> num end),
      Enum.filter(nums, fn {_,idx} -> Integer.is_odd(idx) end) |> Enum.map(fn {num, _} -> num end)
    }    
  end
end

Parser.parse test_input
Parser.parse input

Solution

Part 1

Simply sort each list, zip them to pair them up, subtract them pairwise, take the absolute value then sum the result.

Part 2

Get the frequencies of each number in the right hand list, then simply reduce over the left list, multiplying the left list entry by the frequency of itself in the right list and summing the result.

defmodule Day1 do
  def part1 input do
    {left,right} = Parser.parse(input)

    Enum.zip(Enum.sort(left),Enum.sort(right))
    |> Enum.reduce(0, fn {a,b}, total -> total + abs(a-b) end)
  end

  def part2 input do 
    {left, right} = Parser.parse(input)

    right = Enum.frequencies(right)
    Enum.reduce(left, 0, fn a, total -> total + a * Map.get(right, a, 0) end)
  end 
end

Day1.part1 test_input

Testing

ExUnit.start(auto_run: false)

defmodule Day1Specs do
  @test_input test_input
  @real_input input
  use ExUnit.Case, async: false

  test "part 1 should return 11 for the test input" do
    assert Day1.part1(@test_input) === 11
  end

  test "part 1 should return 1889772 for the real input" do
    assert Day1.part1(@real_input) === 1889772
  end
  
  test "part 2 should return 31 for the test input" do
    assert Day1.part2(@test_input) === 31
  end

  test "part 2 should return 23228917 for the test input" do
    assert Day1.part2(@real_input) === 23228917
  end
  
end

ExUnit.run()

Results

Part 1 Result

Day1.part1(input)

Part 2 Result

Day1.part2(input)