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

Day 5

Day5.livemd

Day 5

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

Input

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "5", System.fetch_env!("LB_AOC_SESSION"))
{:ok,
 "seeds: 3640772818 104094365 1236480411 161072229 376099792 370219099 1590268366 273715765 3224333694 68979978 2070154278 189826014 3855332650 230434913 3033760782 82305885 837883389 177854788 2442602612 571881366\n\nseed-to-soil map:\n496269031 1203272644 52136246\n548405277 496269031 457095898\n1005501175 953364929 249907715\n\nsoil-to-fertilizer map:\n217408321 2086205436 25053699\n2604208456 1670861921 31003781\n1631572552 0 258383552\n129225554 3768288787 36192668\n2421205388 2905533654 126666762\n242462020 3399542287 357404885\n866152503 3032200416 253960559\n2039921781 2262442546 381283607\n2635212237 2714844607 190689047\n3613008578 1753855801 23976114\n3636984692 1503365158 167496763\n1340671861 2111259135 274956\n1889956104 806620565 149965677\n0 3286160975 113381312\n2987089260 956586242 546778916\n2547872150 3756947172 11341615\n3846919647 3807789063 43277850\n3533868176 258383552 79140402\n165418222 1701865702 51990099\n1120113062 586061766 220558799\n2559213765 2217447855 44994691\n3807789063 4255836712 39130584\n3890197497 3851066913 404769799\n1340946817 1777831915 42087923\n2881175496 2111534091 105913764\n113381312 2643726153 15844242\n1383034740 337523954 248537812\n2825901284 2659570395 55274212\n599866905 1819919838 266285598\n\nfertilizer-to-water map:\n3950520280 1751042330 139651634\n936578795 3912173308 42397072\n3553681000 1722281506 28760824\n697953317 651809140 90189394\n3582441824 876081661 368078456\n978975867 2358439651 252255693\n1495879532 2678320518 199775133\n1910380638 3308279888 122339216\n3355092099 1561901004 91630618\n3187667509 2033753243 70292073\n2752202873 3816184128 41568037\n648600286 479585511 49353031\n3149600631 2356473769 1965882\n1695654665 2629130810 49189708\n201901143 385568770 94016741\n536376004 741998534 3182157\n2472303091 3954570380 279899782\n539558161 257732262 15304877\n3131165165 2610695344 18435466\n3446722717 1890693964 106958283\n2793770910 1653531622 40823934\n9849113 65680232 192052030\n2878736712 2104045316 252428453\n1792836692 2878095651 117543946\n2172585320 1244160117 273599019\n2060645804 3884012463 28160845\n3257959582 3719051611 97132517\n408449515 745180691 127926489\n876081661 4234470162 60497134\n1231231560 3043631916 264647972\n3151566513 1997652247 36100996\n2446184339 3430619104 26118752\n803816626 582518586 69290554\n554863038 9849113 40157204\n2146325022 3857752165 26260298\n788142711 50006317 15673915\n295917884 273037139 112531631\n2834594844 1517759136 44141868\n4090171914 3456737856 204795382\n2088806649 3661533238 57518373\n1744844373 2995639597 47992319\n2032719854 1694355556 27925950\n595020242 528938542 53580044\n\nwater-to-light map:\n556810106 840812947 14926117\n2598413684 2184905392 114045192\n2130064037 1600958027 248227533\n1271028210 1253957270 39538107\n3521286912 4262821917 32145379\n1930562940 1944404618 64932992\n4252373354 3302720391 42593942\n1109026743 279496091 162001467\n725777554 2565853410 3969864\n2712458876 1330352326 185715465\n4039742261 3173278185 129442206\n0 2064417497 120487895\n299311037 855739064 257499069\n729747418 1849185560 95219058\n1373001379 767226476 66231296\n571736223 688212171 79014305\n1878676528 2890412515 51886412\n2898174341 682278717 5933454\n1781236499 441497558 97440029\n3090089298 3673052565 50139248\n1365646204 833457772 7355175\n1738475707 2412172480 42760792\n1995495932 2569823274 134568105\n3193548680 3476912261 196140304\n824966476 538937587 143341130\n3553432291 3723191813 486309970\n3140228546 4209501783 53320134\n968307606 1113238133 140719137\n4169184467 3090089298 83188887\n650750528 204469065 75027026\n1439232675 2704391379 186021136\n2511835025 1516067791 84890236\n1625253811 2298950584 113221896\n3389688984 3345314333 131597928\n2378291570 0 22623317\n2596725261 2942298927 1688423\n1310566317 2009337610 55079887\n2940964744 201446459 3022606\n120487895 22623317 178823142\n2904107795 1293495377 36856949\n2400914887 2454933272 110920138\n\nlight-to-temperature map:\n1244459013 624435822 80444775\n2608592263 3309263777 172991510\n3165402867 2278806547 335097905\n292819381 1643978777 105413752\n704475267 462426854 15399493\n3696584161 2678497330 345840247\n2891254573 3613542439 34162874\n1894523870 281665589" <> ...}
