AdventOfCode2024/src/GardenGroups.cpp

120 lines
4.0 KiB
C++

// Solutions to the Advent Of Code 2024.
// Copyright (C) 2025 Stefan Müller
//
// This program is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <http://www.gnu.org/licenses/>.
#include <aoc/GardenGroups.hpp>
GardenGroups::GardenGroups(const int inputFileNameSuffix)
: LinesSolver{ inputFileNameSuffix }
{
}
const std::string GardenGroups::getPuzzleName() const
{
return "Garden Groups";
}
const int GardenGroups::getPuzzleDay() const
{
return 12;
}
void GardenGroups::finish()
{
Grid<bool> isVisited{ lines.size(), lines[0].size() };
isVisited.fill(false);
std::stack<Point2> otherRegionsStack{};
otherRegionsStack.emplace(0, 0);
while (!otherRegionsStack.empty())
{
const Point2 position{ otherRegionsStack.top() };
otherRegionsStack.pop();
if (!isVisited.cell(position))
{
traverseRegion(isVisited, otherRegionsStack, position);
}
}
}
void GardenGroups::traverseRegion(Grid<bool>& isVisited, std::stack<Point2>& otherRegionsStack, const Point2& start)
{
const char plantType{ getCharAt(start) };
long long int area{ 0 };
long long int perimeter{ 0 };
long long int nCorners{ 0 };
std::stack<Point2> regionStack{};
regionStack.push(start);
while (!regionStack.empty())
{
const Point2 position{ regionStack.top() };
regionStack.pop();
if (!isVisited.cell(position))
{
isVisited.cell(position) = true;
area++;
Point2 previousNeighbor{ position + Point2::cardinalDirections.back() };
bool isPreviousNeighborSameRegion{
isInBounds(previousNeighbor) && getCharAt(previousNeighbor) == plantType
};
for (const Point2& direction : Point2::cardinalDirections)
{
const Point2 neighbor{ position + direction };
const bool isNeighborSameRegion{ isInBounds(neighbor) && getCharAt(neighbor) == plantType };
if (isNeighborSameRegion)
{
if (!isVisited.cell(neighbor))
{
regionStack.push(neighbor);
}
// Increases the corner count if two adjacent neighbors are both inside this region, but the
// diagonal neighbor in between them is outside this region.
if (isPreviousNeighborSameRegion)
{
const Point2 diagonalNeighbor{ previousNeighbor + direction };
if (!isInBounds(diagonalNeighbor) || getCharAt(diagonalNeighbor) != plantType)
{
nCorners++;
}
}
}
else
{
if (isInBounds(neighbor) && !isVisited.cell(neighbor))
{
otherRegionsStack.push(neighbor);
}
perimeter++;
// Increases the corner count if two adjacent neighbors are both outside this region.
if (!isPreviousNeighborSameRegion)
{
nCorners++;
}
}
previousNeighbor = neighbor;
isPreviousNeighborSameRegion = isNeighborSameRegion;
}
}
}
part1 += area * perimeter;
part2 += area * nCorners;
}