diff --git a/include/aoc/KeypadConundrum.hpp b/include/aoc/KeypadConundrum.hpp new file mode 100644 index 0000000..093495b --- /dev/null +++ b/include/aoc/KeypadConundrum.hpp @@ -0,0 +1,34 @@ +// 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 . + +#pragma once + +#include +#include + +class KeypadConundrum + : public Solver +{ +public: + KeypadConundrum(); + 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: + static constexpr char getStartPositionChar(); + KeypadRobot numericKeyboardRobot_; + KeypadRobot directionalKeyboardRobot_; +}; diff --git a/include/aoc/extra/KeypadRobot.hpp b/include/aoc/extra/KeypadRobot.hpp new file mode 100644 index 0000000..f7ad1e0 --- /dev/null +++ b/include/aoc/extra/KeypadRobot.hpp @@ -0,0 +1,32 @@ +// 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 . + +#pragma once + +#include +#include + +#include + +class KeypadRobot +{ +public: + KeypadRobot(const std::map&& keypad, const Point2&& forbidden); + std::string calcInputKeys(const std::string& targetOutputKeys) const; +private: + const std::map keypad_; + const Point2 forbidden_; + void move(std::ostringstream& stream, const int delta, const char positive, const char negative) const; +}; diff --git a/src/KeypadConundrum.cpp b/src/KeypadConundrum.cpp new file mode 100644 index 0000000..a53d583 --- /dev/null +++ b/src/KeypadConundrum.cpp @@ -0,0 +1,62 @@ +// 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 . + +#include + +#include +#include + +#include + +KeypadConundrum::KeypadConundrum() + : numericKeyboardRobot_{ { { 'A', { 0, 0 } }, { '0', { -1, 0 } }, { '1', { -2, -1 } }, { '2', { -1, -1 } }, + { '3', { 0, -1 } }, { '4', { -2, -2 } }, { '5', { -1, -2 } }, { '6', { 0, -2 } }, { '7', { -2, -3 } }, + { '8', { -1, -3 } }, { '9', { 0, -3 } } }, { -2, 0 } }, + directionalKeyboardRobot_{ { { 'A', { 0, 0 } }, { '<', { -2, 1 } }, { '>', { 0, 1 } }, { '^', { -1, 0 } }, + { 'v', { -1, 1 } } }, { -2, 0 } } +{ +} + +const std::string KeypadConundrum::getPuzzleName() const +{ + return "Keypad Conundrum"; +} + +const int KeypadConundrum::getPuzzleDay() const +{ + return 21; +} + +void KeypadConundrum::processDataLine(const std::string& line) +{ + std::istringstream stream{ line }; + int64_t number; + stream >> number; + std::string inputKeys{ numericKeyboardRobot_.calcInputKeys(line) }; + for (size_t i = 0; i < 2; i++) + { + inputKeys = directionalKeyboardRobot_.calcInputKeys(inputKeys); + } + part1 += number * static_cast(inputKeys.size()); +} + +void KeypadConundrum::finish() +{ +} + +constexpr char KeypadConundrum::getStartPositionChar() +{ + return 'A'; +} diff --git a/src/Program.cpp b/src/Program.cpp index 79e8586..b486f6a 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include void Program::run() @@ -79,6 +80,7 @@ void Program::runSolvers() runSolver(solverEngine); runSolver(solverEngine); runSolver(solverEngine); + runSolver(solverEngine); runSolver(solverEngine); } diff --git a/src/extra/KeypadRobot.cpp b/src/extra/KeypadRobot.cpp new file mode 100644 index 0000000..49a5c12 --- /dev/null +++ b/src/extra/KeypadRobot.cpp @@ -0,0 +1,68 @@ +// 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 . + +#include + +#include + +KeypadRobot::KeypadRobot(const std::map&& keypad, const Point2&& forbidden) + : keypad_{ keypad }, forbidden_{ forbidden } +{ +} + +std::string KeypadRobot::calcInputKeys(const std::string& targetOutputKeys) const +{ + std::ostringstream stream{}; + Point2 position{ 0, 0 }; + for (const char c : targetOutputKeys) + { + Point2 next = keypad_.at(c); + + // This specific order of robot arm movements aims to reduce resulting combinations of 'A' and '<' for the + // second robot, which expand to more key presses starting with the third robot. + bool horizontalFirst{ (next.x < position.x && !(next.x == forbidden_.x && position.y == forbidden_.y)) || + (position.x == forbidden_.x && next.y == forbidden_.y) }; + if (horizontalFirst) + { + move(stream, next.x - position.x, '>', '<'); + } + move(stream, next.y - position.y, 'v', '^'); + if (!horizontalFirst) + { + move(stream, next.x - position.x, '>', '<'); + } + stream << 'A'; + position = next; + } + return stream.str(); +} + +void KeypadRobot::move(std::ostringstream& stream, const int delta, const char positive, const char negative) const +{ + if (delta > 0) + { + for (int i{ 0 }; i < delta; i++) + { + stream << positive; + } + } + else + { + for (int i{ 0 }; i < -delta; i++) + { + stream << negative; + } + } +} diff --git a/tests/src/TestCases.cpp b/tests/src/TestCases.cpp index a83b57d..5671c3c 100644 --- a/tests/src/TestCases.cpp +++ b/tests/src/TestCases.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #define REQUIRE_MESSAGE(cond, msg) if (!(cond)) { INFO(msg); REQUIRE(cond); } @@ -369,6 +370,19 @@ TEST_CASE("[RaceConditionTests]") } } +TEST_CASE("[KeypadConundrumTests]") +{ + TestContext test; + SECTION("FullData") + { + test.runFull(std::make_unique(), 136780, 0); + } + SECTION("ExampleData") + { + test.runExample(std::make_unique(), 126384, 0); + } +} + TEST_CASE("[LanPartyTests]") { TestContext test;