Add solution for "Day 21: Keypad Conundrum", part 1

This commit is contained in:
Stefan Müller 2025-06-05 23:31:30 +02:00
parent c67bb9054a
commit 60798118ea
6 changed files with 212 additions and 0 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
#pragma once
#include <aoc/extra/KeypadRobot.hpp>
#include <aoc/framework/Solver-types.hpp>
class KeypadConundrum
: public Solver<int64_t, int64_t>
{
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_;
};

View File

@ -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 <http://www.gnu.org/licenses/>.
#pragma once
#include <map>
#include <string>
#include <aoc/common/Point2.hpp>
class KeypadRobot
{
public:
KeypadRobot(const std::map<char, Point2>&& keypad, const Point2&& forbidden);
std::string calcInputKeys(const std::string& targetOutputKeys) const;
private:
const std::map<char, Point2> keypad_;
const Point2 forbidden_;
void move(std::ostringstream& stream, const int delta, const char positive, const char negative) const;
};

62
src/KeypadConundrum.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include <aoc/KeypadConundrum.hpp>
#include <map>
#include <sstream>
#include <aoc/common/Point2.hpp>
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<int64_t>(inputKeys.size());
}
void KeypadConundrum::finish()
{
}
constexpr char KeypadConundrum::getStartPositionChar()
{
return 'A';
}

View File

@ -41,6 +41,7 @@
#include <aoc/RamRun.hpp>
#include <aoc/LinenLayout.hpp>
#include <aoc/RaceCondition.hpp>
#include <aoc/KeypadConundrum.hpp>
#include <aoc/LanParty.hpp>
void Program::run()
@ -79,6 +80,7 @@ void Program::runSolvers()
runSolver<RamRun>(solverEngine);
runSolver<LinenLayout>(solverEngine);
runSolver<RaceCondition>(solverEngine);
runSolver<KeypadConundrum>(solverEngine);
runSolver<LanParty>(solverEngine);
}

68
src/extra/KeypadRobot.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include <aoc/extra/KeypadRobot.hpp>
#include <sstream>
KeypadRobot::KeypadRobot(const std::map<char, Point2>&& 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;
}
}
}

View File

@ -38,6 +38,7 @@
#include <aoc/RamRun.hpp>
#include <aoc/LinenLayout.hpp>
#include <aoc/RaceCondition.hpp>
#include <aoc/KeypadConundrum.hpp>
#include <aoc/LanParty.hpp>
#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<KeypadConundrum>(), 136780, 0);
}
SECTION("ExampleData")
{
test.runExample(std::make_unique<KeypadConundrum>(), 126384, 0);
}
}
TEST_CASE("[LanPartyTests]")
{
TestContext test;