From 42e14095af9ec0c92165238b34903b0756dd7413 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:29:06 -0600 Subject: [PATCH] feat: part 9 solve part 1 --- 09/DiskFragmenter.Tests/DiskMapTests.cs | 62 +++++++++ 09/DiskFragmenter/Program.cs | 161 +++++++++++++++++++++++- 2 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 09/DiskFragmenter.Tests/DiskMapTests.cs diff --git a/09/DiskFragmenter.Tests/DiskMapTests.cs b/09/DiskFragmenter.Tests/DiskMapTests.cs new file mode 100644 index 0000000..5147742 --- /dev/null +++ b/09/DiskFragmenter.Tests/DiskMapTests.cs @@ -0,0 +1,62 @@ +namespace DiskFragmenter.Tests; + +public class DiskMapTests +{ + private const string TestInput = "12345"; + private const string ExampleInput = "2333133121414131402"; + + [Test] + public async Task FromDenseFormat_WhenCalled_ItShouldReturnDiskMap() + { + var map = DiskMap.FromDenseFormat(TestInput); + + await Assert.That(map).IsTypeOf(); + } + + [Test] + public async Task CompactDisk_WhenCalled_ItShouldReturnNewDiskCompacted() + { + var map = DiskMap.FromDenseFormat(TestInput); + var compactedMap = map.CompactDisk(); + + await Assert.That(compactedMap).IsNotEqualTo(map); + } + + [Test] + public async Task CompactDiskWithoutFragmentation_WhenCalledWithTestInput_ItShouldReturnNewDiskCompacted() + { + var map = DiskMap.FromDenseFormat(TestInput); + var compactedMap = map.CompactDiskWithoutFragmentation(); + + await Assert.That(compactedMap).IsNotEqualTo(map); + } + + [Test] + public async Task CompactDiskWithoutFragmentation_WhenCalledWithExampleInput_ItShouldReturnNewDiskCompacted() + { + var map = DiskMap.FromDenseFormat(ExampleInput); + var compactedMap = map.CompactDiskWithoutFragmentation(); + + await Assert.That(compactedMap).IsNotEqualTo(map); + } + + [Test] + public async Task CalculateCheckSum_WhenCalledWhenCalledWithTestInput_ItShouldReturnExpectedValue() + { + var result = DiskMap.FromDenseFormat(TestInput) + .CompactDisk() + .CalculateCheckSum(); + + await Assert.That(result).IsEqualTo(60); + } + + [Test] + public async Task CalculateCheckSum_WhenCalledWithExampleInput_ItShouldReturnExpectedValue() + { + var result = DiskMap.FromDenseFormat(ExampleInput) + .CompactDisk() + .CalculateCheckSum(); + + await Assert.That(result).IsEqualTo(1928); + } +} \ No newline at end of file diff --git a/09/DiskFragmenter/Program.cs b/09/DiskFragmenter/Program.cs index 5f94bf8..00dbbb7 100644 --- a/09/DiskFragmenter/Program.cs +++ b/09/DiskFragmenter/Program.cs @@ -13,12 +13,167 @@ if (File.Exists(args[0]) is false) } var isPart2 = args.Length is 2 && args[1] is "part2"; -var input = await File.ReadAllLinesAsync(args[0]); +var input = await File.ReadAllTextAsync(args[0]); var stopwatch = new Stopwatch(); stopwatch.Start(); -// TODO: Implement solution +var result = DiskMap.FromDenseFormat(input).CompactDisk().CalculateCheckSum(); stopwatch.Stop(); -Console.WriteLine($". ({stopwatch.ElapsedMilliseconds}ms)"); \ No newline at end of file +Console.WriteLine($"The checksum of the compacted disk is {result}. ({stopwatch.ElapsedMilliseconds}ms)"); + +class DiskMap +{ + private readonly IReadOnlyList _expandedFormat; + + private DiskMap(string denseFormat) + { + _expandedFormat = GetExpandedFormat(denseFormat); + } + + private DiskMap(List expandedFormat) + { + _expandedFormat = expandedFormat; + } + + public long CalculateCheckSum() + { + long total = 0; + + foreach (var (fileId, index) in _expandedFormat.Select((block, index) => (block, index))) + { + if (fileId is -1) + { + continue; + } + + total += fileId * index; + } + + return total; + } + + 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) + { + break; + } + + var possiblePlacement = compactedFormat[possiblePlacementIndex]; + var blockToBePlaced = compactedFormat[blockToBeMovedIndex]; + + if (possiblePlacement is not -1) + { + continue; + } + + if (blockToBePlaced is -1) + { + possiblePlacementIndex--; + blockToBeMovedIndex--; + continue; + } + + compactedFormat[possiblePlacementIndex] = blockToBePlaced; + compactedFormat[blockToBeMovedIndex] = -1; + blockToBeMovedIndex--; + } + + return new(compactedFormat); + } + + public DiskMap CompactDiskWithoutFragmentation() + { + var compactedFormat = _expandedFormat.ToList(); + + var fileBlockStartIndex = compactedFormat.FindLastIndex(i => i is not -1); + var fileBlockEndIndex = fileBlockStartIndex; + + var freeBlockStartIndex = compactedFormat.IndexOf(-1); + var freeBlockEndIndex = freeBlockStartIndex; + + while (fileBlockEndIndex > -1) + { + while (compactedFormat[fileBlockEndIndex] == compactedFormat[fileBlockStartIndex]) + { + fileBlockEndIndex--; + + if (fileBlockEndIndex is -1) + { + break; + } + } + + while (freeBlockEndIndex < compactedFormat.Count - 1 && freeBlockEndIndex is not -1) + { + while (compactedFormat[freeBlockStartIndex] == compactedFormat[freeBlockEndIndex]) + { + freeBlockEndIndex++; + } + + var fileBlockLength = fileBlockStartIndex - fileBlockEndIndex; + var freeBlockLength = freeBlockEndIndex - freeBlockStartIndex; + + if (freeBlockLength < fileBlockLength || freeBlockStartIndex > file) + { + freeBlockStartIndex = compactedFormat.IndexOf(-1, freeBlockEndIndex); + freeBlockEndIndex = freeBlockStartIndex; + continue; + } + + for (int i = freeBlockStartIndex; i < freeBlockEndIndex; i++) + { + for (int j = fileBlockStartIndex; j < fileBlockEndIndex; j++) + { + var fileIdToMove = compactedFormat[j]; + compactedFormat[i] = fileIdToMove; + compactedFormat[j] = -1; + } + } + } + + fileBlockStartIndex = compactedFormat.FindLastIndex(fileBlockEndIndex, i => i is not -1); + fileBlockEndIndex = fileBlockStartIndex; + + freeBlockStartIndex = compactedFormat.IndexOf(-1); + freeBlockEndIndex = freeBlockStartIndex; + } + + + return new(compactedFormat); + } + + public static DiskMap FromDenseFormat(string denseFormat) => new(denseFormat); + + private static List GetExpandedFormat(string denseFormat) + { + var expandedFormat = new List(); + var fileId = 0; + + for (var characterIndex = 0; characterIndex < denseFormat.Length; characterIndex += 2) + { + var fileLength = int.Parse(denseFormat[characterIndex].ToString()); + expandedFormat.AddRange(Enumerable.Repeat(fileId, fileLength)); + + var freeSpaceIndex = characterIndex + 1; + + if (freeSpaceIndex < denseFormat.Length) + { + var freeSpaceLength = int.Parse(denseFormat[characterIndex + 1].ToString()); + expandedFormat.AddRange(Enumerable.Repeat(-1, freeSpaceLength)); + } + + fileId++; + } + + return expandedFormat; + } +} \ No newline at end of file