From 79067f454bf6fb0dcdd484a7975467953946bf22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Gergely?= Date: Wed, 1 Jan 2025 00:39:13 +0100 Subject: [PATCH] Day 9 Part 1 --- Kodfodrasz.AoC.Year2024.Tests/Day9Tests.fs | 84 +++++++++++ .../Kodfodrasz.AoC.Year2024.Tests.fsproj | 1 + Kodfodrasz.AoC.Year2024/Day9.fs | 133 ++++++++++++++++++ .../Kodfodrasz.AoC.Year2024.fsproj | 1 + 4 files changed, 219 insertions(+) create mode 100644 Kodfodrasz.AoC.Year2024.Tests/Day9Tests.fs create mode 100644 Kodfodrasz.AoC.Year2024/Day9.fs diff --git a/Kodfodrasz.AoC.Year2024.Tests/Day9Tests.fs b/Kodfodrasz.AoC.Year2024.Tests/Day9Tests.fs new file mode 100644 index 0000000..980639e --- /dev/null +++ b/Kodfodrasz.AoC.Year2024.Tests/Day9Tests.fs @@ -0,0 +1,84 @@ +module Kodfodrasz.AoC.Year2024.Tests.Day9Tests + +open Xunit +open Swensen.Unquote.Assertions + +open Kodfodrasz.AoC +open Kodfodrasz.AoC.Year2024 +open Kodfodrasz.AoC.Year2024.Day9 + + +let exampleInput = """ +2333133121414131402 + """ + +[] +let ``Parsing example input`` () = + let expected = [| + File (0, 2, 0) + Free (2, 3) + File (5, 3, 1) + Free (8, 3) + File (11, 1, 2) + Free (12, 3) + File (15, 3, 3) + Free (18, 1) + File (19, 2, 4) + Free (21, 1) + File (22, 4, 5) + Free (26, 1) + File (27, 4, 6) + Free (31, 1) + File (32, 3, 7) + Free (35, 1) + File (36, 4, 8) + Free (40, 0) + File (40, 2, 9) + |] + + test + <@ let actual = parseInput exampleInput + actual = Ok expected @> + + +[] +let ``Parsing example input for 12345`` () = + test + <@ + let expected = Ok [| + File (0, 1, 0) + Free (1, 2) + File (3, 3, 1) + Free (6, 4) + File (10, 5, 2) + |] + let actual = parseInput "12345" + actual = expected + @> + +[] +let ``Answer 1 for example input for 12345`` () = + let blocks = parseInput "12345" |> Result.get + // BLOCKS BEFORE: 0..111....22222 + // BLOCKS AFTER : 022111222 + // POS : 0123456789 + // CHECKSUM : 60 = 0 + 2 + 4 + 3 + 4 + 5 + 12 + 14 + 16 + test <@ answer1 blocks = Ok 60L @> + +[] +let ``Answer 1 for example input`` () = + let input = parseInput exampleInput + + test + <@ let actual = Result.bind answer1 input + let expected: Result<_, string> = Ok 1928L + actual = expected @> + +[] +let ``Answer 2 for example input`` () = + let input = parseInput exampleInput + + test + <@ let actual = Result.bind answer2 input + let expected: Result<_, string> = Ok 31L + actual = expected @> diff --git a/Kodfodrasz.AoC.Year2024.Tests/Kodfodrasz.AoC.Year2024.Tests.fsproj b/Kodfodrasz.AoC.Year2024.Tests/Kodfodrasz.AoC.Year2024.Tests.fsproj index 219b9b4..b0069b0 100644 --- a/Kodfodrasz.AoC.Year2024.Tests/Kodfodrasz.AoC.Year2024.Tests.fsproj +++ b/Kodfodrasz.AoC.Year2024.Tests/Kodfodrasz.AoC.Year2024.Tests.fsproj @@ -14,6 +14,7 @@ + diff --git a/Kodfodrasz.AoC.Year2024/Day9.fs b/Kodfodrasz.AoC.Year2024/Day9.fs new file mode 100644 index 0000000..585daba --- /dev/null +++ b/Kodfodrasz.AoC.Year2024/Day9.fs @@ -0,0 +1,133 @@ +module Kodfodrasz.AoC.Year2024.Day9 + +open System +open System.Text.RegularExpressions +open Kodfodrasz.AoC +open System.Collections.Generic + +type Block = + | File of pos : int * size : int * id : int + | Free of pos : int * size : int + +let pos = function +| File(pos, _, _) -> pos +| Free(pos, _) -> pos + +let size = function +| File(_, size, _) -> size +| Free(_, size) -> size + +let fid = function +| File(_, _, _id) -> _id +| Free(_, _) -> failwith "Not a file" + +type BlockPosComparer() = + interface IComparer with + member _.Compare(x, y) = + compare (pos x) (pos y) + +type parsedInput = Block array +let parseInput (input: string): Result = + let numbers = + input.Split('\n', StringSplitOptions.TrimEntries ||| StringSplitOptions.RemoveEmptyEntries) + |> Seq.collect(String.toCharArray) + |> Seq.map (string >> int) + |> Seq.toArray + + let blocks = + numbers + |> Array.fold (fun (id, pos, l) n -> + match l with + | [] | Free _ :: _ -> + let f = File(pos, n, id) + (id + 1, pos + n, f :: l) + | File _ :: _-> + let f = Free(pos, n) + (id, pos + n, f :: l) + ) (0, 0, List.empty) + |> (fun (_, _, l) -> List.rev l) + + blocks + |> List.toArray + |> Ok + +let checksum blocks = + Seq.sumBy(function + | File(pos, size, id) -> + let mutable sum = 0L + for x in 0 .. size - 1 do + sum <- sum + int64(pos + x) * int64(id) + sum + | Free _ -> 0 + ) blocks + +let defrag1 blocks : Block array = + let blocks = System.Collections.Generic.List(blocks :> Block seq) + + let cmp = BlockPosComparer() + let mutable fragmented = true + while fragmented do + let firstFreeIdx = blocks.FindIndex(function + | Free (pos, size) -> size > 0 + | _ -> false) + let lastFileIdx = blocks.FindLastIndex(function + | File(pos, size, id) -> true + | _ -> false) + + fragmented <- firstFreeIdx >= 0 && lastFileIdx >= 0 + + if fragmented then + let free = blocks[firstFreeIdx] + let file = blocks[lastFileIdx] + + if pos free > pos file then + blocks.RemoveAt(firstFreeIdx) + elif size free = size file then + blocks[firstFreeIdx] <- File( + pos free, + size file, + fid file) + blocks.RemoveAt(lastFileIdx) + elif size free > size file then + blocks[lastFileIdx] <- File( + pos free, + size file, + fid file) + blocks[firstFreeIdx] <- Free( + pos free + size file, + size free - size file) + else // file is bigger + blocks[firstFreeIdx] <- File( + pos free, + size free, + fid file) + blocks[lastFileIdx] <- File( + pos file, + size file - size free, + fid file) + blocks.Sort(cmp) + + blocks.ToArray() + +let answer1 (blocks : parsedInput) = + blocks + |> defrag1 + |> checksum + |> Ok + +let answer2 (data : parsedInput) = + failwith "TODO" + +type Solver() = + inherit SolverBase("Disk Fragmenter") + with + override this.Solve input = + input + |> + this.DoSolve + (parseInput) + [ + answer1; + answer2; + ] + diff --git a/Kodfodrasz.AoC.Year2024/Kodfodrasz.AoC.Year2024.fsproj b/Kodfodrasz.AoC.Year2024/Kodfodrasz.AoC.Year2024.fsproj index 4c3acff..60be4bd 100644 --- a/Kodfodrasz.AoC.Year2024/Kodfodrasz.AoC.Year2024.fsproj +++ b/Kodfodrasz.AoC.Year2024/Kodfodrasz.AoC.Year2024.fsproj @@ -14,6 +14,7 @@ +