120 lines
4.0 KiB
C++
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;
|
|
}
|