Sean McLemon | Advent of Code

Home | Czech | Blog | GitHub | Advent Of Code | Notes


2018-12-16 - Chronal Calibration

(original .ipynb)
puzzle_input_str = open("puzzle_input/day16.txt").read()

def parse_sample(sample_str):
    before_str, instr_str, after_str = sample_str.split("\n")

    before = [int(s) for s in before_str[9:][:-1].split(", ")]
    instr = [int(s) for s in instr_str.split(" ")]
    after = [int(s) for s in after_str[9:][:-1].split(", ")]
    
    return (before, instr, after)

def parse_samples(samples_str):
    return [ parse_sample(sample_str) for sample_str in samples_str.split("\n\n")]

def parse_program(program_str):
    return program_str.split("\n")

def parse_input(input_str):
    samples_str, puzzle_program_str = input_str.split("\n\n\n\n")
    
    return parse_samples(samples_str), parse_program(puzzle_program_str)
    
samples, program = parse_input(puzzle_input_str)
class VM(object):
    def __init__(self, register_count):
        self.register = [0,0,0,0]
        self.opcodes = {
            "addr": self.addr,
            "addi": self.addi,
            "mulr": self.mulr,
            "muli": self.muli,
            "banr": self.banr,
            "bani": self.bani,
            "borr": self.borr,
            "bori": self.bori,
            "setr": self.setr,
            "seti": self.seti,
            "gtir": self.gtir,
            "gtri": self.gtri,
            "gtrr": self.gtrr,
            "eqir": self.eqir,
            "eqri": self.eqri,
            "eqrr": self.eqrr
        }

        
    def execute(self, opcode, a, b, c):
        decode = {
            0 : self.eqir,
            1 : self.seti,
            2 : self.eqri,
            3 : self.eqrr,
            4 : self.addi,
            5 : self.setr,
            6 : self.gtrr,
            7 : self.gtri,
            8 : self.muli,
            9 : self.bori,
            10: self.bani,
            11: self.borr,
            12: self.gtir,
            13: self.banr,
            14: self.addr,
            15: self.mulr
        }
        
        assert(decode[opcode](a,b,c))        
        
    def any_invalid_registers(self, regs):
        result = True

        for reg in regs:
            result = result & (reg < len(self.register))

        return not result

    def test_opcode(self, opcode_name, initial_register_state, a, b, c, final_register_state):
        self.register = [ reg for reg in initial_register_state ]
        result = self.opcodes[opcode_name](a, b, c)
        return result and (self.register == final_register_state)

    # addr (add register) stores into register C the result of adding register A and register B.
    def addr(self, a, b, c):
        if self.any_invalid_registers((a,b,c)):
            return False

        self.register[c] = self.register[a] + self.register[b]
        return True

    # addi (add immediate) stores into register C the result of adding register A and value B.
    def addi(self, a, b, c):
        if self.any_invalid_registers((a, c)):
            return False    

        self.register[c] = self.register[a] + b
        return True

    # mulr (multiply register) stores into register C the result of multiplying register A and register B.
    def mulr(self, a, b, c):
        if self.any_invalid_registers((a, b, c)):
            return False

        self.register[c] = self.register[a] * self.register[b]
        return True

    # muli (multiply immediate) stores into register C the result of multiplying register A and value B.
    def muli(self, a, b, c):
        if self.any_invalid_registers((a, c)):
            return False

        self.register[c] = self.register[a] * b
        return True

    # banr (bitwise AND register) stores into register C the result of the bitwise AND of register A and register B.
    def banr(self, a, b, c):
        if self.any_invalid_registers((a, b, c)):
            return False

        self.register[c] = self.register[a] & self.register[b]
        return True

    # bani (bitwise AND immediate) stores into register C the result of the bitwise AND of register A and value B.
    def bani(self, a, b, c):
        if self.any_invalid_registers((a, c)):
            return False

        self.register[c] = self.register[a] & b
        return True

    # borr (bitwise OR register) stores into register C the result of the bitwise OR of register A and register B.
    def borr(self, a, b, c):
        if self.any_invalid_registers((a, b, c)):
            return False

        self.register[c] = self.register[a] | self.register[b]
        return True


    # bori (bitwise OR immediate) stores into register C the result of the bitwise OR of register A and value B.
    def bori(self, a, b, c):
        if self.any_invalid_registers((a, c)):
            return False

        self.register[c] = self.register[a] | b
        return True

    # setr (set register) copies the contents of register A into register C. (Input B is ignored.)
    def setr(self, a, b, c):
        if self.any_invalid_registers((a, c)):
            return False

        self.register[c] = self.register[a]
        return True

    # seti (set immediate) stores value A into register C. (Input B is ignored.)
    def seti(self, a, b, c):
        if self.any_invalid_registers([c]):
            return False

        self.register[c] = a
        return True

    # gtir (greater-than immediate/register) sets register C to 1 if value A is greater than register B. Otherwise, register C is set to 0.
    def gtir(self, a, b, c):
        if self.any_invalid_registers((c, b)):
            return False

        self.register[c] = int(a > self.register[b])
        return True

    # gtri (greater-than register/immediate) sets register C to 1 if register A is greater than value B. Otherwise, register C is set to 0.
    def gtri(self, a, b, c):
        if self.any_invalid_registers((c, a)):
            return False

        self.register[c] = int(self.register[a] > b)
        return True

    # gtrr (greater-than register/register) sets register C to 1 if register A is greater than register B. Otherwise, register C is set to 0.
    def gtrr(self, a, b, c):
        if self.any_invalid_registers((a, b)):
            return False

        self.register[c] = int(self.register[a] > self.register[b])
        return True

    # eqir (equal immediate/register) sets register C to 1 if value A is equal to register B. Otherwise, register C is set to 0.
    def eqir(self, a, b, c):
        if self.any_invalid_registers((c, b)):
            return False

        self.register[c] = int(a == self.register[b])
        return True

    # eqri (equal register/immediate) sets register C to 1 if register A is equal to value B. Otherwise, register C is set to 0.
    def eqri(self, a, b, c):
        if self.any_invalid_registers((c, a)):
            return False

        self.register[c] = int(self.register[a] == b)
        return True

    # eqrr (equal register/register) sets register C to 1 if register A is equal to register B. Otherwise, register C is set to 0.
    def eqrr(self, a, b, c):
        if self.any_invalid_registers((a,b,c)):
            return False

        self.register[c] = int(self.register[a] == self.register[b])
        return True
