diff --git a/2024/README.md b/2024/README.md index e43dca3..316419d 100644 --- a/2024/README.md +++ b/2024/README.md @@ -3,7 +3,7 @@ | S | M | T | W | T | F | S | | :-: | :-: | :-: | :-: | :-: | :-: | :-: | | [1] | [2] | [3] | [4] | [5] | [6] | [7] | -| [8] | 9 | 10 | 11 | 12 | 13 | 14 | +| [8] | [9] | 10 | 11 | 12 | 13 | 14 | | 15 | 16 | 17 | 18 | 19 | 20 | 21 | | 22 | 23 | 24 | 25 | | | | @@ -15,3 +15,4 @@ [6]: ./lib/2024/6.ex [7]: ./lib/2024/7.ex [8]: ./lib/2024/8.ex +[9]: ./lib/2024/9.ex diff --git a/2024/lib/2024/9.ex b/2024/lib/2024/9.ex new file mode 100644 index 0000000..963d806 --- /dev/null +++ b/2024/lib/2024/9.ex @@ -0,0 +1,155 @@ +import AOC +import AOC.Prelude + +aoc 2024, 9 do + require Integer + + def p1(input) do + input + |> read_memory_tuple() + |> defrag_memory() + |> checksum() + end + + def p2(input) do + input + |> read_blocks_tuple() + |> defrag_blocks() + |> blocks_to_memory() + |> checksum() + end + + ## part 1 + + defp defrag_memory(memory) do + defrag_memory(memory, 0, tuple_size(memory) - 1) + end + + defp defrag_memory(memory, l, r) when l >= r, do: memory + + defp defrag_memory(memory, l, r) when elem(memory, l) == nil and elem(memory, r) != nil do + memory + |> put_elem(l, elem(memory, r)) + |> put_elem(r, nil) + |> defrag_memory(l + 1, r - 1) + end + + defp defrag_memory(memory, l, r) when elem(memory, l) != nil, do: defrag_memory(memory, l + 1, r) + defp defrag_memory(memory, l, r) when elem(memory, r) == nil, do: defrag_memory(memory, l, r - 1) + + defp checksum(memory) do + for i <- 0..(tuple_size(memory) - 1), reduce: 0 do + sum -> + el = elem(memory, i) + + if is_nil(el) do + sum + else + sum + el * i + end + end + end + + ## part 2 + + defp defrag_blocks(blocks), do: defrag_blocks(blocks, tuple_size(blocks) - 1) + defp defrag_blocks(blocks, i) when i < 0, do: blocks + defp defrag_blocks(blocks, i) when elem(elem(blocks, i), 0) == :free, do: defrag_blocks(blocks, i - 1) + + defp defrag_blocks(blocks, i) do + {:file, _id, file_size} = file = elem(blocks, i) + + free_idx = Enum.find(0..i, &match?({:free, free_size} when free_size >= file_size, elem(blocks, &1))) + + blocks = + if is_nil(free_idx) do + # no space for that block, do nothing + blocks + else + {:free, free_size} = elem(blocks, free_idx) + + blocks = + blocks + |> put_elem(free_idx, file) + |> put_elem(i, {:free, file_size}) + + if file_size == free_size do + blocks + else + new_free_block = {:free, free_size - file_size} + + Tuple.insert_at(blocks, free_idx + 1, new_free_block) + end + end + + blocks + |> merge_blocks() + |> defrag_blocks(i - 1) + end + + defp merge_blocks(blocks) do + for i <- (tuple_size(blocks) - 2)..0//-1, reduce: blocks do + blocks -> + left = elem(blocks, i) + right = elem(blocks, i + 1) + + case {left, right} do + {{:free, left_size}, {:free, right_size}} -> + blocks + |> put_elem(i, {:free, left_size + right_size}) + |> Tuple.delete_at(i + 1) + + {{:file, id, left_size}, {:file, id, right_size}} -> + blocks + |> put_elem(i, {:file, id, left_size + right_size}) + |> Tuple.delete_at(i + 1) + + _ -> + blocks + end + end + end + + defp blocks_to_memory(blocks) do + blocks + |> Tuple.to_list() + |> Enum.flat_map(fn + {:free, n} -> List.duplicate(nil, n) + {:file, id, n} -> List.duplicate(id, n) + end) + |> List.to_tuple() + end + + ## input + + defp read_memory_tuple(input) do + input + |> ints("") + |> Enum.with_index() + |> Enum.flat_map(fn {n, i} -> + if Integer.is_even(i) do + id = div(i, 2) + List.duplicate(id, n) + else + List.duplicate(nil, n) + end + end) + # because memory is a fixed size we can just read it into a tuple for + # constant time random access + |> List.to_tuple() + end + + defp read_blocks_tuple(input) do + input + |> ints("") + |> Enum.with_index() + |> Enum.map(fn {n, i} -> + if Integer.is_even(i) do + {:file, div(i, 2), n} + else + {:free, n} + end + end) + |> List.to_tuple() + end +end diff --git a/README.md b/README.md index 9f10a57..b3b4d49 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ | [2021] | **43/50** 🌟 | Elixir | | [2022] | **14/50** 🌟 | Elixir, Haskell | | [2023] | **19/50** 🌟 | Elixir, Haskell | -| [2024] | **16/50** 🌟 | Elixir | -| **Total** | **172** 🌟| | +| [2024] | **18/50** 🌟 | Elixir | +| **Total** | **174** 🌟| | [2015]: ./2015 [2017]: ./2017