From 6d3973bd1ec57eafbc7eb93e2660aed34dd67000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Sat, 17 May 2025 00:05:50 +0200 Subject: [PATCH] Add solution for "Day 17: Chronospatial Computer", part 1 --- include/aoc/ChronospatialComputer.hpp | 40 ++++++ .../ChronospatialComputerInstruction.hpp | 120 ++++++++++++++++ .../ChronospatialComputerOperandType.hpp | 22 +++ .../aoc/extra/ChronospatialComputerState.hpp | 27 ++++ src/ChronospatialComputer.cpp | 85 +++++++++++ src/Program.cpp | 2 + .../ChronospatialComputerInstruction.cpp | 132 ++++++++++++++++++ tests/include/aocTests/TestContext.hpp | 2 + tests/src/TestCases.cpp | 56 ++++++++ tests/src/TestContext.cpp | 10 ++ 10 files changed, 496 insertions(+) create mode 100644 include/aoc/ChronospatialComputer.hpp create mode 100644 include/aoc/extra/ChronospatialComputerInstruction.hpp create mode 100644 include/aoc/extra/ChronospatialComputerOperandType.hpp create mode 100644 include/aoc/extra/ChronospatialComputerState.hpp create mode 100644 src/ChronospatialComputer.cpp create mode 100644 src/extra/ChronospatialComputerInstruction.cpp diff --git a/include/aoc/ChronospatialComputer.hpp b/include/aoc/ChronospatialComputer.hpp new file mode 100644 index 0000000..86b7eb0 --- /dev/null +++ b/include/aoc/ChronospatialComputer.hpp @@ -0,0 +1,40 @@ +// 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 + +#include +#include +#include + +class ChronospatialComputer + : public Solver +{ +public: + ChronospatialComputer(); + 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; + void runProgram(const std::vector& program, ChronospatialComputerState& state) const; +private: + std::array, 8> instructions_; + std::vector program_; + ChronospatialComputerState state_; +}; diff --git a/include/aoc/extra/ChronospatialComputerInstruction.hpp b/include/aoc/extra/ChronospatialComputerInstruction.hpp new file mode 100644 index 0000000..fa695bf --- /dev/null +++ b/include/aoc/extra/ChronospatialComputerInstruction.hpp @@ -0,0 +1,120 @@ +// 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 +#include + +class ChronospatialComputerInstruction +{ +public: + ChronospatialComputerInstruction(const ChronospatialComputerOperandType type); + virtual ~ChronospatialComputerInstruction() = default; + void run(ChronospatialComputerState& state, const int operand) const; +protected: + virtual void runValue(ChronospatialComputerState& state, const int operandValue) const = 0; +private: + std::function&, const int)> operandFunctor_; +}; + +#pragma region ChronospatialComputerDivisionInstruction + +// Represents instruction "adv" (opcode 0), "bdv" (opcode 6), or "cdv" (opcode 7). +class ChronospatialComputerDivisionInstruction + : public ChronospatialComputerInstruction +{ +public: + ChronospatialComputerDivisionInstruction(const size_t destination); +protected: + virtual void runValue(ChronospatialComputerState& state, const int operandValue) const override; +private: + size_t destination_; +}; + +#pragma endregion + +#pragma region ChronospatialComputerXorLiteralInstruction + +// Represents instruction "bxl" (opcode 1). +class ChronospatialComputerXorLiteralInstruction + : public ChronospatialComputerInstruction +{ +public: + ChronospatialComputerXorLiteralInstruction(); +protected: + virtual void runValue(ChronospatialComputerState& state, const int operandValue) const override; +}; + +#pragma endregion + +#pragma region ChronospatialComputerModuloInstruction + +// Represents instruction "bst" (opcode 2). +class ChronospatialComputerModuloInstruction + : public ChronospatialComputerInstruction +{ +public: + ChronospatialComputerModuloInstruction(); +protected: + virtual void runValue(ChronospatialComputerState& state, const int operandValue) const override; +}; + +#pragma endregion + +#pragma region ChronospatialComputerJumpInstruction + +// Represents instruction "jnz" (opcode 3). +class ChronospatialComputerJumpInstruction + : public ChronospatialComputerInstruction +{ +public: + ChronospatialComputerJumpInstruction(); +protected: + virtual void runValue(ChronospatialComputerState& state, const int operandValue) const override; +}; + +#pragma endregion + +#pragma region ChronospatialComputerXorRegisterInstruction + +// Represents instruction "bxc" (opcode 4). +class ChronospatialComputerXorRegisterInstruction + : public ChronospatialComputerInstruction +{ +public: + ChronospatialComputerXorRegisterInstruction(); +protected: + virtual void runValue(ChronospatialComputerState& state, const int operandValue) const override; +}; + +#pragma endregion + +#pragma region ChronospatialComputerOutInstruction + +// Represents instruction "out" (opcode 5). +class ChronospatialComputerOutInstruction + : public ChronospatialComputerInstruction +{ +public: + ChronospatialComputerOutInstruction(); +protected: + virtual void runValue(ChronospatialComputerState& state, const int operandValue) const override; +}; + +#pragma endregion diff --git a/include/aoc/extra/ChronospatialComputerOperandType.hpp b/include/aoc/extra/ChronospatialComputerOperandType.hpp new file mode 100644 index 0000000..5a0d78e --- /dev/null +++ b/include/aoc/extra/ChronospatialComputerOperandType.hpp @@ -0,0 +1,22 @@ +// 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 + +enum class ChronospatialComputerOperandType +{ + Literal, + Combo +}; diff --git a/include/aoc/extra/ChronospatialComputerState.hpp b/include/aoc/extra/ChronospatialComputerState.hpp new file mode 100644 index 0000000..5aef12c --- /dev/null +++ b/include/aoc/extra/ChronospatialComputerState.hpp @@ -0,0 +1,27 @@ +// 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 ChronospatialComputerState +{ +public: + size_t instructionPointer{ 0 }; + std::array registers{}; + std::ostringstream output{}; +}; diff --git a/src/ChronospatialComputer.cpp b/src/ChronospatialComputer.cpp new file mode 100644 index 0000000..73b154f --- /dev/null +++ b/src/ChronospatialComputer.cpp @@ -0,0 +1,85 @@ +// 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 + +ChronospatialComputer::ChronospatialComputer() + : state_{}, program_{}, instructions_{ + std::make_unique(0), // adv + std::make_unique(), // bxl + std::make_unique(), // bst + std::make_unique(), // jnz + std::make_unique(), // bxc + std::make_unique(), // out + std::make_unique(1), // bdv + std::make_unique(2) // cdv + } +{ +} + +const std::string ChronospatialComputer::getPuzzleName() const +{ + return "Chronospatial Computer"; +} + +const int ChronospatialComputer::getPuzzleDay() const +{ + return 17; +} + +void ChronospatialComputer::processDataLine(const std::string& line) +{ + std::istringstream stream{ line }; + std::string token{}; + char c; + int value; + if (stream >> token) + { + if (token == "Register") + { + stream >> c >> token >> value; + // c must be 'A', 'B', or 'C'. + size_t regIndex{ static_cast(c - 65) }; + state_.registers[regIndex] = value; + } + else + { + while (stream >> value) + { + program_.push_back(value); + // Streams a comma from between values. + stream >> c; + } + } + } +} + +void ChronospatialComputer::finish() +{ + runProgram(program_, state_); + part1 = state_.output.str(); +} + +void ChronospatialComputer::runProgram(const std::vector& program, ChronospatialComputerState& state) const +{ + state.instructionPointer = 0; + while (state.instructionPointer < program.size()) + { + instructions_[program[state.instructionPointer]]->run(state, program[state.instructionPointer + 1]); + } +} diff --git a/src/Program.cpp b/src/Program.cpp index 7aa7b44..98d42a2 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include void Program::run() @@ -71,6 +72,7 @@ void Program::runSolvers() runSolver(solverEngine); runSolver(solverEngine); runSolver(solverEngine); + runSolver(solverEngine); runSolver(solverEngine); } diff --git a/src/extra/ChronospatialComputerInstruction.cpp b/src/extra/ChronospatialComputerInstruction.cpp new file mode 100644 index 0000000..6053f9b --- /dev/null +++ b/src/extra/ChronospatialComputerInstruction.cpp @@ -0,0 +1,132 @@ +// 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 + +ChronospatialComputerInstruction::ChronospatialComputerInstruction(const ChronospatialComputerOperandType type) +{ + switch (type) + { + case ChronospatialComputerOperandType::Literal : + operandFunctor_ = [](const std::array& registers, const int operand) { return operand; }; + break; + case ChronospatialComputerOperandType::Combo : + operandFunctor_ = [](const std::array& registers, const int operand) + { return operand < 4 ? operand : registers[static_cast(operand - 4)]; }; + break; + } +} + +void ChronospatialComputerInstruction::run(ChronospatialComputerState& state, const int operand) const +{ + runValue(state, operandFunctor_(state.registers, operand)); +} + +#pragma region ChronospatialComputerDivisionInstruction + +ChronospatialComputerDivisionInstruction::ChronospatialComputerDivisionInstruction(const size_t destination) + : ChronospatialComputerInstruction(ChronospatialComputerOperandType::Combo), destination_{ destination } +{ +} + +void ChronospatialComputerDivisionInstruction::runValue(ChronospatialComputerState& state, const int operandValue) const +{ + state.registers[destination_] = state.registers[0] / Math::ipow(2, operandValue); + state.instructionPointer += 2; +} + +#pragma endregion + +#pragma region ChronospatialComputerXorLiteralInstruction + +ChronospatialComputerXorLiteralInstruction::ChronospatialComputerXorLiteralInstruction() + : ChronospatialComputerInstruction(ChronospatialComputerOperandType::Literal) +{ +} + +void ChronospatialComputerXorLiteralInstruction::runValue(ChronospatialComputerState& state, + const int operandValue) const +{ + state.registers[1] = state.registers[1] ^ operandValue; + state.instructionPointer += 2; +} + +#pragma endregion + +#pragma region ChronospatialComputerModuloInstruction + +ChronospatialComputerModuloInstruction::ChronospatialComputerModuloInstruction() + : ChronospatialComputerInstruction(ChronospatialComputerOperandType::Combo) +{ +} + +void ChronospatialComputerModuloInstruction::runValue(ChronospatialComputerState& state, const int operandValue) const +{ + state.registers[1] = operandValue & 0b111; + state.instructionPointer += 2; +} + +#pragma endregion + +#pragma region ChronospatialComputerJumpInstruction + +ChronospatialComputerJumpInstruction::ChronospatialComputerJumpInstruction() + : ChronospatialComputerInstruction(ChronospatialComputerOperandType::Literal) +{ +} + +void ChronospatialComputerJumpInstruction::runValue(ChronospatialComputerState& state, const int operandValue) const +{ + state.instructionPointer = state.registers[0] == 0 ? state.instructionPointer + 2 : operandValue; +} + +#pragma endregion + +#pragma region ChronospatialComputerXorRegisterInstruction + +ChronospatialComputerXorRegisterInstruction::ChronospatialComputerXorRegisterInstruction() + : ChronospatialComputerInstruction(ChronospatialComputerOperandType::Literal) +{ +} + +void ChronospatialComputerXorRegisterInstruction::runValue(ChronospatialComputerState& state, + const int operandValue) const +{ + state.registers[1] = state.registers[1] ^ state.registers[2]; + state.instructionPointer += 2; +} + +#pragma endregion + +#pragma region ChronospatialComputerOutInstruction + +ChronospatialComputerOutInstruction::ChronospatialComputerOutInstruction() + : ChronospatialComputerInstruction(ChronospatialComputerOperandType::Combo) +{ +} + +void ChronospatialComputerOutInstruction::runValue(ChronospatialComputerState& state, const int operandValue) const +{ + if (state.output.tellp() != std::streampos(0)) + { + state.output << ','; + } + state.output << (operandValue & 0b111); + state.instructionPointer += 2; +} + +#pragma endregion diff --git a/tests/include/aocTests/TestContext.hpp b/tests/include/aocTests/TestContext.hpp index eb1a8ff..4ebf650 100644 --- a/tests/include/aocTests/TestContext.hpp +++ b/tests/include/aocTests/TestContext.hpp @@ -28,6 +28,8 @@ class TestContext const long long expected2, const std::vector& inputPaths); void run(const std::unique_ptr>&& solver, const long long expected1, const std::string expected2, const std::vector& inputPaths); + void run(const std::unique_ptr>&& solver, const std::string expected1, + const long long expected2, const std::vector& inputPaths); void runPart1(const std::unique_ptr>&& solver, const long long expected, const std::vector& inputPaths); void runPart2(const std::unique_ptr>&& solver, const long long expected, diff --git a/tests/src/TestCases.cpp b/tests/src/TestCases.cpp index c1c62d1..6d49696 100644 --- a/tests/src/TestCases.cpp +++ b/tests/src/TestCases.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #define REQUIRE_MESSAGE(cond, msg) if (!(cond)) { INFO(msg); REQUIRE(cond); } @@ -270,6 +271,61 @@ TEST_CASE("[ReindeerMazeTests]") } } +TEST_CASE("[ChronospatialComputerTests]") +{ + TestContext test; + SECTION("FullData") + { + test.run(std::make_unique(), "5,0,3,5,7,6,1,5,4", 0, test.getInputPaths()); + } + SECTION("ExampleData") + { + test.run(std::make_unique(), "4,6,3,5,6,3,5,2,1,0", 0, test.getExampleInputPaths()); + } + SECTION("ExampleInstruction1") + { + auto solver = std::make_unique(); + ChronospatialComputerState state{}; + state.registers[2] = 9; + solver->runProgram({ 2, 6 }, state); + REQUIRE(1 == state.registers[1]); + } + SECTION("ExampleInstruction2") + { + auto solver = std::make_unique(); + ChronospatialComputerState state{}; + state.registers[0] = 10; + solver->runProgram({ 5, 0, 5, 1, 5, 4 }, state); + REQUIRE("0,1,2" == state.output.str()); + } + SECTION("ExampleInstruction3") + { + auto solver = std::make_unique(); + ChronospatialComputerState state{}; + state.registers[0] = 2024; + solver->runProgram({ 0, 1, 5, 4, 3, 0 }, state); + REQUIRE(0 == state.registers[0]); + REQUIRE("4,2,5,6,7,7,7,7,3,1,0" == state.output.str()); + } + SECTION("ExampleInstruction4") + { + auto solver = std::make_unique(); + ChronospatialComputerState state{}; + state.registers[1] = 29; + solver->runProgram({ 1, 7 }, state); + REQUIRE(26 == state.registers[1]); + } + SECTION("ExampleInstruction5") + { + auto solver = std::make_unique(); + ChronospatialComputerState state{}; + state.registers[1] = 2024; + state.registers[2] = 43690; + solver->runProgram({ 4, 0 }, state); + REQUIRE(44354 == state.registers[1]); + } +} + TEST_CASE("[LanPartyTests]") { TestContext test; diff --git a/tests/src/TestContext.cpp b/tests/src/TestContext.cpp index 9bbf0a1..2170e71 100644 --- a/tests/src/TestContext.cpp +++ b/tests/src/TestContext.cpp @@ -39,6 +39,16 @@ void TestContext::run(const std::unique_ptr>&& so REQUIRE(expected2 == solver->getResultPart2()); } +void TestContext::run(const std::unique_ptr>&& solver, const std::string expected1, + const long long expected2, const std::vector& inputPaths) +{ + SolverEngine solverEngine{ inputPaths }; + solverEngine.run(*solver); + + REQUIRE(expected1 == solver->getResultPart1()); + REQUIRE(expected2 == solver->getResultPart2()); +} + void TestContext::runPart1(const std::unique_ptr>&& solver, const long long expected, const std::vector& inputPaths) {