diff --git a/include/aoc/CrossedWires.hpp b/include/aoc/CrossedWires.hpp new file mode 100644 index 0000000..54db4da --- /dev/null +++ b/include/aoc/CrossedWires.hpp @@ -0,0 +1,45 @@ +// 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 + +class CrossedWires + : public Solver +{ +public: + CrossedWires(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: + static constexpr char getInputWireDelimiter(); + static constexpr char getOutputWiresChar(); + bool isProcessingInputWires_; + std::unordered_map wires_; + std::forward_list unevaluatedGates_; + std::bitset<64> z_; + void processInputWire(const std::string& line); + void processLogicGate(const std::string& line); + bool tryEvaluateLogicGate(const LogicGate& gate); + void setOutputWire(const std::string& wire, const bool value); +}; diff --git a/include/aoc/common/LogicGate.hpp b/include/aoc/common/LogicGate.hpp new file mode 100644 index 0000000..ffd6ae1 --- /dev/null +++ b/include/aoc/common/LogicGate.hpp @@ -0,0 +1,33 @@ +// 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 LogicGate +{ +public: + enum class Kind { And, Or, Xor, Unknown }; + + std::string inputWire1; + std::string inputWire2; + std::string outputWire; + Kind kind{ Kind::Unknown }; +}; + +std::istream& operator>>(std::istream& is, LogicGate& logicGate); +std::istream& operator>>(std::istream& is, LogicGate::Kind& kind); diff --git a/src/CrossedWires.cpp b/src/CrossedWires.cpp new file mode 100644 index 0000000..e9ade90 --- /dev/null +++ b/src/CrossedWires.cpp @@ -0,0 +1,160 @@ +// 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 + +CrossedWires::CrossedWires(const int inputFileNameSuffix) + : Solver(inputFileNameSuffix), isProcessingInputWires_{ true }, wires_{}, unevaluatedGates_{}, z_{ 0 } +{ +} + +const std::string CrossedWires::getPuzzleName() const +{ + return "Crossed Wires"; +} + +const int CrossedWires::getPuzzleDay() const +{ + return 24; +} + +void CrossedWires::processDataLine(const std::string& line) +{ + if (isProcessingInputWires_) + { + if (line.empty()) + { + isProcessingInputWires_ = false; + } + else + { + processInputWire(line); + } + } + else + { + processLogicGate(line); + } +} + +void CrossedWires::finish() +{ + while (!unevaluatedGates_.empty()) + { + unevaluatedGates_.remove_if([this](const LogicGate& gate) { return tryEvaluateLogicGate(gate); }); + } + part1 = z_.to_ullong(); +} + +constexpr char CrossedWires::getInputWireDelimiter() +{ + return ':'; +} + +constexpr char CrossedWires::getOutputWiresChar() +{ + return 'z'; +} + +void CrossedWires::processInputWire(const std::string& line) +{ + std::istringstream stream{ line }; + std::string wire; + int value; + std::getline(stream, wire, getInputWireDelimiter()); + stream >> value; + + wires_.insert({ wire, value == 1 }); +} + +void CrossedWires::processLogicGate(const std::string& line) +{ + std::istringstream stream{ line }; + LogicGate gate; + stream >> gate; + + if (!tryEvaluateLogicGate(gate)) + { + unevaluatedGates_.push_front(gate); + } +} + +bool CrossedWires::tryEvaluateLogicGate(const LogicGate& gate) +{ + auto in1 = wires_.find(gate.inputWire1); + if (in1 != wires_.end()) + { + if (in1->second) + { + if (gate.kind == LogicGate::Kind::Or) + { + setOutputWire(gate.outputWire, true); + return true; + } + } + else if (gate.kind == LogicGate::Kind::And) + { + setOutputWire(gate.outputWire, false); + return true; + } + } + + auto in2 = wires_.find(gate.inputWire2); + if (in2 != wires_.end()) + { + if (in2->second) + { + if (gate.kind == LogicGate::Kind::Or) + { + setOutputWire(gate.outputWire, true); + return true; + } + } + else if (gate.kind == LogicGate::Kind::And) + { + setOutputWire(gate.outputWire, false); + return true; + } + if (in1 != wires_.end()) + { + switch (gate.kind) + { + case LogicGate ::Kind ::And : + setOutputWire(gate.outputWire, true); + return true; + case LogicGate ::Kind ::Or : + setOutputWire(gate.outputWire, false); + return true; + case LogicGate ::Kind ::Xor : + setOutputWire(gate.outputWire, in1->second != in2->second); + return true; + } + } + } + + return false; +} + +void CrossedWires::setOutputWire(const std::string& wire, const bool value) +{ + if (wire[0] == getOutputWiresChar()) + { + size_t index = std::stoull(wire.substr(1, 2)); + z_[index] = value; + } + wires_.insert({ wire, value }); +} diff --git a/src/Program.cpp b/src/Program.cpp index 01246cb..a9f9906 100644 --- a/src/Program.cpp +++ b/src/Program.cpp @@ -44,6 +44,7 @@ #include #include #include +#include void Program::run() { @@ -84,6 +85,7 @@ void Program::runSolvers() runSolver(solverEngine); runSolver(solverEngine); runSolver(solverEngine); + runSolver(solverEngine); } std::vector Program::getInputPaths() const diff --git a/src/common/LogicGate.cpp b/src/common/LogicGate.cpp new file mode 100644 index 0000000..046266c --- /dev/null +++ b/src/common/LogicGate.cpp @@ -0,0 +1,45 @@ +// 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 + +std::istream& operator>>(std::istream& is, LogicGate& logicGate) +{ + std::string token; + is >> logicGate.inputWire1 >> logicGate.kind >> logicGate.inputWire2 >> token >> logicGate.outputWire; + return is; +} + +std::istream& operator>>(std::istream& is, LogicGate::Kind& kind) +{ + std::string s; + is >> s; + switch (s[0]) + { + case 'A' : + kind = LogicGate::Kind::And; + break; + case 'O' : + kind = LogicGate::Kind::Or; + break; + case 'X' : + kind = LogicGate::Kind::Xor; + break; + default : + kind = LogicGate::Kind::Unknown; + break; + } + return is; +} diff --git a/tests/include/aocTests/TestContext.hpp b/tests/include/aocTests/TestContext.hpp index 0f5c01c..35f92ac 100644 --- a/tests/include/aocTests/TestContext.hpp +++ b/tests/include/aocTests/TestContext.hpp @@ -38,6 +38,7 @@ class TestContext const std::string& expected2); void runExamplePart1(std::unique_ptr>&& solver, const int64_t expected); void runExamplePart1(std::unique_ptr>&& solver, const std::string& expected); + void runExamplePart1(std::unique_ptr>&& solver, const int64_t expected); void runExamplePart2(std::unique_ptr>&& solver, const int64_t expected); private: template diff --git a/tests/src/TestCases.cpp b/tests/src/TestCases.cpp index 2a2dbc5..5618d8d 100644 --- a/tests/src/TestCases.cpp +++ b/tests/src/TestCases.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #define REQUIRE_MESSAGE(cond, msg) if (!(cond)) { INFO(msg); REQUIRE(cond); } @@ -413,3 +414,20 @@ TEST_CASE("[LanPartyTests]") test.runExample(std::make_unique(), 7, "co,de,ka,ta"); } } + +TEST_CASE("[CrossedWiresTests]") +{ + TestContext test; + SECTION("FullData") + { + test.runFull(std::make_unique(), 48508229772400, ""); + } + SECTION("ExampleData") + { + test.runExamplePart1(std::make_unique(), 4); + } + SECTION("ExampleData2") + { + test.runExamplePart1(std::make_unique(2), 2024); + } +} diff --git a/tests/src/TestContext.cpp b/tests/src/TestContext.cpp index 316ffb2..b24be71 100644 --- a/tests/src/TestContext.cpp +++ b/tests/src/TestContext.cpp @@ -55,6 +55,11 @@ void TestContext::runExamplePart1(std::unique_ptr>& runPart1Generic(std::move(solver), expected, exampleInputPaths_); } +void TestContext::runExamplePart1(std::unique_ptr>&& solver, const int64_t expected) +{ + runPart1Generic(std::move(solver), expected, exampleInputPaths_); +} + void TestContext::runExamplePart2(std::unique_ptr>&& solver, const int64_t expected) { runPart2Generic(std::move(solver), expected, exampleInputPaths_);