Add solution for "Day 9: Disk Fragmenter", part 2
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user