Add solution for "Day 15: Warehouse Woes", part 2

This commit is contained in:
2025-03-09 16:21:28 +01:00
parent 8e67c19f98
commit 51b537098d
8 changed files with 338 additions and 37 deletions

View File

@@ -0,0 +1,53 @@
// 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/WarehouseBoxPusher.hpp>
WarehouseBoxPusher::WarehouseBoxPusher(const char boxChar, const char wallChar, const char emptyChar)
: boxChar_{ boxChar }, wallChar_{ wallChar }, emptyChar_{ emptyChar }
{
}
void WarehouseBoxPusher::processMovements(Lines& warehouseMap, const std::string& line)
{
for (const char c : line)
{
processMovement(warehouseMap, Point2::getCardinalDirection(c));
}
}
void WarehouseBoxPusher::processMovement(Lines& warehouseMap, const Point2& direction)
{
const Point2 next{ robotPosition_ + direction };
Point2 push{ next };
while (warehouseMap.getCharAt(push) == boxChar_)
{
push += direction;
}
if (warehouseMap.getCharAt(push) != wallChar_)
{
if (push != next)
{
warehouseMap.setCharAt(push, boxChar_);
warehouseMap.setCharAt(next, emptyChar_);
}
robotPosition_ = next;
}
}
void WarehouseBoxPusher::setRobotPosition(const Point2& robotPosition)
{
robotPosition_ = robotPosition;
}

View File

@@ -0,0 +1,130 @@
// 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/WarehouseWideBoxPusher.hpp>
#include <stack>
WarehouseWideBoxPusher::WarehouseWideBoxPusher(const char leftWideBoxChar, const char rightWideBoxChar,
const char wallChar, const char emptyChar)
: WarehouseBoxPusher{ 0, wallChar, emptyChar }, leftWideBoxChar_{ leftWideBoxChar },
rightWideBoxChar_{ rightWideBoxChar }
{
}
void WarehouseWideBoxPusher::processMovement(Lines& warehouseMap, const Point2& direction)
{
const Point2 next{ robotPosition_ + direction };
if (direction.y == 0)
{
// Moves robot horizontally.
const char facingBoxChar = direction.x > 0 ? leftWideBoxChar_ : rightWideBoxChar_;
const Point2 doubleDirection{ direction.x * 2, 0 };
Point2 push{ next };
while (warehouseMap.getCharAt(push) == facingBoxChar)
{
push += doubleDirection;
}
if (warehouseMap.getCharAt(push) != wallChar_)
{
if (push != next)
{
auto& line = warehouseMap[next.y];
line.erase(line.begin() + push.x);
line.insert(line.begin() + next.x, emptyChar_);
}
robotPosition_ = next;
}
}
else
{
// Moves robot vertically.
// Checks if a wall is blocking the movement directly in front of the robot.
const char charToCheck{ warehouseMap.getCharAt(next) };
if (charToCheck == wallChar_)
{
return;
}
bool canPush{ true };
std::vector<Point2> boxesToPush{};
std::stack<Point2> boxesToCheck{};
// Checks for the first box to push and stacks its left position, if any.
if (charToCheck == leftWideBoxChar_)
{
boxesToCheck.push(next);
}
else if (charToCheck == rightWideBoxChar_)
{
boxesToCheck.emplace(next.x - 1, next.y);
}
// Checks all pushed boxes.
while (!boxesToCheck.empty())
{
Point2 leftPush{ boxesToCheck.top() };
boxesToCheck.pop();
// Checks whether any wall is blocking the push.
Point2 rightPush{ leftPush + Point2::right };
const char leftCharToCheck{ warehouseMap.getCharAt(leftPush) };
const char rightCharToCheck{ warehouseMap.getCharAt(rightPush) };
if (leftCharToCheck == wallChar_ || rightCharToCheck == wallChar_)
{
canPush = false;
break;
}
// Checks if any more boxes are being pushed by this box.
if (leftCharToCheck == leftWideBoxChar_)
{
boxesToPush.push_back(leftPush);
boxesToCheck.emplace(leftPush.x, leftPush.y + direction.y);
}
else
{
if (leftCharToCheck == rightWideBoxChar_)
{
leftPush += Point2::left;
boxesToPush.push_back(leftPush);
boxesToCheck.emplace(leftPush.x, leftPush.y + direction.y);
}
if (rightCharToCheck == leftWideBoxChar_)
{
boxesToPush.push_back(rightPush);
boxesToCheck.emplace(rightPush.x, rightPush.y + direction.y);
}
}
}
if (canPush)
{
// Moves all pushed boxes.
auto it = boxesToPush.crbegin();
while (it != boxesToPush.crend())
{
warehouseMap.setCharAt(*it, emptyChar_);
warehouseMap.setCharAt(*it + Point2::right, emptyChar_);
warehouseMap.setCharAt(*it + direction, leftWideBoxChar_);
warehouseMap.setCharAt(*it + direction + Point2::right, rightWideBoxChar_);
it++;
}
robotPosition_ = next;
}
}
}