# Part 1

from pprint import pprint 

vm = VM(4)

matching_opcode_by_name = { opcode_name:set() for opcode_name in vm.opcodes }
matching_opcode_by_code = [ set() for opcode_name in vm.opcodes ]
little_test_data =[
    [ [0, 2, 2, 2], 4, 2, 3, 2, [0, 2, 5, 2] ],
#     [ [3, 2, 1, 1], 9, 2, 1, 2, [3, 2, 2, 1] ]
]

three_or_more_matching = 0

for test_case in samples:
    before, (op, a, b, c), after = test_case
    matching_opcodes = 0
    for opcode_name in vm.opcodes:
        if vm.test_opcode(opcode_name, before, a, b, c, after):
            matching_opcode_by_name[opcode_name].add(op)
            matching_opcode_by_code[op].add(opcode_name)
            matching_opcodes += 1
    if (matching_opcodes >= 3):
        three_or_more_matching += 1
    
print(three_or_more_matching)
521
pprint(matching_opcode_by_name)

for i, opcodes in enumerate(matching_opcode_by_code):
    print(i, ", ".join(opcodes))

# mapping is:
#         0  eqir
#         1  seti
#         2  eqri
#         3  eqrr
#         4  addi
#         5  setr
#         6  gtrr
#         7  gtri
#         8  muli
#         9  bori
#         10 bani
#         11 borr
#         12 gtir
#         13 banr
#         14 addr
#         15 mulr
{'addi': {1, 4, 5, 8, 9, 10, 13, 14, 15},
 'addr': {0, 1, 5, 9, 10, 11, 14},
 'bani': {10},
 'banr': {10, 13},
 'bori': {1, 5, 9, 10, 13},
 'borr': {0, 1, 5, 9, 10, 11, 13},
 'eqir': {0, 12, 13, 6},
 'eqri': {0, 2, 12, 6},
 'eqrr': {2, 3, 12, 13},
 'gtir': {10, 12, 5},
 'gtri': {3, 5, 6, 7, 10, 12, 13},
 'gtrr': {10, 12, 5, 6},
 'muli': {8, 9, 10, 14},
 'mulr': {1, 8, 9, 10, 11, 13, 14, 15},
 'seti': {0, 1, 2, 3, 5, 6, 7, 10, 12, 13},
 'setr': {10, 13, 5}}
0 borr, eqri, eqir, seti, addr
1 mulr, borr, bori, seti, addi, addr
2 eqri, eqrr, seti
3 eqrr, seti, gtri
4 addi
5 gtrr, borr, bori, setr, gtir, seti, gtri, addi, addr
6 gtrr, eqri, eqir, seti, gtri
7 seti, gtri
8 mulr, addi, muli
9 mulr, borr, bori, addi, muli, addr
10 mulr, gtrr, borr, bori, setr, bani, banr, gtir, seti, gtri, addi, muli, addr
11 mulr, borr, addr
12 gtrr, eqri, gtir, eqir, seti, eqrr, gtri
13 mulr, borr, bori, setr, banr, eqir, seti, gtri, eqrr, addi
14 mulr, addi, muli, addr
15 mulr, addi
vm = VM(4)
vm.register = [0,0,0,0]

for raw_instruction in program:
    op, a, b, c = [int(s) for s in raw_instruction.split(" ")]
    vm.execute(op, a, b, c)
    
print(vm.register[0])
594