Add solution for "Day 15: Warehouse Woes", part 2
This commit is contained in:
53
src/WarehouseBoxPusher.cpp
Normal file
53
src/WarehouseBoxPusher.cpp
Normal 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;
|
||||
}
|
||||
130
src/WarehouseWideBoxPusher.cpp
Normal file
130
src/WarehouseWideBoxPusher.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user