View File

@@ -15,8 +15,12 @@
#include <aoc/WarehouseWoes.hpp>
#include <sstream>
WarehouseWoes::WarehouseWoes(const int inputFileNameSuffix)
: LinesSolver{ inputFileNameSuffix }, isProcessingMap_{ true }, robotPosition_{}
: LinesSolver{ inputFileNameSuffix }, isProcessingMap_{ true }, isSearchingStartPosition_{ true }, lines2_{},
warehouseBoxPusher1_{ getBoxChar(), getWallChar(), getEmptyChar() },
warehouseBoxPusher2_{ getLeftWideBoxChar(), getRightWideBoxChar(), getWallChar(), getEmptyChar() }
{
}
@@ -36,16 +40,14 @@ void WarehouseWoes::processDataLine(const std::string& line)
{
if (isProcessingMap_)
{
auto hit = line.find(getRobotChar());
if (hit != std::string::npos)
{
robotPosition_ = { static_cast<int>(hit), static_cast<int>(lines.size()) };
}
checkStartingPosition(line);
LinesSolver::processDataLine(line);
addWarehouseMap2Line(line);
}
else
{
processDirections(line);
warehouseBoxPusher1_.processMovements(lines, line);
warehouseBoxPusher2_.processMovements(lines2_, line);
}
}
else
@@ -56,16 +58,8 @@ void WarehouseWoes::processDataLine(const std::string& line)
void WarehouseWoes::finish()
{
for (size_t j = 0; j < lines.size(); j++)
{
for (size_t i = 0; i < lines[j].size(); i++)
{
if (lines[j][i] == getBoxChar())
{
part1 += calcGpsCoordinate(i, j);
}
}
}
part1 = calcAllGpsCoordinates(lines, getBoxChar());
part2 = calcAllGpsCoordinates(lines2_, getLeftWideBoxChar());
}
constexpr char WarehouseWoes::getRobotChar()
@@ -78,6 +72,16 @@ constexpr char WarehouseWoes::getBoxChar()
return 'O';
}
constexpr char WarehouseWoes::getLeftWideBoxChar()
{
return '[';
}
constexpr char WarehouseWoes::getRightWideBoxChar()
{
return ']';
}
constexpr char WarehouseWoes::getWallChar()
{
return '#';
@@ -88,27 +92,56 @@ constexpr char WarehouseWoes::getEmptyChar()
return '.';
}
void WarehouseWoes::processDirections(const std::string& line)
void WarehouseWoes::checkStartingPosition(const std::string& line)
{
for (const char c : line)
if (isSearchingStartPosition_)
{
const Point2 direction{ Point2::getCardinalDirection(c) };
const Point2 next{ robotPosition_ + direction };
Point2 push{ next };
while (getCharAt(push) == getBoxChar())
auto hit = line.find(getRobotChar());
if (hit != std::string::npos)
{
push += direction;
}
if (getCharAt(push) != getWallChar())
{
setCharAt(push, getBoxChar());
setCharAt(next, getEmptyChar());
robotPosition_ = next;
int x = static_cast<int>(hit);
int y = static_cast<int>(lines.size());
warehouseBoxPusher1_.setRobotPosition({ x, y });
warehouseBoxPusher2_.setRobotPosition({ x * 2, y });
isSearchingStartPosition_ = false;
}
}
}
int WarehouseWoes::calcGpsCoordinate(const size_t x, const size_t y)
void WarehouseWoes::addWarehouseMap2Line(const std::string& line)
{
std::ostringstream stream{};
for (const char c : line)
{
if (c == getBoxChar())
{
stream << getLeftWideBoxChar() << getRightWideBoxChar();
}
else
{
stream << c << c;
}
}
lines2_.push_back(stream.str());
}
long long int WarehouseWoes::calcAllGpsCoordinates(Lines& warehouseMap, const char boxChar)
{
long long int result{ 0 };
for (size_t j = 0; j < warehouseMap.size(); j++)
{
for (size_t i = 0; i < warehouseMap[j].size(); i++)
{
if (warehouseMap[j][i] == boxChar)
{
result += calcGpsCoordinate(i, j);
}
}
}
return result;
}
long long int WarehouseWoes::calcGpsCoordinate(const size_t x, const size_t y)
{
return x + 100 * y;
}