1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ def parse (data ):
5
+ lines = data .split ('\n ' )
6
+ register = {
7
+ 'A' : int (lines [0 ].split (': ' )[1 ]),
8
+ 'B' : int (lines [1 ].split (': ' )[1 ]),
9
+ 'C' : int (lines [2 ].split (': ' )[1 ]),
10
+ }
11
+ instructions = lines [4 ].split (': ' )[1 ].split (',' )
12
+ return register , [int (x ) for x in instructions ]
13
+
14
+ split_data = parse
15
+ completed = True
16
+ raw_data = None # Not To be touched
17
+
18
+ def part1 (data ):
19
+ register , instructions = data
20
+
21
+ def combo (oprand ):
22
+ if 0 <= oprand <= 3 :
23
+ return oprand
24
+ elif oprand == 4 :
25
+ return register ['A' ]
26
+ elif oprand == 5 :
27
+ return register ['B' ]
28
+ elif oprand == 6 :
29
+ return register ['C' ]
30
+ else :
31
+ raise "How are we executing a reserved value?"
32
+
33
+ pointer = 0
34
+ output = []
35
+ while pointer < len (instructions ):
36
+ opcode = instructions [pointer ]
37
+ oprand = instructions [pointer + 1 ]
38
+
39
+ match opcode :
40
+ case 0 :
41
+ register ['A' ] = int (register ['A' ] / (2 ** combo (oprand )))
42
+ pointer += 2
43
+ case 1 :
44
+ register ['B' ] = register ['B' ] ^ oprand
45
+ pointer += 2
46
+ case 2 :
47
+ register ['B' ] = combo (oprand ) % 8
48
+ pointer += 2
49
+ case 3 :
50
+ if register ['A' ] == 0 :
51
+ pointer += 2
52
+ else :
53
+ pointer = oprand
54
+ case 4 :
55
+ register ['B' ] = register ['B' ] ^ register ['C' ]
56
+ pointer += 2
57
+ case 5 :
58
+ output .append (str (combo (oprand ) % 8 ))
59
+ pointer += 2
60
+ case 6 :
61
+ register ['B' ] = int (register ['A' ] / (2 ** combo (oprand )))
62
+ pointer += 2
63
+ case 7 :
64
+ register ['C' ] = int (register ['A' ] / (2 ** combo (oprand )))
65
+ pointer += 2
66
+ case default :
67
+ raise "ERR"
68
+
69
+ # print(pointer, output, register)
70
+ return ',' .join (output )
71
+
72
+
73
+ def compute (register , instructions ):
74
+ pointer = 0
75
+ output = []
76
+ while pointer < len (instructions ):
77
+ opcode = instructions [pointer ]
78
+ oprand = instructions [pointer + 1 ]
79
+ combo = ({4 :register ['A' ],5 :register ['B' ],6 :register ['C' ]}).get (oprand , oprand )
80
+
81
+ match opcode :
82
+ case 0 :
83
+ register ['A' ] = int (register ['A' ] / (2 ** combo ))
84
+ pointer += 2
85
+ case 1 :
86
+ register ['B' ] = register ['B' ] ^ oprand
87
+ pointer += 2
88
+ case 2 :
89
+ register ['B' ] = combo % 8
90
+ pointer += 2
91
+ case 3 :
92
+ if register ['A' ] == 0 :
93
+ pointer += 2
94
+ else :
95
+ pointer = oprand
96
+ case 4 :
97
+ register ['B' ] = register ['B' ] ^ register ['C' ]
98
+ pointer += 2
99
+ case 5 :
100
+ output .append (combo % 8 )
101
+ if len (output ) > len (instructions ) or output [- 1 ] != instructions [len (output )- 1 ]: return False
102
+ pointer += 2
103
+ case 6 :
104
+ register ['B' ] = int (register ['A' ] / (2 ** combo ))
105
+ pointer += 2
106
+ case 7 :
107
+ register ['C' ] = int (register ['A' ] / (2 ** combo ))
108
+ pointer += 2
109
+
110
+ return output == instructions
111
+
112
+ def part2 (data ):
113
+ """
114
+ Forget about getting the answer by brute forcing it.
115
+ It's not going to happen as the values we are looking for are too large
116
+ A much simpler method is backtracking. We can look at our program and notice a few things
117
+
118
+ My program:
119
+
120
+ B = A mod 8
121
+ B = B XOR 7
122
+ C = A / 2**B
123
+ A = A / 8
124
+ B = B XOR 7
125
+ B = B XOR C
126
+ OUT (B mod 8)
127
+ if A != 0 then jump 0
128
+
129
+
130
+ It ends with 3,0 aka if A != 0 then jump 0
131
+ and the only modification done to A is A = A / 2**3
132
+
133
+ Thus we know that our A should end in 0 for the program to halt which leaves us with a range of for A at the start [0*8, 1*8)
134
+ The range equals out to 0,1,2,3,4,5,6,7. `[]` brackets means inclusive and `()` brackets mean exclusive.
135
+
136
+ Running these possibilities through the program we can see that only 0 and 7 output a `0`.
137
+ As A can't with a 0 (as it would cause the program to halt in the previous cycle) we know the program will have to have a 7 at the start.
138
+ Thus in the last cycle we begin with a 7 to allow the program to halt and also give us the required `0` as output
139
+ Now we can extend the same logic backwards. Now we check the [7*8, (7+1)*8) interval for a 3 as output giving us 58 and 60.
140
+ Thus creating the interval [58*8,(58+1)*8) and [60*8, (60+1)*8) to check for the third last value and so on...
141
+
142
+ Summary:
143
+ last value: [0*8, 1*8) interval that outputs 0
144
+ 2nd last value: [7*8, (7+1)*8) interval that outputs 3
145
+ 3rd last value: [58*8,(58+1)*8) and [60*8, (60+1)*8) interval that outputs x
146
+ and so on...
147
+
148
+ As we are checking at max 8n possibilities for a single cycle where n averages at ~8 we should not have to look for much
149
+
150
+ Thus we get values of A which all end up producing the output we want. This may only work my input
151
+ """
152
+
153
+ _ , program = data
154
+
155
+
156
+ def output (A ):
157
+ return ((A % 8 )^ 7 ^ 7 ^ int (A / 2 ** ((A % 8 ) ^ 7 ))) % 8 # Simplified version
158
+ print (A )
159
+ B = A % 8
160
+ B = A ^ 7
161
+ C = int (A / 2 ** B )
162
+ A = int (A / 8 )
163
+ B = B ^ 7
164
+ B = B ^ C
165
+ return B % 8
166
+
167
+ growth = [0 ]
168
+ n = []
169
+ for out in program [::- 1 ]:
170
+ growth = [x for a in growth for x in range (a * 8 , (a + 1 )* 8 ) if x != 0 and output (x ) == out ]
171
+ n .append (len (growth ))
172
+ return growth [0 ]
173
+
174
+ """
175
+ Program: 2,4 1,7 7,5 0,3 1,7 4,1 5,5 3,0
176
+
177
+ B = A mod 8
178
+ B = B XOR 7 (7,6,5,4,3,2,1,0 as possibilities)
179
+ C = A / 2**B
180
+ A = A / 8
181
+ B = B XOR 7 (0,1,2,3,4,5,6,7 as possibilities)
182
+ B = B XOR C
183
+ OUT B mod 8
184
+ if A != 0 then jump 0
185
+
186
+ ```
187
+ def check(A):
188
+ return ((A % 8) ^ int(A / 2**((A % 8) ^ 7))) % 8
189
+
190
+ growth = [0]
191
+ n = []
192
+ for output in (2,4,1,7,7,5,0,3,1,7,4,1,5,5,3,0)[::-1]:
193
+ growth = [x for a in growth for x in range(a*8, (a+1)*8) if x != 0 and check(x) == output]
194
+ n.append(len(growth))
195
+
196
+ growth[0]
197
+ ```
198
+
199
+
200
+
201
+ The output is being done by B essentially.
202
+ We can manually back track the entire fucking thing...
203
+
204
+ The only way to terminate the program is if A started less than 8
205
+ We get 0 as output if A was 0 or 7 at the start.
206
+
207
+ Knowing the logic of the program. We check [0*8, 1*8) and [7*8, 8*8) for output of 3.
208
+ Sure enough the outputs 3, 58, and 60 all produce 3 which go on to produce 0.
209
+ Thus we check for [3*8,4*8), [58*8, 59*8), and [60*8, 61*8) for production of 5.
210
+ and so on and we are bound to find the value...
211
+
212
+ Note: 0 can't be a possibility since it would mean that mean it would have terminated before...
213
+
214
+
215
+
216
+
217
+
218
+ Thus the next values we look
219
+
220
+ Hence A started out with <8
221
+ B = 7 - A
222
+ C = A / 2**(7-A) (0,1,3,7 as possible values)
223
+ B = A
224
+
225
+
226
+ """
0 commit comments