2018-12-21 - Chronal Conversion
(original .ipynb)
class VMCustomized(object):
def __init__(self, register):
self.r1_values = []
self.ticks = 0
self.ip = -1
self.ip_binding = None
self.register = register
self.instructions = []
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 load(self, instructions):
self.instructions = []
for instruction in instructions:
if "#ip" == instruction[:3]:
_, ip_str = instruction.split(" ")
self.ip_binding = int(ip_str)
else:
op, a, b, c = instruction.split(" ")
self.instructions.append([op, int(a), int(b), int(c)])
def get_debug_state(self):
return " ".join([
f"ip={self.ip}",
str(self.register),
" ".join(str(i) for i in self.instructions[self.ip])
])
def serialize_state(self):
return "-".join(str(r) for r in self.register)
def run(self, debug=False):
while True:
try:
self.ip += 1
if self.ip == 28:
print(self.register[1])
if (self.register[1] in self.r1_values):
break
self.r1_values.append(self.register[1])
debug_state = self.get_debug_state()
self.step()
self.ticks += 1
if debug:
print(debug_state, str(self.register))
except:
break
return 0
def step(self):
if self.ip >= len(self.instructions):
return False
op, a, b, c = self.instructions[self.ip]
self.execute(op, a, b, c)
return True
def execute(self, opcode, a, b, c):
if self.ip_binding != None:
self.register[self.ip_binding] = self.ip
decode_int = {
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
}
decode_str = {
"eqir" : self.eqir,
"seti" : self.seti,
"eqri" : self.eqri,
"eqrr" : self.eqrr,
"addi" : self.addi,
"setr" : self.setr,
"gtrr" : self.gtrr,
"gtri" : self.gtri,
"muli" : self.muli,
"bori" : self.bori,
"bani" : self.bani,
"borr" : self.borr,
"gtir" : self.gtir,
"banr" : self.banr,
"addr" : self.addr,
"mulr" : self.mulr
}
if (type(opcode) is str):
assert(decode_str[opcode](a,b,c))
elif (type(opcode) is int):
assert(decode_int[opcode](a,b,c))
else:
raise Exception(f"opcode {opcode} is neither int nor str")
if self.ip_binding != None:
self.ip = self.register[self.ip_binding]
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
def disassemble(self):
return "\n".join(
f"{i:02}: " + self.disassemble_instr(instr)
for i, instr in enumerate(self.instructions)
)
def disassemble_instr(self, instr):
op, a_, b_, c = instr
a = self.disassemble_reg(a_, True)
ra = self.disassemble_reg(a_, False)
b = self.disassemble_reg(b_, True)
rb = self.disassemble_reg(b_, False)
rc = self.disassemble_reg(c, False)
diss_instr = {
"addr": "{ra} + {rb}",
"addi": "{ra} + {b}",
"mulr": "{ra} * {rb}",
"muli": "{ra} * {b}",
"banr": "{ra} & {rb}",
"bani": "{ra} & {b}",
"borr": "{ra} | {rb}",
"bori": "{ra} | {b}",
"setr": "{ra}",
"seti": "{a}",
"gtrr": "{ra} > {rb} ? 1 : 0",
"gtri": "{ra} > {b} ? 1 : 0",
"gtir": "{a} > {rb} ? 1 : 0",
"eqir": "{a} == {rb} ? 1 : 0",
"eqri": "{ra} == {b} ? 1 : 0",
"eqrr": "{ra} == {rb} ? 1 : 0",
}
return f"{rc} = " + diss_instr[op].format(a=a, ra=ra, b=b, rb=rb)
def disassemble_reg(self, reg, immediate):
if self.ip_binding != None and reg == self.ip_binding:
return "IP"
elif immediate:
return str(reg)
return f"R{reg}"
puzzle_input_str = open("puzzle_input/day21.txt").read()
def disassemble():
instructions = puzzle_input_str.split("\n")
vm = VMCustomized([0] * 6)
vm.load(instructions)
print(vm.disassemble())
# run_program(puzzle_input_str, [0] * 6)
disassemble()
00: R1 = 123
01: R1 = R1 & 456
02: R1 = R1 == 72 ? 1 : 0
03: IP = R1 + IP
04: IP = 0
05: R1 = 0
06: R2 = R1 | 65536
07: R1 = 8725355
08: R5 = R2 & 255
09: R1 = R1 + R5
10: R1 = R1 & 16777215
11: R1 = R1 * 65899
12: R1 = R1 & 16777215
13: R5 = 256 > R2 ? 1 : 0
14: IP = R5 + IP
15: IP = IP + 1
16: IP = 27
17: R5 = 0
18: R3 = R5 + 1
19: R3 = R3 * 256
20: R3 = R3 > R2 ? 1 : 0
21: IP = R3 + IP
22: IP = IP + 1
23: IP = 25
24: R5 = R5 + 1
25: IP = 17
26: R2 = R5
27: IP = 7
28: R5 = R1 == R0 ? 1 : 0
29: IP = R5 + IP
30: IP = 5
instructions = puzzle_input_str.split("\n")
vm = VMCustomized(registers)
vm.load(instructions)
vm.run(debug)