From 5e38e1e8878b5e8b1da872695c6b0420dab3e775 Mon Sep 17 00:00:00 2001 From: bluepython508 Date: Tue, 5 Dec 2023 21:20:45 +0000 Subject: [PATCH] Day 5 --- lib/common.ex | 4 +++ lib/day5.ex | 85 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/mix_tasks.ex | 1 + tests/day5/1 | 33 +++++++++++++++++++ tests/day5/1.1 | 1 + tests/day5/1.2 | 1 + 6 files changed, 125 insertions(+) create mode 100644 lib/day5.ex create mode 100644 tests/day5/1 create mode 100644 tests/day5/1.1 create mode 100644 tests/day5/1.2 diff --git a/lib/common.ex b/lib/common.ex index 899fbbb..14bc967 100644 --- a/lib/common.ex +++ b/lib/common.ex @@ -33,5 +33,9 @@ defmodule Aoc2023.Common do def id(x), do: x def const(_, x), do: x + def lines(x), do: String.split(x, "\n") + + def head([head | _]), do: head + def tail([_ | tail]), do: tail end diff --git a/lib/day5.ex b/lib/day5.ex new file mode 100644 index 0000000..7872bf8 --- /dev/null +++ b/lib/day5.ex @@ -0,0 +1,85 @@ +defmodule Aoc2023.Day5 do + use Aoc2023 + + defp numbers(str), do: str |> String.split() |> Enum.map(&String.to_integer/1) + + def parse(input) do + [seeds | maps] = + input + |> String.split("\n\n") + + { + seeds |> String.split(": ") |> Enum.at(1) |> numbers, + maps + |> Enum.map(pipe( + String.split("\n") + |> tail + |> Enum.map(pipe( + numbers + |> List.to_tuple() + |> then(fn {dst, src, len} -> {src .. (src + len - 1), dst - src} end) + )) + )) + } + end + + defp split_range_on(r1, r2) do + cond do + Range.disjoint?(r1, r2) -> {[r1], []} + r1.first < r2.first && r1.last < r2.last -> + {a, b} = Range.split(r1, r2.first - r1.first) + {[a], [b]} + r1.first > r2.first && r1.last > r2.last -> + {a, b} = Range.split(r1, r1.first - r2.first) + {[b], [a]} + r1.first >= r2.first && r1.last <= r2.last -> + {[], [r1]} + r1.first <= r2.first && r1.last >= r2.last + or r1.first -> + {pre, rest} = Range.split(r1, r2.first - r1.first) + {mid, last} = Range.split(rest, r2.last - rest.first) + {[pre, last], [mid]} + end + end + + defp map(loc, map) do + {unmapped, mapped} = for {src, offset} <- map, + reduce: {[loc], []} do + {unmapped, mapped} -> + for range <- unmapped, + reduce: {[], mapped} do + {unmapped_, mapped_} -> + {unmapped, mapped} = split_range_on(range, src) + { + unmapped_ ++ unmapped, + mapped_ ++ Enum.map(mapped, &(Range.shift(&1, offset))) + } + end + end + unmapped ++ mapped + end + + defp run(seeds, maps) do + for seed <- seeds do + for map <- maps, reduce: [seed] do + loc -> + loc + |> Enum.flat_map(pipe(map(map))) + end + end + |> Enum.flat_map(&id/1) + |> Enum.min_by(&(&1.first)) + |> then(&(&1.first)) + end + + def part1({seeds, maps}) do + run(seeds |> Enum.map(&(&1..&1)), maps) + end + + def part2({seeds, maps}) do + run( + seeds |> Enum.chunk_every(2) |> Enum.map(fn [s, len] -> s..(s + len - 1) end), + maps + ) + end +end diff --git a/lib/mix_tasks.ex b/lib/mix_tasks.ex index 59cb84e..fc1b0ca 100644 --- a/lib/mix_tasks.ex +++ b/lib/mix_tasks.ex @@ -6,6 +6,7 @@ defmodule Mix.Tasks.Aoc do defp module(2), do: Aoc2023.Day2 defp module(3), do: Aoc2023.Day3 defp module(4), do: Aoc2023.Day4 + defp module(5), do: Aoc2023.Day5 # [MODULE INSERTION POINT] defp base_dir(), do: System.get_env("AOC_BASE") diff --git a/tests/day5/1 b/tests/day5/1 new file mode 100644 index 0000000..f756727 --- /dev/null +++ b/tests/day5/1 @@ -0,0 +1,33 @@ +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 diff --git a/tests/day5/1.1 b/tests/day5/1.1 new file mode 100644 index 0000000..8f92bfd --- /dev/null +++ b/tests/day5/1.1 @@ -0,0 +1 @@ +35 diff --git a/tests/day5/1.2 b/tests/day5/1.2 new file mode 100644 index 0000000..9e5feb5 --- /dev/null +++ b/tests/day5/1.2 @@ -0,0 +1 @@ +46