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

Day 5

day5/sol.livemd

Day 5

Mix.install([
  {:kino, "~> 0.11.3"}
])

Section

input = Kino.Input.textarea("Please enter the input here:")
lines =
  input
  |> Kino.Input.read()
  |> String.split("\n\n", trim: true)
  |> Enum.map(&String.split(&1, "\n", trim: true))

[[seeds] | maps] = lines

# we get the integer list of seeds
# consumes the seeds text
["seeds", seeds] = String.split(seeds, ": ", trim: true)

seeds =
  String.split(seeds, " ", trim: true)
  |> Enum.map(&String.to_integer/1)

# processing the maps to get them as list of int 
maps =
  maps
  # discarding the text about maps since it's static
  |> Enum.map(&tl/1)
  |> Enum.map(fn mp ->
    Enum.map(mp, fn row ->
      String.split(row, " ", trim: true)
      |> Enum.map(&String.to_integer/1)
    end)
  end)

# Now we have seeds and maps as list of integers
# We now create tuple of ranges to filter out the stuff
maps =
  maps
  |> Enum.map(fn mappa ->
    Enum.map(mappa, fn [dst, src, cnt] ->
      {src..(src + cnt - 1), dst..(dst + cnt - 1)}
    end)
  end)

seeds
|> Enum.map(fn seed ->
  Enum.reduce(maps, seed, fn layer, curr ->
    match =
      Enum.filter(layer, fn {fst, _} ->
        curr in fst
      end)

    case match do
      # does not match any range so matches with itself
      [] -> curr
      [{start1.._, start2.._}] -> start2 + curr - start1
    end
  end)
end)
|> Enum.min()
551761867

Part 2

defmodule Day5Part2 do
  def solve(input) do
    lines =
      input
      |> String.split("\n\n", trim: true)
      |> Enum.map(&String.split(&1, "\n", trim: true))

    [[seeds] | maps] = lines

    # we get the integer list of seeds
    # consumes the seeds text
    ["seeds", seeds] = String.split(seeds, ": ", trim: true)

    seeds =
      String.split(seeds, " ", trim: true)
      |> Enum.map(&String.to_integer/1)
      |> Enum.chunk_every(2)
      |> Enum.map(fn [start | [cnt]] -> start..(start + cnt - 1) end)

    # processing the maps to get them as list of int 
    maps =
      maps
      # discarding the text about maps since it's static
      |> Enum.map(&tl/1)
      |> Enum.map(fn mp ->
        Enum.map(mp, fn row ->
          String.split(row, " ", trim: true)
          |> Enum.map(&String.to_integer/1)
        end)
      end)

    # Now we have seeds and maps as list of integers
    # We now create tuple of ranges to filter out the stuff
    maps =
      maps
      |> Enum.map(fn mappa ->
        Enum.map(mappa, fn [dst, src, cnt] ->
          {src..(src + cnt - 1), dst..(dst + cnt - 1)}
        end)
      end)

    final_ranges =
      Enum.reduce(maps, seeds, fn layer, curr_range ->
        intersect_ranges(layer, curr_range)
      end)
      |> Enum.sort()

    [hd | _] = final_ranges
    ans.._ = hd
    ans
  end

  defp intersect_ranges(layer, curr_range) do
    sorted_range = Enum.sort(curr_range)
    sorted_layer = Enum.sort(layer)
    intersect(sorted_layer, sorted_range)
  end

  defp intersect(_, []), do: []
  # check this once again
  defp intersect([], x), do: x

  defp intersect([layer_head | layer_tail], [range_head | range_tail]) do
    {src_rng, dst_rng} = layer_head
    left..right = range_head
    src_left..src_right = src_rng
    dst_left..dst_right = dst_rng
    offset = left - src_left

    cond do
      # our range is completely outside any layer mapping
      right < src_left ->
        [range_head | intersect([layer_head | layer_tail], range_tail)]

      # move to the next range
      src_right < left ->
        intersect(layer_tail, [range_head | range_tail])

      # fully inside
      left >= src_left and right <= src_right ->
        [
          (dst_left + offset)..(dst_left + offset + right - left)
          | intersect([layer_head | layer_tail], range_tail)
        ]

      left < src_left and right <= src_right ->
        [
          left..(src_left - 1)
          | [
              dst_left..(dst_left + right - src_left)
              | intersect([layer_head | layer_tail], range_tail)
            ]
        ]

      left >= src_left and src_right < right ->
        [
          (dst_left + offset)..dst_right
          | intersect(layer_tail, [(src_right + 1)..right | range_tail])
        ]

      left <= src_left and right >= src_right ->
        [
          left..(src_left - 1)
          | [dst_left..dst_right | intersect(layer_tail, [(src_right + 1)..right | range_tail])]
        ]
    end
  end
end
{:module, Day5Part2, <<70, 79, 82, 49, 0, 0, 21, ...>>, {:intersect, 2}}
input
|> Kino.Input.read()
|> Day5Part2.solve()
57451709