diff --git a/2024/day9.exs b/2024/day9.exs index 7393026..729e973 100755 --- a/2024/day9.exs +++ b/2024/day9.exs @@ -27,11 +27,50 @@ defmodule Day9 do |> elem(0) end - # Wrote a crazy long implementation, but was fatally flawed because it didn't consider that - # moving a block needs to leave contiguous spaces behind if there were spaces on either side. - # Maybe can store the spaces as ranges, then merge the ranges? - def part2(_input) do - :ok + # Super inefficient, but this is the 2nd attempt after trying to be more efficient + # worked on the sample but not the actual input + def part2(input) do + {_, _, _, disk, files} = + Enum.reduce(input, {0, 0, true, %{}, []}, fn len, {idx, id, file?, disk, files} -> + if file? do + disk = Map.put(disk, idx, {:file, id, len}) + files = [{idx, id, len} | files] + {idx + len, id + 1, not file?, disk, files} + else + disk = Map.put(disk, idx, {:space, len}) + {idx + len, id, not file?, disk, files} + end + end) + + Enum.reduce(files, disk, fn {idx, id, len}, disk -> + # This scans the disk every time. Could possibly optimise by storing the known + # eligible spaces separately + case Enum.find(0..(idx - 1), &match?({:space, gap} when gap >= len, disk[&1])) do + nil -> + disk + + space_idx -> + {:space, spaces} = Map.fetch!(disk, space_idx) + disk = disk |> Map.delete(idx) |> Map.put(space_idx, {:file, id, len}) + + remainder_spaces = spaces - len + + if remainder_spaces > 0 do + Map.put(disk, space_idx + len, {:space, remainder_spaces}) + else + disk + end + end + end) + |> Enum.reduce(0, fn + {_idx, {:space, _}}, checksum -> + checksum + + {idx, {:file, id, len}}, checksum -> + for idx <- idx..(idx + len - 1), reduce: checksum do + checksum -> checksum + idx * id + end + end) end def input do