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

Advent of Code 2023 Day 5 Part 2

2023_day5_part2.livemd

Advent of Code 2023 Day 5 Part 2

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

Get Inputs

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

My answer

defmodule Resolver do
  def parse(input) do
    input
    |> String.split("\n\n")
    |> Enum.into(%{}, fn group ->
      group
      |> String.split("\n")
      |> parse_group()
    end)
  end

  defp parse_group(group) when length(group) == 1 do
    seeds =
      group
      |> hd()
      |> String.split(" ")
      |> tl()
      |> Enum.map(&String.to_integer(&1))
      |> Enum.chunk_every(2)
      |> Enum.map(fn [start, len] ->
        %{
          start_index: start,
          end_index: start + len - 1
        }
      end)

    {:seeds, seeds}
  end

  defp parse_group(group) do
    group_name =
      group
      |> hd()
      |> String.slice(0..-6)
      |> String.replace("-", "_")
      |> String.to_atom()

    mappings =
      group
      |> tl()
      |> Enum.map(fn line ->
        [dst, src, len] =
          line
          |> String.split(" ")
          |> Enum.map(&String.to_integer(&1))

        %{
          src: src,
          dst: dst,
          len: len
        }
      end)

    {group_name, mappings}
  end

  def resolve(maps) do
    0
    |> Stream.iterate(&(&1 + 1))
    |> Enum.reduce_while(nil, fn location, _acc ->
      seed =
        location
        |> reverse_search(maps.humidity_to_location)
        |> reverse_search(maps.temperature_to_humidity)
        |> reverse_search(maps.light_to_temperature)
        |> reverse_search(maps.water_to_light)
        |> reverse_search(maps.fertilizer_to_water)
        |> reverse_search(maps.soil_to_fertilizer)
        |> reverse_search(maps.seed_to_soil)

      maps.seeds
      |> Enum.any?(fn %{start_index: start_index, end_index: end_index} ->
        seed >= start_index and seed <= end_index
      end)
      |> if do
        {:halt, location}
      else
        {:cont, nil}
      end
    end)
  end

  defp reverse_search(key, mappings) do
    target_mapping =
      Enum.find(mappings, fn mapping ->
        key >= mapping.dst and key <= mapping.dst + mapping.len - 1
      end)

    if is_nil(target_mapping) do
      key
    else
      target_mapping.src + key - target_mapping.dst
    end
  end
end
maps =
  """
  seeds: 79 14 55 13

  seed-to-soil map:
  50 98 2
  52 50 48

  soil-to-fertilizer map:
  0 15 37
  37 52 2
  39 0 15

  fertilizer-to-water map:
  49 53 8
  0 11 42
  42 0 7
  57 7 4

  water-to-light map:
  88 18 7
  18 25 70

  light-to-temperature map:
  45 77 23
  81 45 19
  68 64 13

  temperature-to-humidity map:
  0 69 1
  1 0 69

  humidity-to-location map:
  60 56 37
  56 93 4
  """
  |> String.slice(0..-2)
  |> Resolver.parse()
Resolver.resolve(maps)
maps = Resolver.parse(puzzle_input)
Resolver.resolve(maps)