-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathp17.py
122 lines (95 loc) · 3.1 KB
/
p17.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import re
from aocd import data
class ReservedComboOperandError(Exception):
"""Raised if given a combo operator of 7."""
class Computer:
def __init__(self, reg_a, reg_b=0, reg_c=0):
self.reg_a = reg_a
self.reg_b = reg_b
self.reg_c = reg_c
self.pointer = 0
self.output = []
self.instruction = {
0: self.adv,
1: self.bxl,
2: self.bst,
3: self.jnz,
4: self.bxc,
5: self.out,
6: self.bdv,
7: self.cdv,
}
def get_combo_operand(self, operand):
if 0 <= operand <= 3:
return operand
elif operand == 4:
return self.reg_a
elif operand == 5:
return self.reg_b
elif operand == 6:
return self.reg_c
elif operand == 7:
raise ReservedComboOperandError
def adv(self, operand):
numerator = self.reg_a
denominator = 2 ** self.get_combo_operand(operand)
self.reg_a = numerator // denominator
def bxl(self, operand):
self.reg_b ^= operand
def bst(self, operand):
self.reg_b = self.get_combo_operand(operand) % 8
def jnz(self, operand):
if self.reg_a == 0:
self.pointer += 2
else:
self.pointer = operand
def bxc(self, operand):
self.reg_b ^= self.reg_c
def out(self, operand):
value = self.get_combo_operand(operand) % 8
self.output.append(str(value))
def bdv(self, operand):
numerator = self.reg_a
denominator = 2 ** self.get_combo_operand(operand)
self.reg_b = numerator // denominator
def cdv(self, operand):
numerator = self.reg_a
denominator = 2 ** self.get_combo_operand(operand)
self.reg_c = numerator // denominator
def execute(self, instructions):
while True:
if self.pointer >= len(instructions) - 1:
output = ",".join(self.output)
return output
break
opcode = instructions[self.pointer]
operand = instructions[self.pointer + 1]
instruction = self.instruction[opcode]
instruction(operand)
if opcode != 3: # jnz
self.pointer += 2
def dfs(octals, target):
if octals:
output = Computer(int(octals, 8)).execute(target)
output = list(map(int, output.split(",")))
if output == target:
return int(octals, 8)
else:
output = []
reg_a = None
if output == target[-len(output):] or len(output) == 0:
for i in range(8):
if not octals and i == 0:
continue
reg_a = dfs(octals + str(i), target)
if reg_a is not None:
break
return reg_a
registers, program = data.split("\n\n")
reg_a, *_ = map(int, re.findall(r"(\d+)", registers))
instructions = list(map(int, re.findall(r"(\d+)", program)))
computer = Computer(reg_a)
output = computer.execute(instructions)
print("Part 1:", output)
reg_a = dfs("", instructions)
print("Part 2:", reg_a)