Add solution for "Day 24: Crossed Wires", part 2
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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' :
|
||||
|
||||
Reference in New Issue
Block a user