Add solution for "Day 24: Crossed Wires", part 2

This commit is contained in:
2025-07-08 20:56:39 +02:00
parent d9fdb22bab
commit 4d58746c6d
6 changed files with 218 additions and 7 deletions

View File

@@ -15,10 +15,13 @@
#include <aoc/CrossedWires.hpp>
#include <format>
#include <numeric>
#include <sstream>
CrossedWires::CrossedWires(const int inputFileNameSuffix)
: Solver(inputFileNameSuffix), isProcessingInputWires_{ true }, wires_{}, unevaluatedGates_{}, z_{ 0 }
: Solver(inputFileNameSuffix), isProcessingInputWires_{ true }, wires_{}, gatesByInput_{}, gatesByOutput_{},
unevaluatedGates_{}, z_{ 0 }, swappedOutputWires_{}
{
}
@@ -55,9 +58,16 @@ void CrossedWires::finish()
{
while (!unevaluatedGates_.empty())
{
unevaluatedGates_.remove_if([this](const LogicGate& gate) { return tryEvaluateLogicGate(gate); });
unevaluatedGates_.remove_if([this](auto gate) { return tryEvaluateLogicGate(*gate); });
}
part1 = z_.to_ullong();
validateFullAdderWires();
if (!swappedOutputWires_.empty())
{
part2 = std::accumulate(++swappedOutputWires_.begin(), swappedOutputWires_.end(), *swappedOutputWires_.begin(),
[](const std::string& acc, const std::string& x) { return acc + "," + x; });
}
}
constexpr char CrossedWires::getInputWireDelimiter()
@@ -65,6 +75,11 @@ constexpr char CrossedWires::getInputWireDelimiter()
return ':';
}
constexpr char CrossedWires::getInput1WiresChar()
{
return 'x';
}
constexpr char CrossedWires::getOutputWiresChar()
{
return 'z';
@@ -84,10 +99,15 @@ void CrossedWires::processInputWire(const std::string& line)
void CrossedWires::processLogicGate(const std::string& line)
{
std::istringstream stream{ line };
LogicGate gate;
stream >> gate;
auto gate = std::make_shared<LogicGate>();
stream >> *gate;
if (!tryEvaluateLogicGate(gate))
// Uses different keys for the two maps to guarantee unique keys.
gatesByInput_.insert({ gate->inputWire1 + gate->getKindString(), gate });
gatesByInput_.insert({ gate->inputWire2 + gate->getKindString(), gate });
gatesByOutput_.insert({ gate->outputWire, gate });
if (!tryEvaluateLogicGate(*gate))
{
unevaluatedGates_.push_front(gate);
}
@@ -158,3 +178,143 @@ void CrossedWires::setOutputWire(const std::string& wire, const bool value)
}
wires_.insert({ wire, value });
}
void CrossedWires::validateFullAdderWires()
{
// Except for the first digit "00", one full adder for a single digit is comprised of the same configuration of five
// logic gates, chained together by the "carry" from one digit to the next. For example, these are the logic gates
// for the second digit "01".
// x01 AND y01 -> carryFromDigits
// x01 XOR y01 -> digitsXor
// digitsXor AND carry00 -> carryFromCarry
// digitsXor XOR carry00 -> z01
// carryFromDigits OR carryFromCarry -> carry01
// The following codes validates parts of these in order to find swapped output wires.
uint64_t i{ 0 };
std::string carryWire;
bool success{ true };
while (success)
{
std::string inputDigitWire{ std::format("{0}{1:0>2}", getInput1WiresChar(), i) };
std::string expResultDigitWire{ std::format("{0}{1:0>2}", getOutputWiresChar(), i) };
std::string resultDigitWire, carryFromDigitsWire, digitsXorWire, carryFromCarryWire;
if (i == 0)
{
// Checks "x00 AND y00 -> carry" and "x00 XOR y00 -> z00" gate.
std::tie(success, carryWire, resultDigitWire) = validateAndXorGatePair(inputDigitWire);
if (success && expResultDigitWire != resultDigitWire)
{
swapOutputWires(expResultDigitWire, resultDigitWire);
}
}
else
{
// Checks "x** AND y**" and "x** XOR y**" gate pair.
std::tie(success, carryFromDigitsWire, digitsXorWire) = validateAndXorGatePair(inputDigitWire);
if (!success)
{
break;
}
// Checks the AND and XOR gate pair with the digits XOR gate output and carry as inputs.
std::tie(success, carryFromCarryWire, resultDigitWire) = validateAndXorGatePair(digitsXorWire, carryWire);
if (!success)
{
std::tie(success, carryFromCarryWire, resultDigitWire) =
validateAndXorGatePair(carryFromDigitsWire, carryWire);
if (success)
{
swapOutputWires(carryFromDigitsWire, digitsXorWire);
}
}
if (success && expResultDigitWire != resultDigitWire)
{
swapOutputWires(expResultDigitWire, resultDigitWire);
if (carryFromDigitsWire == resultDigitWire)
{
carryFromDigitsWire = expResultDigitWire;
}
if (carryFromCarryWire == resultDigitWire)
{
carryFromCarryWire = expResultDigitWire;
}
}
// Checks the OR gate with the two carry wires as inputs.
std::tie(success, carryWire) = validateOrGate(carryFromCarryWire, carryFromDigitsWire);
}
++i;
}
}
std::tuple<bool, std::string, std::string> CrossedWires::validateAndXorGatePair(const std::string& inputWire)
{
// Checks "in AND XXX" gate.
auto itAnd = gatesByInput_.find(inputWire + LogicGate::getKindString(LogicGate::Kind::And));
if (itAnd == gatesByInput_.end())
{
// AND gate is missing.
return std::make_tuple(false, "", "");
}
// Checks "in XOR XXX" gate.
auto itXor = gatesByInput_.find(inputWire + LogicGate::getKindString(LogicGate::Kind::Xor));
if (itXor == gatesByInput_.end())
{
// XOR gate is missing.
return std::make_tuple(false, "", "");
}
return std::make_tuple(true, itAnd->second->outputWire, itXor->second->outputWire);
}
std::tuple<bool, std::string, std::string> CrossedWires::validateAndXorGatePair(const std::string& inputWire1,
const std::string& inputWire2)
{
// Checks "in1 AND in2" gate.
auto itAnd = gatesByInput_.find(inputWire1 + LogicGate::getKindString(LogicGate::Kind::And));
if (itAnd == gatesByInput_.end() ||
!(itAnd->second->inputWire1 == inputWire2 || itAnd->second->inputWire2 == inputWire2))
{
// AND gate is missing.
return std::make_tuple(false, "", "");
}
// Checks "in1 XOR in2" gate.
auto itXor = gatesByInput_.find(inputWire1 + LogicGate::getKindString(LogicGate::Kind::Xor));
if (itXor == gatesByInput_.end() ||
!(itXor->second->inputWire1 == inputWire2 || itXor->second->inputWire2 == inputWire2))
{
// XOR gate is missing.
return std::make_tuple(false, "", "");
}
return std::make_tuple(true, itAnd->second->outputWire, itXor->second->outputWire);
}
std::pair<bool, std::string> CrossedWires::validateOrGate(const std::string& inputWire1, const std::string& inputWire2)
{
// Checks "in1 OR in2 -> carry" gate.
auto itOr = gatesByInput_.find(inputWire1 + LogicGate::getKindString(LogicGate::Kind::Or));
if (itOr == gatesByInput_.end() ||
!(itOr->second->inputWire1 == inputWire2 || itOr->second->inputWire2 == inputWire2))
{
// OR gate is missing.
return std::make_pair(false, "");
}
return std::make_pair(true, itOr->second->outputWire);
}
void CrossedWires::swapOutputWires(std::string& outputWire1, std::string& outputWire2)
{
swappedOutputWires_.insert(outputWire1);
swappedOutputWires_.insert(outputWire2);
auto& gate1 = gatesByOutput_[outputWire1];
gate1->outputWire = outputWire2;
auto& gate2 = gatesByOutput_[outputWire2];
gate2->outputWire = outputWire1;
std::swap(gate1, gate2);
std::swap(outputWire1, outputWire2);
}

View File

@@ -15,6 +15,26 @@
#include <aoc/common/LogicGate.hpp>
std::string LogicGate::getKindString(const Kind kind)
{
switch (kind)
{
case Kind::And :
return "AND";
case Kind::Or :
return "OR";
case Kind::Xor :
return "XOR";
case Kind::Unknown :
return "unknown";
}
}
std::string LogicGate::getKindString() const
{
return getKindString(kind);
}
std::istream& operator>>(std::istream& is, LogicGate& logicGate)
{
std::string token;
@@ -26,6 +46,7 @@ std::istream& operator>>(std::istream& is, LogicGate::Kind& kind)
{
std::string s;
is >> s;
// Checks only the first char.
switch (s[0])
{
case 'A' :