Add solution for "Day 17: Chronospatial Computer", part 2

This commit is contained in:
2025-05-21 20:34:34 +02:00
parent 5201dcf0bc
commit 45ab5f93ec
9 changed files with 104 additions and 6 deletions

View File

@@ -73,6 +73,7 @@ void ChronospatialComputer::finish()
{
runProgram(program_, state_);
part1 = state_.output.str();
part2 = findQuineState();
}
void ChronospatialComputer::runProgram(const std::vector<int>& program, ChronospatialComputerState& state) const
@@ -83,3 +84,80 @@ void ChronospatialComputer::runProgram(const std::vector<int>& program, Chronosp
instructions_[program[state.instructionPointer]]->run(state, program[state.instructionPointer + 1]);
}
}
long long ChronospatialComputer::findQuineState()
{
// The following masks and output patterns are specific to a single program and have been precalculated manually.
// The given Chronospatial Computer program essentially does the following calculations.
//
// A' = A >> 3
// OUT = (((A mod 8) xor 4) xor (A shr ((A mod 8) xor 1))) mod 8,
//
// where A is the initial value of register A, A' is the value of register A when program flow reaches the jump
// instruction at the end of the program, and OUT is the single value written to the output when program flow reaches
// that same jump instruction.
//
// So given a desired output OUT, the number to be appended to the accumulated result can be found by matching the
// output patterns below against the masked trailing bits of the accumulated result, see the calculations below.
// Array index corresponds to number to append.
std::array<int, 8> masks{ 0b1, 0b0, 0b111, 0b11, 0b11100, 0b1110, 0b1110000, 0b111000 };
// Array index is the output number.
// First pair element is the number appended to register A.
// Second pair element is the pattern to match against register A.
std::array<std::vector<std::pair<int, int>>, 8> outputPatterns
{ {
// output 0
{ { 0, 0b1 }, { 2, 0b110 }, { 4, 0b00000 }, { 5, 0b0010 }, { 6, 0b0100000 }, { 7, 0b0110000 } },
// output 1
{ { 2, 0b111 }, { 3, 0b11 }, { 4, 0b00100 }, { 5, 0b0000 }, { 6, 0b0110000 }, { 7, 0b010000 } },
// output 2
{ { 2, 0b100 }, { 4, 0b01000 }, { 5, 0b0110 }, { 6, 0b0000000 }, { 7, 0b001000 } },
// output 3
{ { 2, 0b101 }, { 3, 0b10 }, { 4, 0b01100 }, { 5, 0b0100 }, { 6, 0b0010000 }, { 7, 0b000000 } },
// output 4
{ { 0, 0b0 }, { 1, 0b0 }, { 2, 0b010 }, { 4, 0b10000 }, { 5, 0b1010 }, { 6, 0b1100000 }, { 7, 0b111000 } },
// output 5
{ { 2, 0b011 }, { 3, 0b01 }, { 4, 0b10100 }, { 5, 0b1000 }, { 6, 0b1110000 }, { 7, 0b110000 } },
// output 6
{ { 2, 0b000 }, { 4, 0b11000 }, { 5, 0b1110 }, { 6, 0b1000000 }, { 7, 0b101000 } },
// output 7
{ { 2, 0b001 }, { 3, 0b00 }, { 4, 0b11100 }, { 5, 0b1100 }, { 6, 0b1010000 }, { 7, 0b100000 } }
} };
long long result{ 0 };
size_t i{ program_.size() };
int lastAppend{ -1 };
// Appends 3-digit binary numbers to the end of 'result' that will cause the program to output the next desired
// number (from 'program_'). The target output numbers are considered in reverse order.
while (i > 0)
{
auto patterns = outputPatterns[program_[i - 1]];
auto it = std::find_if(patterns.begin(), patterns.end(),
[&lastAppend, &result, &masks](auto& x) {
return lastAppend < x.first && (result & masks[x.first]) == x.second; });
if (it != patterns.end())
{
// Appends the next number 'it->first' that will result in the desired output 'program_[i - 1]'.
lastAppend = -1;
result = (result << 3) + it->first;
i--;
}
else if (i < program_.size())
{
// Backtrack because no 3-digit number can be appended to yield the desired output.
lastAppend = result & 0b111;
result >>= 3;
i++;
}
else
{
// This should not execute, it's a safeguard against an infinite loop in case of unexpected input.
return result;
}
}
return result;
}

View File

@@ -22,10 +22,10 @@ ChronospatialComputerInstruction::ChronospatialComputerInstruction(const Chronos
switch (type)
{
case ChronospatialComputerOperandType::Literal :
operandFunctor_ = [](const std::array<int, 3>& registers, const int operand) { return operand; };
operandFunctor_ = [](const std::array<long long, 3>& registers, const int operand) { return operand; };
break;
case ChronospatialComputerOperandType::Combo :
operandFunctor_ = [](const std::array<int, 3>& registers, const int operand)
operandFunctor_ = [](const std::array<long long, 3>& registers, const int operand)
{ return operand < 4 ? operand : registers[static_cast<size_t>(operand - 4)]; };
break;
}