diff --git a/include/aoc/Point2.hpp b/include/aoc/Point2.hpp
index 82cc65d..9bdade1 100644
--- a/include/aoc/Point2.hpp
+++ b/include/aoc/Point2.hpp
@@ -31,6 +31,7 @@ class Point2
     /// The four cardinal directions starting down, rotating in positive direction.
     /// </summary>
     static const std::array<Point2, 4> cardinalDirections;
+    static Point2 getCardinalDirection(const char directionChar);
     int x, y;
     Point2();
     Point2(const int x, const int y);
diff --git a/include/aoc/WarehouseWoes.hpp b/include/aoc/WarehouseWoes.hpp
new file mode 100644
index 0000000..10c36d1
--- /dev/null
+++ b/include/aoc/WarehouseWoes.hpp
@@ -0,0 +1,38 @@
+// 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/>.
+
+#pragma once
+
+#include <aoc/LinesSolver.hpp>
+
+class WarehouseWoes
+    : public LinesSolver
+{
+public:
+    WarehouseWoes(const int inputFileNameSuffix = 0);
+    virtual const std::string getPuzzleName() const override;
+    virtual const int getPuzzleDay() const override;
+    virtual void processDataLine(const std::string& line) override;
+    virtual void finish() override;
+private:
+    bool isProcessingMap_;
+    Point2 robotPosition_;
+    static constexpr char getRobotChar();
+    static constexpr char getBoxChar();
+    static constexpr char getWallChar();
+    static constexpr char getEmptyChar();
+    void processDirections(const std::string& line);
+    int calcGpsCoordinate(const size_t x, const size_t y);
+};
diff --git a/src/Point2.cpp b/src/Point2.cpp
index 004289f..9469644 100644
--- a/src/Point2.cpp
+++ b/src/Point2.cpp
@@ -27,6 +27,24 @@ const std::array<Point2, 8> Point2::directions = { Point2::down, Point2::downRig
     Point2::up, Point2::upLeft, Point2::left, Point2::downLeft };
 const std::array<Point2, 4> Point2::cardinalDirections = { Point2::down, Point2::right, Point2::up, Point2::left };
 
+Point2 Point2::getCardinalDirection(const char directionChar)
+{
+    switch (directionChar)
+    {
+        case 'v' :
+            return Point2::down;
+        case '>' :
+            return Point2::right;
+        case '^' :
+            return Point2::up;
+        case '<' :
+            return Point2::left;
+        default :
+            return { 0, 0 };
+    }
+}
+
+
 Point2::Point2()
     : Point2{ 0, 0 }
 {
diff --git a/src/Program.cpp b/src/Program.cpp
index 5c84d14..f88ac2d 100644
--- a/src/Program.cpp
+++ b/src/Program.cpp
@@ -35,6 +35,7 @@
 #include <aoc/GardenGroups.hpp>
 #include <aoc/ClawContraption.hpp>
 #include <aoc/RestroomRedoubt.hpp>
+#include <aoc/WarehouseWoes.hpp>
 #include <aoc/LanParty.hpp>
 
 void Program::run()
@@ -67,6 +68,7 @@ void Program::runSolvers()
     runSolver<GardenGroups>(solverEngine);
     runSolver<ClawContraption>(solverEngine);
     runSolver<RestroomRedoubt>(solverEngine);
+    runSolver<WarehouseWoes>(solverEngine);
     runSolver<LanParty>(solverEngine);
 }
 
diff --git a/src/WarehouseWoes.cpp b/src/WarehouseWoes.cpp
new file mode 100644
index 0000000..deb4dc7
--- /dev/null
+++ b/src/WarehouseWoes.cpp
@@ -0,0 +1,114 @@
+// 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/WarehouseWoes.hpp>
+
+WarehouseWoes::WarehouseWoes(const int inputFileNameSuffix)
+    : LinesSolver{ inputFileNameSuffix }, isProcessingMap_{ true }, robotPosition_{}
+{
+}
+
+const std::string WarehouseWoes::getPuzzleName() const
+{
+    return "Warehouse Woes";
+}
+
+const int WarehouseWoes::getPuzzleDay() const
+{
+    return 15;
+}
+
+void WarehouseWoes::processDataLine(const std::string& line)
+{
+    if (!line.empty())
+    {
+        if (isProcessingMap_)
+        {
+            auto hit = line.find(getRobotChar());
+            if (hit != std::string::npos)
+            {
+                robotPosition_ = { static_cast<int>(hit), static_cast<int>(lines.size()) };
+            }
+            LinesSolver::processDataLine(line);
+        }
+        else
+        {
+            processDirections(line);
+        }
+    }
+    else
+    {
+        isProcessingMap_ = false;
+    }
+}
+
+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);
+            }
+        }
+    }
+}
+
+constexpr char WarehouseWoes::getRobotChar()
+{
+    return '@';
+}
+
+constexpr char WarehouseWoes::getBoxChar()
+{
+    return 'O';
+}
+
+constexpr char WarehouseWoes::getWallChar()
+{
+    return '#';
+}
+
+constexpr char WarehouseWoes::getEmptyChar()
+{
+    return '.';
+}
+
+void WarehouseWoes::processDirections(const std::string& line)
+{
+    for (const char c : line)
+    {
+        const Point2 direction{ Point2::getCardinalDirection(c) };
+        const Point2 next{ robotPosition_ + direction };
+        Point2 push{ next };
+        while (getCharAt(push) == getBoxChar())
+        {
+            push += direction;
+        }
+        if (getCharAt(push) != getWallChar())
+        {
+            setCharAt(push, getBoxChar());
+            setCharAt(next, getEmptyChar());
+            robotPosition_ = next;
+        }
+    }
+}
+
+int WarehouseWoes::calcGpsCoordinate(const size_t x, const size_t y)
+{
+    return x + 100 * y;
+}
diff --git a/tests/src/TestCases.cpp b/tests/src/TestCases.cpp
index d333bb5..9fd78cc 100644
--- a/tests/src/TestCases.cpp
+++ b/tests/src/TestCases.cpp
@@ -32,6 +32,7 @@
 #include <aoc/GardenGroups.hpp>
 #include <aoc/ClawContraption.hpp>
 #include <aoc/RestroomRedoubt.hpp>
+#include <aoc/WarehouseWoes.hpp>
 #include <aoc/LanParty.hpp>
 
 #define REQUIRE_MESSAGE(cond, msg) if (!(cond)) { INFO(msg); REQUIRE(cond); }
@@ -234,6 +235,23 @@ TEST_CASE("[RestroomRedoubtTests]")
     }
 }
 
+TEST_CASE("[WarehouseWoesTests]")
+{
+    TestContext test;
+    SECTION("FullData")
+    {
+        test.run(std::make_unique<WarehouseWoes>(), 1515788, 0, test.getInputPaths());
+    }
+    SECTION("ExampleData")
+    {
+        test.run(std::make_unique<WarehouseWoes>(), 10092, 0, test.getExampleInputPaths());
+    }
+    SECTION("ExampleData2")
+    {
+        test.run(std::make_unique<WarehouseWoes>(2), 2028, 0, test.getExampleInputPaths());
+    }
+}
+
 TEST_CASE("[LanPartyTests]")
 {
     TestContext test;