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_);