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