From 0113dc579e1ecdd4aca80759085ae0d89f756ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20M=C3=BCller?= Date: Thu, 10 Jul 2025 19:42:44 +0200 Subject: [PATCH] Add solution for "Day 25: Code Chronicle" --- README.md | 6 ++ include/aoc/CodeChronicle.hpp | 44 +++++++++++++++ src/CodeChronicle.cpp | 101 ++++++++++++++++++++++++++++++++++ src/Program.cpp | 2 + tests/src/TestCases.cpp | 14 +++++ 5 files changed, 167 insertions(+) create mode 100644 include/aoc/CodeChronicle.hpp create mode 100644 src/CodeChronicle.cpp diff --git a/README.md b/README.md index b117586..cd4a77b 100644 --- a/README.md +++ b/README.md @@ -194,6 +194,12 @@ For part 1, many of the output wire values can be determined on the fly, in part The solver then validates all logic gates against parts of the expected structure of a full adder, comprised out of a certain combination of five logic gates per resulting digit and their linking wires, to find the 8 swapped output wires. +### Day 25: Code Chronicle + +:mag_right: Puzzle: , :white_check_mark: Solver: [`CodeChronicle.cpp`](src/CodeChronicle.cpp) + +For the last puzzle, the solver translates each key and lock into an array of pin heights as described in the puzzle, and then checks each of these schematics against the list of already parsed locks or keys, respectively. If the sums of all pairs of corresponding pins from a lock and a key are less than five, they fit. + ## Thanks * [Alexander Brouwer](https://github.com/Bromvlieg) for getting the project set up with CMake. diff --git a/include/aoc/CodeChronicle.hpp b/include/aoc/CodeChronicle.hpp new file mode 100644 index 0000000..0961da9 --- /dev/null +++ b/include/aoc/CodeChronicle.hpp @@ -0,0 +1,44 @@ +// 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 CodeChronicle + : public Solver +{ +public: + 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: + using Schematic = std::array; + static constexpr char getFilledChar(); + static constexpr unsigned getSchematicHeight(); + bool isNewSchematic_{ true }; + bool isLock_{}; + Schematic* current_; + std::vector locks_{}; + std::vector keys_{}; + void resetCurrentSchematic(); + void updateCurrentSchematic(const std::string& line); + void finishCurrentSchematic(); + bool canFit(const Schematic& schematic1, const Schematic& schematic2); +}; diff --git a/src/CodeChronicle.cpp b/src/CodeChronicle.cpp new file mode 100644 index 0000000..21bee23 --- /dev/null +++ b/src/CodeChronicle.cpp @@ -0,0 +1,101 @@ +// 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 + +const std::string CodeChronicle::getPuzzleName() const +{ + return "Code Chronicle"; +} + +const int CodeChronicle::getPuzzleDay() const +{ + return 25; +} + +void CodeChronicle::processDataLine(const std::string& line) +{ + if (line.empty()) + { + finishCurrentSchematic(); + isNewSchematic_ = true; + } + else if (isNewSchematic_) + { + isNewSchematic_ = false; + isLock_ = (line[0] == getFilledChar()); + resetCurrentSchematic(); + } + else + { + updateCurrentSchematic(line); + } +} + +void CodeChronicle::finish() +{ + finishCurrentSchematic(); +} + +constexpr char CodeChronicle::getFilledChar() +{ + return '#'; +} + +constexpr unsigned CodeChronicle::getSchematicHeight() +{ + return 5; +} + +void CodeChronicle::resetCurrentSchematic() +{ + current_ = &(isLock_ ? locks_ : keys_).emplace_back(); + // Sets the current schema to all -1's for keys, to compensate for counting their invariable "#" base line later. + current_->fill(isLock_ ? 0 : -1); +} + +void CodeChronicle::updateCurrentSchematic(const std::string& line) +{ + for (size_t i = 0; i < line.size(); i++) + { + if (line[i] == getFilledChar()) + { + (*current_)[i]++; + } + } +} + +void CodeChronicle::finishCurrentSchematic() +{ + for (auto& schema : isLock_ ? keys_ : locks_) + { + if (canFit(*current_, schema)) + { + part1++; + } + } +} + +bool CodeChronicle::canFit(const Schematic& schematic1, const Schematic& schematic2) +{ + for (size_t i = 0; i < schematic1.size(); i++) + { + if (schematic1[i] + schematic2[i] > getSchematicHeight()) + { + return false; + } + } + return true; +} diff --git a/src/Program.cpp b/src/Program.cpp index a9f9906..d382c95 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -45,6 +45,7 @@ #include #include #include +#include void Program::run() { @@ -86,6 +87,7 @@ void Program::runSolvers() runSolver(solverEngine); runSolver(solverEngine); runSolver(solverEngine); + runSolver(solverEngine); } std::vector Program::getInputPaths() const diff --git a/tests/src/TestCases.cpp b/tests/src/TestCases.cpp index d765386..94347e0 100644 --- a/tests/src/TestCases.cpp +++ b/tests/src/TestCases.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #define REQUIRE_MESSAGE(cond, msg) if (!(cond)) { INFO(msg); REQUIRE(cond); } @@ -431,3 +432,16 @@ TEST_CASE("[CrossedWiresTests]") test.runExamplePart1(std::make_unique(2), 2024); } } + +TEST_CASE("[CodeChronicleTests]") +{ + TestContext test; + SECTION("FullData") + { + test.runFull(std::make_unique(), 2854, 0); + } + SECTION("ExampleData") + { + test.runExamplePart1(std::make_unique(), 3); + } +}