Advent of Code 2023 - Day 5

Hi folks! Ukrainian Erlanger is here :metal:! It’s a brand new day, and that means a fresh puzzle challenge awaits us on Advent of Code for for Day 5 . So, get those coding fingers ready and embark on this exciting journey of problem-solving! Happy coding to all!

3 Likes

Here is my Day 5 solution:

defmodule Aoc.Five do
  def parse(string) do
    ["seeds:" <> seeds | rest] = String.split(string, "\n\n", trim: true)

    {parse_line(seeds),
     rest
     |> Enum.map(fn block ->
       String.split(block, "\n", trim: true)
       |> tl()
       |> Enum.map(fn line ->
         line
         |> parse_line()
         |> then(fn [map, from, len] -> {from, from + len - 1, map} end)
       end)
       |> Enum.sort()
     end)}
  end

  def parse_line(line) do
    line
    |> String.split(" ", trim: true)
    |> Enum.map(&String.to_integer/1)
  end

  def part_one({seeds, map}) do
    seeds
    |> Enum.map(&follow_map(&1, map))
    |> Enum.min()
  end

  def follow_map(seed, []), do: seed

  def follow_map(seed, [ranges | rest]) do
    ranges
    |> Enum.find_value(seed, fn {from, to, map} ->
      if seed in from..to do
        seed - from + map
      else
        false
      end
    end)
    |> follow_map(rest)
  end

  def part_two({seeds, map}) do
    seeds
    |> Enum.chunk_every(2)
    |> Enum.map(fn [a, b] -> {a, a + b - 1} end)
    |> follow_map_ranges(map)
    |> Enum.min()
    |> elem(0)
  end

  def follow_map_ranges(seed_ranges, []), do: seed_ranges

  def follow_map_ranges(seed_ranges, [ranges | map]) do
    for seed_range <- seed_ranges,
        new_ranges <- follow_map_ranges(walk_range(seed_range, ranges), map) do
      new_ranges
    end
  end

  def walk_range(range, []), do: [range]

  def walk_range({_, b} = range, [{from, _, _} | _])
      when b < from do
    [range]
  end

  def walk_range({a, _} = range, [{_, to, _} | rest])
      when a > to do
    walk_range(range, rest)
  end

  def walk_range({a, b}, [{from, _, _} | _] = ranges)
      when a < from and b >= from do
    [{a, from - 1} | walk_range({from, b}, ranges)]
  end

  def walk_range({a, b}, [{from, to, new_from} | _])
      when a >= from and b <= to do
    [{new_from + a - from, b - from + new_from}]
  end

  def walk_range({a, b}, [{from, to, new_from} | rest])
      when a >= from and a <= to and b > to do
    [{new_from + a - from, new_from + to - from} | walk_range({to + 1, b}, rest)]
  end
end

input = File.read!("priv/5.txt") |> Aoc.Five.parse()

input |> Aoc.Five.part_one() |> IO.inspect(label: "part 1")
input |> Aoc.Five.part_two() |> IO.inspect(label: "part 2")
1 Like

My day 5 solution: https://github.com/wfvining/aoc2023/blob/main/day5/day5.erl

That was a fun one. Made me spend some time thinking.

1 Like