Add solution for "Day 6: Guard Gallivant", part 2

This commit is contained in:
2025-03-19 09:56:50 +01:00
parent 51b537098d
commit e0cd315909
6 changed files with 178 additions and 37 deletions

View File

@@ -27,33 +27,18 @@ const int GuardGallivant::getPuzzleDay() const
void GuardGallivant::processDataLine(const std::string& line)
{
LinesSolver::processDataLine(line);
auto pos{ line.find(getStartChar()) };
auto pos = line.find(getStartChar());
if (pos != std::string::npos)
{
start_ = Point2{ static_cast<int>(pos), static_cast<int>(lines.size() - 1) };
start_ = Point2{ static_cast<int>(pos), static_cast<int>(lines.size()) };
}
LinesSolver::processDataLine(line);
}
void GuardGallivant::finish()
{
auto dirIndex{ getStartDirectionIndex() };
auto current{ start_ };
visitPosition(current);
auto next{ current + Point2::cardinalDirections[dirIndex] };
while (isInBounds(next))
{
if (getCharAt(next) == getObstructionChar())
{
dirIndex = turnDirection(dirIndex);
}
else
{
current = next;
visitPosition(current);
}
next = current + Point2::cardinalDirections[dirIndex];
}
tracePath();
}
constexpr size_t GuardGallivant::getStartDirectionIndex()
@@ -66,26 +51,148 @@ constexpr char GuardGallivant::getStartChar()
return '^';
}
constexpr char GuardGallivant::getVisitedChar()
{
return 'X';
}
constexpr char GuardGallivant::getObstructionChar()
{
return '#';
}
void GuardGallivant::visitPosition(const Point2& current)
constexpr char GuardGallivant::getEmptyChar()
{
if (getCharAt(current) != getVisitedChar())
return '.';
}
void GuardGallivant::tracePath()
{
VisitGrid visitGrid{ lines.size(), lines[0].size() };
visitGrid.fill(false);
PathGrid pathGrid{ lines.size(), lines[0].size() };
pathGrid.fill(0);
// PathGrid for cycle tests, reused to avoid repeated memory allocation.
PathGrid cyclePathGrid{ lines.size(), lines[0].size() };
size_t directionIndex{ getStartDirectionIndex() };
Point2 current{ start_ };
visitPosition(current, directionIndex, visitGrid);
tracePosition(current, directionIndex, pathGrid);
backtracePath(current, directionIndex, pathGrid);
auto next = current + Point2::cardinalDirections[directionIndex];
while (isInBounds(next))
{
setCharAt(current, getVisitedChar());
if (!checkTurn(current, next, directionIndex, pathGrid))
{
if (!visitGrid.cell(next))
{
if (pathGrid.cell(current)[turnDirectionRight(directionIndex)])
{
part2++;
}
else
{
setCharAt(next, getObstructionChar());
if (tracePathCycleTest(current, turnDirectionRight(directionIndex), pathGrid, cyclePathGrid))
{
part2++;
}
setCharAt(next, getEmptyChar());
}
}
current = next;
visitPosition(current, directionIndex, visitGrid);
tracePosition(current, directionIndex, pathGrid);
}
next = current + Point2::cardinalDirections[directionIndex];
}
}
bool GuardGallivant::tracePathCycleTest(const Point2& startPosition, const size_t startDirectionIndex,
PathGrid& mainPathGrid, PathGrid& cyclePathGrid)
{
cyclePathGrid.fill(0);
size_t directionIndex{ startDirectionIndex };
Point2 current{ startPosition };
tracePosition(current, directionIndex, cyclePathGrid);
backtracePath(current, directionIndex, cyclePathGrid);
auto next = current + Point2::cardinalDirections[directionIndex];
while (isInBounds(next))
{
if (!checkTurn(current, next, directionIndex, cyclePathGrid))
{
if (mainPathGrid.cell(next)[directionIndex] || cyclePathGrid.cell(next)[directionIndex])
{
return true;
}
current = next;
tracePosition(current, directionIndex, cyclePathGrid);
}
next = current + Point2::cardinalDirections[directionIndex];
}
return false;
}
bool GuardGallivant::checkTurn(const Point2& current, const Point2& next, size_t& directionIndex, PathGrid& pathGrid)
{
if (getCharAt(next) == getObstructionChar())
{
directionIndex = turnDirectionRight(directionIndex);
backtracePath(current, directionIndex, pathGrid);
return true;
}
return false;
}
void GuardGallivant::visitPosition(const Point2& current, const size_t directionIndex, VisitGrid& visitGrid)
{
if (!visitGrid.cell(current))
{
visitGrid.cell(current) = true;
part1++;
}
}
size_t GuardGallivant::turnDirection(const size_t current) const
void GuardGallivant::tracePosition(const Point2& current, const size_t directionIndex, PathGrid& pathGrid)
{
return current == 0 ? 3 : current - 1;
pathGrid.cell(current)[directionIndex] = true;
backtraceTurn(current, turnDirectionLeft(directionIndex), pathGrid);
}
void GuardGallivant::backtracePath(const Point2& current, const size_t directionIndex, PathGrid& pathGrid)
{
auto oppositeDirectionIndex = revertDirection(directionIndex);
auto next = current + Point2::cardinalDirections[oppositeDirectionIndex];
while (isObstruction(next) && !pathGrid.cell(next)[directionIndex])
{
tracePosition(next, oppositeDirectionIndex, pathGrid);
next = next + Point2::cardinalDirections[oppositeDirectionIndex];
}
}
void GuardGallivant::backtraceTurn(const Point2& current, const size_t reverseTurnDirectionIndex, PathGrid& pathGrid)
{
auto left = current + Point2::cardinalDirections[reverseTurnDirectionIndex];
if (isObstruction(left))
{
backtracePath(current, reverseTurnDirectionIndex, pathGrid);
}
}
size_t GuardGallivant::turnDirectionRight(const size_t currentDirectionIndex) const
{
return currentDirectionIndex == 0 ? 3 : currentDirectionIndex - 1;
}
size_t GuardGallivant::turnDirectionLeft(const size_t currentDirectionIndex) const
{
return currentDirectionIndex == 3 ? 0 : currentDirectionIndex + 1;
}
size_t GuardGallivant::revertDirection(const size_t currentDirectionIndex) const
{
return currentDirectionIndex > 1 ? currentDirectionIndex - 2 : currentDirectionIndex + 2;
}
bool GuardGallivant::isObstruction(const Point2& position) const
{
return isInBounds(position) && getCharAt(position) == getObstructionChar();
}

View File

@@ -107,3 +107,9 @@ int& Point2::operator[](size_t coordinateIndex)
{
return coordinateIndex == 0 ? x : y;
}
std::ostream& operator<<(std::ostream& os, const Point2& rhs)
{
os << "(" << rhs.x << ", " << rhs.y << ")";
return os;
}