From b51d76ad3c0ab593899ce7f98c0df007bfee1b43 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Mon, 9 Dec 2024 20:04:30 -0600 Subject: [PATCH] feat: solved day 9 part 2 --- 09/DiskFragmenter.Tests/DiskMapTests.cs | 49 +++++++++++++++++ 09/DiskFragmenter/Program.cs | 73 ++++++++++++++++++++----- README.md | 1 + 3 files changed, 109 insertions(+), 14 deletions(-) diff --git a/09/DiskFragmenter.Tests/DiskMapTests.cs b/09/DiskFragmenter.Tests/DiskMapTests.cs index 5147742..222c09e 100644 --- a/09/DiskFragmenter.Tests/DiskMapTests.cs +++ b/09/DiskFragmenter.Tests/DiskMapTests.cs @@ -4,6 +4,11 @@ public class DiskMapTests { private const string TestInput = "12345"; private const string ExampleInput = "2333133121414131402"; + + private async Task GetPuzzleInput() + { + return await File.ReadAllTextAsync(Path.Combine(AppContext.BaseDirectory, "INPUT.txt")); + } [Test] public async Task FromDenseFormat_WhenCalled_ItShouldReturnDiskMap() @@ -59,4 +64,48 @@ public class DiskMapTests await Assert.That(result).IsEqualTo(1928); } + + [Test] + public async Task CalculateCheckSum_WhenCalledWithPuzzleInput_ItShouldReturnExpectedValue() + { + var input = await GetPuzzleInput(); + + var result = DiskMap.FromDenseFormat(input) + .CompactDisk() + .CalculateCheckSum(); + + await Assert.That(result).IsEqualTo(6200294120911); + } + + [Test] + public async Task CalculateCheckSum_WhenCalledWhenCalledWithTestInputAndNoFragmentation_ItShouldReturnExpectedValue() + { + var result = DiskMap.FromDenseFormat(TestInput) + .CompactDiskWithoutFragmentation() + .CalculateCheckSum(); + + await Assert.That(result).IsEqualTo(132); + } + + [Test] + public async Task CalculateCheckSum_WhenCalledWithExampleInputAndNoFragmentation_ItShouldReturnExpectedValue() + { + var result = DiskMap.FromDenseFormat(ExampleInput) + .CompactDiskWithoutFragmentation() + .CalculateCheckSum(); + + await Assert.That(result).IsEqualTo(2858); + } + + [Test] + public async Task CalculateCheckSum_WhenCalledWithPuzzleInputAndNoFragmentation_ItShouldReturnExpectedValue() + { + var input = await GetPuzzleInput(); + + var result = DiskMap.FromDenseFormat(input) + .CompactDiskWithoutFragmentation() + .CalculateCheckSum(); + + await Assert.That(result).IsEqualTo(6227018762750); + } } \ No newline at end of file diff --git a/09/DiskFragmenter/Program.cs b/09/DiskFragmenter/Program.cs index 00dbbb7..f334b02 100644 --- a/09/DiskFragmenter/Program.cs +++ b/09/DiskFragmenter/Program.cs @@ -18,7 +18,12 @@ var input = await File.ReadAllTextAsync(args[0]); var stopwatch = new Stopwatch(); stopwatch.Start(); -var result = DiskMap.FromDenseFormat(input).CompactDisk().CalculateCheckSum(); +var map = DiskMap.FromDenseFormat(input); +var compactedMap = isPart2 + ? map.CompactDiskWithoutFragmentation() + : map.CompactDisk(); + +var result = compactedMap.CalculateCheckSum(); stopwatch.Stop(); Console.WriteLine($"The checksum of the compacted disk is {result}. ({stopwatch.ElapsedMilliseconds}ms)"); @@ -54,12 +59,19 @@ class DiskMap return total; } + // TODO: Can this be solved without less index manipulation? + // Can I create a data structure that suits both ways + // of doing disk compaction and allows for easily calculating + // the checksum + // maybe something like [block] + // block needs to represent a block of free space + // and a block of file ids + // each will consist indexes, value public DiskMap CompactDisk() { var compactedFormat = _expandedFormat.ToList(); var blockToBeMovedIndex = compactedFormat.Count - 1; - // TODO: I think this might be easier to deal with in a while loop. for (var possiblePlacementIndex = 0; possiblePlacementIndex < compactedFormat.Count; possiblePlacementIndex++) { if (blockToBeMovedIndex < possiblePlacementIndex) @@ -93,15 +105,19 @@ class DiskMap public DiskMap CompactDiskWithoutFragmentation() { var compactedFormat = _expandedFormat.ToList(); - + + // initialize file block pointers var fileBlockStartIndex = compactedFormat.FindLastIndex(i => i is not -1); var fileBlockEndIndex = fileBlockStartIndex; + // initialize free block points var freeBlockStartIndex = compactedFormat.IndexOf(-1); var freeBlockEndIndex = freeBlockStartIndex; - + + // loop over all possible file blocks while (fileBlockEndIndex > -1) { + // loop to end of current file block while (compactedFormat[fileBlockEndIndex] == compactedFormat[fileBlockStartIndex]) { fileBlockEndIndex--; @@ -112,42 +128,71 @@ class DiskMap } } - while (freeBlockEndIndex < compactedFormat.Count - 1 && freeBlockEndIndex is not -1) + // loop over all free blocks that are to the left of the + // current file block + while ( + freeBlockEndIndex < compactedFormat.Count - 1 && + freeBlockEndIndex is not -1 && + freeBlockEndIndex < fileBlockEndIndex + ) { + // find end of current free block while (compactedFormat[freeBlockStartIndex] == compactedFormat[freeBlockEndIndex]) { freeBlockEndIndex++; } + // determine if free block can hold file block var fileBlockLength = fileBlockStartIndex - fileBlockEndIndex; var freeBlockLength = freeBlockEndIndex - freeBlockStartIndex; - if (freeBlockLength < fileBlockLength || freeBlockStartIndex > file) + if (freeBlockLength < fileBlockLength) { + // if current free block cannot hold file block + // advance to next free block freeBlockStartIndex = compactedFormat.IndexOf(-1, freeBlockEndIndex); freeBlockEndIndex = freeBlockStartIndex; continue; } + + // if current free block can hold file block + // loop over the free block positions and place + // the file block's id in them, but only replace + // as many free blocks as there are file blocks + var blockLengthDelta = freeBlockLength - fileBlockLength; - for (int i = freeBlockStartIndex; i < freeBlockEndIndex; i++) + for (var i = freeBlockStartIndex; i < freeBlockEndIndex - blockLengthDelta; i++) { - for (int j = fileBlockStartIndex; j < fileBlockEndIndex; j++) - { - var fileIdToMove = compactedFormat[j]; - compactedFormat[i] = fileIdToMove; - compactedFormat[j] = -1; - } + compactedFormat[i] = compactedFormat[fileBlockStartIndex]; } + + // loop over original file block positions + // and free them up. + for (var j = fileBlockStartIndex; j > fileBlockEndIndex; j--) + { + compactedFormat[j] = -1; + } + + break; } + // if we have reached the end of all + // possible file blocks then no + // need to go to next file block + if (fileBlockEndIndex is -1) + { + break; + } + + // move to next file block fileBlockStartIndex = compactedFormat.FindLastIndex(fileBlockEndIndex, i => i is not -1); fileBlockEndIndex = fileBlockStartIndex; + // reset free block to beginning free block freeBlockStartIndex = compactedFormat.IndexOf(-1); freeBlockEndIndex = freeBlockStartIndex; } - return new(compactedFormat); } diff --git a/README.md b/README.md index c6df107..ecae4a7 100644 --- a/README.md +++ b/README.md @@ -52,3 +52,4 @@ dotnet build | 06 | [Problem](./06/PROBLEM.md) | [Solution](./06/GuardGallivant/) | I kind of brute forced part two... | | 07 | [Problem](./07/PROBLEM.md) | [Solution](./07/BridgeRepair/) | Not perfect or pretty, but good enough | | 08 | [Problem](./08/PROBLEM.md) | [Solution](./08/ResonantCollinearity/) | Without some help from chat I don't know where I'd be...to be fair the description kind of mislead me. | +| 09 | [Problem](./09/PROBLEM.md) | [Solution](./09/DiskFragmenter/) | Doing things with indexes is error prone. |