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

This commit is contained in:
Stefan Müller 2025-06-26 20:41:18 +02:00
parent 958adde4a2
commit 4788a1b5ab
8 changed files with 309 additions and 0 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
#pragma once
#include <bitset>
#include <forward_list>
#include <unordered_map>
#include <aoc/common/LogicGate.hpp>
#include <aoc/framework/Solver-types.hpp>
class CrossedWires
: public Solver<int64_t, std::string>
{
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<std::string, bool> wires_;
std::forward_list<LogicGate> 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);
};

View File

@ -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 <http://www.gnu.org/licenses/>.
#pragma once
#include <iostream>
#include <string>
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);

160
src/CrossedWires.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include <aoc/CrossedWires.hpp>
#include <sstream>
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 });
}

View File

@ -44,6 +44,7 @@
#include <aoc/KeypadConundrum.hpp>
#include <aoc/MonkeyMarket.hpp>
#include <aoc/LanParty.hpp>
#include <aoc/CrossedWires.hpp>
void Program::run()
{
@ -84,6 +85,7 @@ void Program::runSolvers()
runSolver<KeypadConundrum>(solverEngine);
runSolver<MonkeyMarket>(solverEngine);
runSolver<LanParty>(solverEngine);
runSolver<CrossedWires>(solverEngine);
}
std::vector<std::string> Program::getInputPaths() const

45
src/common/LogicGate.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#include <aoc/common/LogicGate.hpp>
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;
}

View File

@ -38,6 +38,7 @@ class TestContext
const std::string& expected2);
void runExamplePart1(std::unique_ptr<Solver<int64_t, int64_t>>&& solver, const int64_t expected);
void runExamplePart1(std::unique_ptr<Solver<std::string, int64_t>>&& solver, const std::string& expected);
void runExamplePart1(std::unique_ptr<Solver<int64_t, std::string>>&& solver, const int64_t expected);
void runExamplePart2(std::unique_ptr<Solver<int64_t, int64_t>>&& solver, const int64_t expected);
private:
template <typename T1, typename T2>

View File

@ -41,6 +41,7 @@
#include <aoc/KeypadConundrum.hpp>
#include <aoc/MonkeyMarket.hpp>
#include <aoc/LanParty.hpp>
#include <aoc/CrossedWires.hpp>
#define REQUIRE_MESSAGE(cond, msg) if (!(cond)) { INFO(msg); REQUIRE(cond); }
@ -413,3 +414,20 @@ TEST_CASE("[LanPartyTests]")
test.runExample(std::make_unique<LanParty>(), 7, "co,de,ka,ta");
}
}
TEST_CASE("[CrossedWiresTests]")
{
TestContext test;
SECTION("FullData")
{
test.runFull(std::make_unique<CrossedWires>(), 48508229772400, "");
}
SECTION("ExampleData")
{
test.runExamplePart1(std::make_unique<CrossedWires>(), 4);
}
SECTION("ExampleData2")
{
test.runExamplePart1(std::make_unique<CrossedWires>(2), 2024);
}
}

View File

@ -55,6 +55,11 @@ void TestContext::runExamplePart1(std::unique_ptr<Solver<std::string, int64_t>>&
runPart1Generic(std::move(solver), expected, exampleInputPaths_);
}
void TestContext::runExamplePart1(std::unique_ptr<Solver<int64_t, std::string>>&& solver, const int64_t expected)
{
runPart1Generic(std::move(solver), expected, exampleInputPaths_);
}
void TestContext::runExamplePart2(std::unique_ptr<Solver<int64_t, int64_t>>&& solver, const int64_t expected)
{
runPart2Generic(std::move(solver), expected, exampleInputPaths_);