defmodule Parser do
  import NimbleParsec

  almanac_seeds =
    wrap(
      ignore(string("seeds:"))
      |> repeat(ignore(string(" ")) |> integer(min: 1))
      |> repeat(ignore(ascii_char([?\n])))
    )

  category =
    ascii_string([?a..?z], min: 1) |> map({String, :to_atom, []})

  ranges =
    wrap(
      integer(min: 1)
      |> ignore(ascii_char([?\s]))
      |> integer(min: 1)
      |> ignore(ascii_char([?\s]))
      |> integer(min: 1)
      |> ignore(optional(ascii_char([?\n])))
    )
    |> map(:to_ranges)

  almanac_map =
    wrap(
      unwrap_and_tag(category, :from)
      |> ignore(string("-to-"))
      |> unwrap_and_tag(category, :to)
      |> ignore(string(" map:\n"))
      |> map(tag(repeat(ranges), :maps), :sort_maps)
      |> ignore(optional(ascii_char([?\n])))
    )
    |> map(:map_by_src)

  defparsec(:almanac, almanac_seeds |> concat(repeat(almanac_map)))

  def to_ranges([dst, src, len]) do
    {src..(src + len - 1), dst..(dst + len - 1)}
  end

  def sort_maps({:maps, maps}) do
    {:maps, Enum.sort_by(maps, &amp;elem(&amp;1, 0))}
  end

  def map_by_src(m) do
    Map.new(m) |> Map.pop!(:from)
  end
end

[:ok, [seeds | mappings] | _] = Parser.almanac(puzzle_input) |> Tuple.to_list()

mappings = Map.new(mappings)
mappings |> Map.take([:seed, :soil])
%{
  seed: %{
    maps: [
      {496269031..953364928, 548405277..1005501174},
      {953364929..1203272643, 1005501175..1255408889},
      {1203272644..1255408889, 496269031..548405276}
    ],
    to: :soil
  },
  soil: %{
    maps: [
      {0..258383551, 1631572552..1889956103},
      {258383552..337523953, 3533868176..3613008577},
      {337523954..586061765, 1383034740..1631572551},
      {586061766..806620564, 1120113062..1340671860},
      {806620565..956586241, 1889956104..2039921780},
      {956586242..1503365157, 2987089260..3533868175},
      {1503365158..1670861920, 3636984692..3804481454},
      {1670861921..1701865701, 2604208456..2635212236},
      {1701865702..1753855800, 165418222..217408320},
      {1753855801..1777831914, 3613008578..3636984691},
      {1777831915..1819919837, 1340946817..1383034739},
      {1819919838..2086205435, 599866905..866152502},
      {2086205436..2111259134, 217408321..242462019},
      {2111259135..2111534090, 1340671861..1340946816},
      {2111534091..2217447854, 2881175496..2987089259},
      {2217447855..2262442545, 2559213765..2604208455},
      {2262442546..2643726152, 2039921781..2421205387},
      {2643726153..2659570394, 113381312..129225553},
      {2659570395..2714844606, 2825901284..2881175495},
      {2714844607..2905533653, 2635212237..2825901283},
      {2905533654..3032200415, 2421205388..2547872149},
      {3032200416..3286160974, 866152503..1120113061},
      {3286160975..3399542286, 0..113381311},
      {3399542287..3756947171, 242462020..599866904},
      {3756947172..3768288786, 2547872150..2559213764},
      {3768288787..3804481454, 129225554..165418221},
      {3807789063..3851066912, 3846919647..3890197496},
      {3851066913..4255836711, 3890197497..4294967295},
      {4255836712..4294967295, 3807789063..3846919646}
    ],
    to: :fertilizer
  }
}
defmodule Mappings do
  def map_to(_, {nil, _}, _), do: :err
  def map_to(_, {category, n}, target) when category == target, do: {target, n}

  def map_to(mappings, {category, n}, target_category) do
    %{maps: maps, to: next_cat} = mappings[category]

    next =
      Enum.find_value(maps, n, fn {src_range, dst_range} ->
        if n in src_range do
          n - src_range.first + dst_range.first
        end
      end)

    map_to(mappings, {next_cat, next}, target_category)
  end
end

seeds
|> Enum.map(&amp;Mappings.map_to(mappings, {:seed, &amp;1}, :location))
|> Enum.min_by(&amp;elem(&amp;1, 1))
{:location, 214922730}

Part 2

defmodule RangeMappings do
  def map_range(range, {src_range, dst_range} = _mapping) do
    if Range.disjoint?(range, src_range) do
      {[], [range]}
    else
      mapped_range =
        max(range.first, src_range.first)..min(range.last, src_range.last)
        |> Range.shift(dst_range.first - src_range.first)

      unmapped_ranges =
        [
          if(range.first < src_range.first, do: range.first..(src_range.first - 1)),
          if(src_range.last < range.last, do: (src_range.last + 1)..range.last)
        ]
        |> Enum.reject(&amp;is_nil/1)

      {[mapped_range], unmapped_ranges}
    end
  end

  def map_ranges(ranges, mappings) do
    mappings
    |> Enum.reduce({[], ranges}, fn mapping, {mapped, unmapped} ->
      Enum.reduce(unmapped, {mapped, []}, fn range, {m, u} ->
        {n, v} = map_range(range, mapping)
        {n ++ m, v ++ u}
      end)
    end)
  end
end

seed_ranges =
  seeds
  |> Enum.chunk_every(2)
  |> Enum.map(fn [start, len] -> start..(start + len - 1) end)

{:location, locations} =
  Stream.iterate({:seed, seed_ranges}, fn {category, ranges} ->
    if Map.has_key?(mappings, category) do
      %{maps: maps, to: next_category} = mappings[category]
      {mapped, unmapped} = RangeMappings.map_ranges(ranges, maps)
      {next_category, mapped ++ unmapped}
    end
  end)
  |> Enum.find(&amp;(elem(&amp;1, 0) == :location))

Enum.min(locations).first
148041808