Add solution for "Day 9: Disk Fragmenter", part 2

This commit is contained in:
2025-02-14 11:24:31 +01:00
parent 648f6d4ebc
commit d8ae103928
5 changed files with 166 additions and 17 deletions

View File

@@ -15,6 +15,8 @@
#include <aoc/DiskFragmenter.hpp>
#include <algorithm>
const std::string DiskFragmenter::getPuzzleName() const
{
return "Day 9: Disk Fragmenter";
@@ -27,36 +29,40 @@ const std::string DiskFragmenter::getInputFileName() const
void DiskFragmenter::processDataLine(const std::string& line)
{
//size_t maxIdNumber{ line.size() / 2 };
moveFileBlocks(line);
moveWholeFiles(line);
}
void DiskFragmenter::moveFileBlocks(const std::string& line)
{
// Index of the first unprocessed digit in the disk map.
size_t front{ 0 };
size_t front{ 1 };
// ID number of the file 'front' refers to (when 'isFile == true'). Equivalent to 'front / 2', but calculated
// incrementally.
size_t frontIdNumber{ 0 };
size_t frontIdNumber{ 1 };
// Index of the last unprocessed digit in the disk map.
size_t back{ line.size() - 1 };
// Number of remaining fragmented blocks in the file that 'back' refers to (when 'isFile == true').
int nRemainingBackBlocks{ getDigit(line, back) };
// ID number of the file 'back' refers to (when 'isFile == true'). Equivalent to 'back / 2', but calculated
// incrementally.
// Number of remaining fragmented blocks in the file that 'back' refers to.
unsigned int nRemainingBackBlocks{ getDigit(line, back) };
// ID number of the file 'back' refers to. Equivalent to 'back / 2', but calculated incrementally.
size_t backIdNumber{ line.size() / 2 };
// Current block position.
size_t position{ 0 };
bool isFile{ true };
size_t position{ getDigit(line, 0) };
// If 'true', 'front' refers to a file in the digit map, otherwise it refers to an empty space.
bool isFile{ false };
while (front < back)
{
if (isFile)
{
// Adds the checksum for the file at 'front'.
int nFileBlocks = getDigit(line, front);
unsigned int nFileBlocks = getDigit(line, front);
part1 += calcChecksumPart(frontIdNumber, nFileBlocks, position);
frontIdNumber++;
}
else
{
int nFreeBlocks = getDigit(line, front);
unsigned int nFreeBlocks = getDigit(line, front);
while (nFreeBlocks > 0)
{
if (nFreeBlocks >= nRemainingBackBlocks)
@@ -87,11 +93,105 @@ void DiskFragmenter::processDataLine(const std::string& line)
part1 += calcChecksumPart(backIdNumber, nRemainingBackBlocks, position);
}
void DiskFragmenter::moveWholeFiles(const std::string& line)
{
// Index of the first unprocessed empty space in the disk map.
size_t front{ 1 };
// Index of the last unprocessed file in the disk map.
size_t back{ line.size() - 1 };
// ID number of the file 'back' refers to. Equivalent to 'back / 2', but calculated incrementally.
size_t backIdNumber{ line.size() / 2 };
// Current block position.
size_t position{ getDigit(line, 0) };
// Contains empty spaces as Interval.
Intervals emptySpaces{};
emptySpaces.reserve(backIdNumber / 2);
// Contains indices of the next empty space with at least as many blocks as their index. 'emptySpaceIndices[0]' is
// not used, but included for convenience of accessing the other values by index.
DigitIndexArray emptySpaceIndices;
emptySpaceIndices.fill(0);
while (back > 0)
{
int nBackBlocks = getDigit(line, back);
if (emptySpaceIndices[nBackBlocks] < emptySpaces.size() &&
emptySpaces[emptySpaceIndices[nBackBlocks]].start < position)
{
if (front >= back)
{
position -= getDigit(line, back) + getDigit(line, back - 1);
}
moveBackFileForward(back, backIdNumber, nBackBlocks, emptySpaces[emptySpaceIndices[nBackBlocks]]);
UpdateEmptySpaceIndices(emptySpaces, emptySpaceIndices);
}
else
{
AddNewEmptySpaces(emptySpaces, line, front, back, nBackBlocks, position);
if (front < back)
{
moveBackFileForward(back, backIdNumber, nBackBlocks, emptySpaces.back());
}
else
{
keepBackFile(line, back, backIdNumber, nBackBlocks, position);
}
UpdateEmptySpaceIndices(emptySpaces, emptySpaceIndices);
}
}
}
void DiskFragmenter::AddNewEmptySpaces(Intervals& emptySpaces, const std::string& line, size_t& front,
const size_t back, const int nBackBlocks, size_t& position)
{
int nFrontBlocks{ 0 };
while (front < back && nFrontBlocks < nBackBlocks)
{
// Adds more empty spaces.
nFrontBlocks = getDigit(line, front);
emptySpaces.emplace_back(position, nFrontBlocks);
front++;
position += static_cast<size_t>(nFrontBlocks) + getDigit(line, front++);
}
}
void DiskFragmenter::UpdateEmptySpaceIndices(const Intervals& emptySpaces, DigitIndexArray& emptySpaceIndices)
{
for (size_t i = 1; i < emptySpaceIndices.size(); i++)
{
while (emptySpaceIndices[i] < emptySpaces.size() && emptySpaces[emptySpaceIndices[i]].length < i)
{
emptySpaceIndices[i]++;
}
}
}
// Moves the 'back' file into the empty space.
void DiskFragmenter::moveBackFileForward(size_t& back, size_t& backIdNumber, const int nBackBlocks,
Interval& emptySpace)
{
part2 += calcChecksumPart(backIdNumber, nBackBlocks, emptySpace.start);
emptySpace.length -= nBackBlocks;
back -= 2;
backIdNumber--;
}
// Does not move the 'back' file, there is no space for it.
void DiskFragmenter::keepBackFile(const std::string& line, size_t& back, size_t& backIdNumber, const int nBackBlocks,
size_t& position)
{
part2 -= calcChecksumPart(backIdNumber, -nBackBlocks, position);
back--;
position -= getDigit(line, back--);
backIdNumber--;
}
void DiskFragmenter::finish()
{
}
int DiskFragmenter::getDigit(const std::string& line, const size_t index) const
unsigned int DiskFragmenter::getDigit(const std::string& line, const size_t index) const
{
return line[index] - '0';
}
@@ -99,5 +199,7 @@ int DiskFragmenter::getDigit(const std::string& line, const size_t index) const
long long int DiskFragmenter::calcChecksumPart(const size_t idNumber, const int nBlocks, size_t& position) const
{
position += nBlocks;
return idNumber * ((nBlocks * (2 * position - nBlocks - 1)) / 2);
// Casting the parameters is required to allow negative block count, resulting in a negative return value.
return static_cast<long long int>(idNumber) *
((nBlocks * (2 * static_cast<long long int>(position) - nBlocks - 1)) / 2);
}