Learning Python with Advent of Code Walkthroughs

Dazbo's Advent of Code solutions, written in Python

Clock Signal

Advent of Code 2016 - Day 25

Day 25: Clock Signal

Useful Links

Concepts and Packages Demonstrated

AssembunnyStopIteration

Problem Intro

It’s the final Assembunny challenge! This time, we need to find the right input for a program to generate a specific clock signal. The program uses the same Assembunny instruction set we’ve seen before, but with one addition: out x, which transmits the value of x as the next value for the clock signal.

Our goal is to find the lowest positive integer that, when used to initialize register a, causes the program to output an endless, alternating pattern of 0, 1, 0, 1, ....

Part 1

What is the lowest positive integer that can be used to initialize register a?

My approach is to once again extend the Computer class from Day 12. I’ll create a new class, Assembunny3, that adds the _op_out method.

The _op_out method will append the output value to a string that represents the clock signal. After each output, it will check if the signal is valid. If it finds a repeating digit (e.g., 0, 0 or 1, 1), it will raise a StopIteration exception to terminate the program. If the signal gets long enough without any issues, we can be reasonably confident that we’ve found the correct input.

class Assembunny3(Computer):
    def __init__(self) -> None:
        self._clock_signal = ""
        super().__init__()
    
    @property
    def clock_signal(self) -> str:
        return self._clock_signal
    
    def _op_out(self, instr_params:list):
        self._clock_signal += str(self.int_or_reg_val(instr_params[0]))
        
        if (len(self.clock_signal) >= 2 and 
            self.clock_signal[-1] == self.clock_signal[-2]):
            raise StopIteration("Bad clock")
        elif len(self.clock_signal) > 100:
            raise StopIteration("Good clock!")

The main part of the program will then loop through positive integers, starting from 0, and use each one as the initial value for register a. It will run the Assembunny program and catch the StopIteration exception. If it’s a “Bad clock”, it will move on to the next integer. If it’s a “Good clock!”, it will print the integer and terminate.

def main():
    # ... (load instructions)
    
    reg_input = 0
    while True:
        computer = Assembunny3()
        computer.set_register('a', reg_input)
        try:
            computer.run_program(data)
        except StopIteration as err:
            if err.value == "Bad clock":
                # try next input value
            else: 
                logger.info("Good clock with %d: %s", reg_input, computer.clock_signal)
                break
            
        reg_input += 1

Part 2

There is no Part 2 for this puzzle! Merry Christmas!

Results

Good clock with 189: 01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010
Execution time: 2.1011 seconds

And that’s a wrap for Advent of Code 2016! It’s been a fun ride, with plenty of interesting challenges. The Assembunny problems, in particular, were a great way to explore interpreters and program optimization.