14
14
from aoc .vm import Program
15
15
from aoc .vm import VirtualMachine
16
16
17
- Input = InputData
17
+ Input = tuple [ int , int , int , list [ int ]]
18
18
Output1 = str
19
19
Output2 = int
20
20
37
37
38
38
class Solution (SolutionBase [Input , Output1 , Output2 ]):
39
39
def parse_input (self , input_data : InputData ) -> Input :
40
- return input_data
40
+ lines = list (input_data )
41
+ a , b , c = map (int , (lines [i ][12 :] for i in range (3 )))
42
+ ops = list (map (int , lines [4 ][9 :].split ("," )))
43
+ return a , b , c , ops
41
44
42
- def run_program (self , lines : list [str ]) -> list [str ]:
45
+ def create_instructions (self , ops : list [int ]) -> list [Instruction ]:
43
46
def combo (operand : int ) -> str :
44
47
match operand :
45
48
case 0 | 1 | 2 | 3 :
@@ -51,30 +54,21 @@ def combo(operand: int) -> str:
51
54
52
55
ins = []
53
56
ip_map = dict [int , int ]()
54
- ins .append (Instruction .SET ("A" , lines [0 ][12 :]))
55
- ins .append (Instruction .SET ("B" , lines [1 ][12 :]))
56
- ins .append (Instruction .SET ("C" , lines [2 ][12 :]))
57
- ip = 3
58
- ops = list (map (int , lines [4 ][9 :].split ("," )))
57
+ ip = 0
59
58
for i in range (0 , len (ops ), 2 ):
60
59
ip_map [i ] = ip
61
60
opcode , operand = ops [i ], ops [i + 1 ]
62
61
match opcode :
63
62
case 0 :
64
- ins .append (Instruction .SET ("X" , "2" ))
65
- ins .append (Instruction .SET ("Y" , combo (operand )))
66
- ins .append (Instruction .ADD ("Y" , - 1 ))
67
- ins .append (Instruction .LSH ("X" , "*Y" ))
68
- ins .append (Instruction .DIV ("A" , "*X" ))
69
- ip += 5
63
+ ins .append (Instruction .RSH ("A" , combo (operand )))
64
+ ip += 1
70
65
case 1 :
71
66
ins .append (Instruction .XOR ("B" , str (operand )))
72
67
ip += 1
73
68
case 2 :
74
- ins .append (Instruction .SET ("X" , str (combo (operand ))))
75
- ins .append (Instruction .MOD ("X" , "8" ))
76
- ins .append (Instruction .SET ("B" , "*X" ))
77
- ip += 3
69
+ ins .append (Instruction .SET ("B" , str (combo (operand ))))
70
+ ins .append (Instruction .AND ("B" , "7" ))
71
+ ip += 2
78
72
case 3 :
79
73
ins .append (
80
74
Instruction .JN0 ("*A" , "!" + str (ip_map [operand ]))
@@ -85,61 +79,55 @@ def combo(operand: int) -> str:
85
79
ip += 1
86
80
case 5 :
87
81
ins .append (Instruction .SET ("X" , str (combo (operand ))))
88
- ins .append (Instruction .MOD ("X" , "8 " ))
82
+ ins .append (Instruction .AND ("X" , "7 " ))
89
83
ins .append (Instruction .OUT ("*X" ))
90
84
ip += 3
91
85
case 6 :
92
- ins .append (Instruction .SET ("X" , "2" ))
93
- ins .append (Instruction .SET ("Y" , combo (operand )))
94
- ins .append (Instruction .ADD ("Y" , - 1 ))
95
- ins .append (Instruction .LSH ("X" , "*Y" ))
96
- ins .append (Instruction .SET ("B" , "*A" ))
97
- ins .append (Instruction .DIV ("B" , "*X" ))
98
- ip += 6
86
+ ins .append (Instruction .SET ("C" , "*B" ))
87
+ ins .append (Instruction .RSH ("C" , combo (operand )))
88
+ ip += 2
99
89
case 7 :
100
- ins .append (Instruction .SET ("X" , "2" ))
101
- ins .append (Instruction .SET ("Y" , combo (operand )))
102
- ins .append (Instruction .ADD ("Y" , - 1 ))
103
- ins .append (Instruction .LSH ("X" , "*Y" ))
104
90
ins .append (Instruction .SET ("C" , "*A" ))
105
- ins .append (Instruction .DIV ("C" , "*X" ))
106
- ip += 6
91
+ ins .append (Instruction .RSH ("C" , combo (operand )))
92
+ ip += 2
93
+ case _:
94
+ raise ValueError
95
+ return ins
96
+
97
+ def run_program (
98
+ self , ins : list [Instruction ], a : int , b : int , c : int
99
+ ) -> list [str ]:
107
100
output = []
108
101
program = Program (ins , output_consumer = lambda s : output .append (s ))
109
- vm = VirtualMachine ()
110
- vm .run_program (program )
102
+ program .registers ["A" ] = int (a )
103
+ program .registers ["B" ] = int (b )
104
+ program .registers ["C" ] = int (c )
105
+ VirtualMachine ().run_program (program )
111
106
return output
112
107
113
108
def part_1 (self , input : Input ) -> Output1 :
114
- lines = list ( input )
115
- output = self .run_program ( lines )
116
- return "," .join (map ( str , output ))
109
+ a , b , c , ops = input
110
+ ins = self .create_instructions ( ops )
111
+ return "," .join (self . run_program ( ins , a , b , c ))
117
112
118
113
def part_2 (self , input : Input ) -> Output2 :
119
- lines = list (input )
120
-
121
- def run_with (a : str ) -> list [str ]:
122
- lines [0 ] = "Register A: " + a
123
- return self .run_program (lines )
124
-
125
- wanted = lines [4 ][9 :].replace ("," , "" )
114
+ _ , b , c , ops = input
115
+ ins = self .create_instructions (ops )
116
+ wanted = list (str (_ ) for _ in ops )
126
117
log (f"{ wanted = } " )
127
- seen = set (["0" ])
128
- q = deque (["0" ])
118
+ seen = set ([0 ])
119
+ q = deque ([0 ])
129
120
while q :
130
- a = q .popleft ()
131
- if "" .join (str (_ ) for _ in run_with (a )) == wanted :
132
- return int (a )
133
- na = int (a ) * 8
121
+ cand_a = q .popleft () * 8
134
122
for i in range (8 ):
135
- test = str ( na + i )
136
- res = "" . join ( str ( _ ) for _ in run_with ( test ) )
137
- size = len ( res )
138
- if res == wanted [ - size :]:
139
- if test not in seen :
140
- seen .add (test )
141
- log (test )
142
- q .append (test )
123
+ na = cand_a + i
124
+ res = self . run_program ( ins , na , b , c )
125
+ if res == wanted :
126
+ return na
127
+ if res == wanted [ - len ( res ) :] and na not in seen : # noqa E203
128
+ seen .add (na )
129
+ log (na )
130
+ q .append (na )
143
131
raise RuntimeError ("unsolvable" )
144
132
145
133
@aoc_samples (
0 commit comments