@@ -18,7 +18,6 @@ class ALU:
18
18
def __init__ (self , instrcutions ):
19
19
self .instructions = instrcutions
20
20
self ._procedures = self .procedures ()
21
- self .behaviour = self .detect_procedures_behaviour ()
22
21
23
22
def procedures (self ):
24
23
procs = []
@@ -78,136 +77,6 @@ def run(self, inputs):
78
77
z = state ['z' ]
79
78
return z
80
79
81
- def check_for_digit (self , proc_id , d , target ):
82
- n = 0
83
- while True :
84
- res = self .exec_proc (proc_id , n , d )['z' ]
85
- if res == target :
86
- print (' .found one:' , proc_id , d , target , ' => ' , n )
87
- break
88
- n += 1
89
- if n > 10_000_000 :
90
- return None
91
- return n
92
-
93
- @cache
94
- def check_for_procedure (self , proc_id , target ):
95
- results = []
96
- d = 9
97
- while d >= 0 :
98
- res = self .check_for_digit (proc_id - 1 , d , target )
99
- if res is None :
100
- continue
101
- results .append (res )
102
-
103
- d -= 1
104
-
105
-
106
- def detect_procedure_behaviour_per_digit (self , proc_id , d ):
107
- detected_jump = None
108
- prev = None
109
- first = None
110
- coeffs = []
111
- for i in range (0 , 1000 ):
112
- res = self .exec_proc (proc_id , i , d )['z' ]
113
- if i :
114
- coeffs .append (res / i )
115
- if prev is None :
116
- prev = res
117
- continue
118
- if res < prev :
119
- if first is None :
120
- first = i
121
- else :
122
- detected_jump = i - first
123
- return ('jump' , detected_jump , first )
124
- prev = res
125
-
126
- # linear increase
127
-
128
- coeff = sum (coeffs )/ len (coeffs )
129
- if not (coeff > 25 and coeff < 27 ):
130
- print ('!!!!!>>' , coeff )
131
- raise Exception ('Not steady increase?' )
132
-
133
- return ('increase' , coeff )
134
-
135
- def detect_procedures_behaviour (self ):
136
- result = {}
137
- for proc_id in range (14 ):
138
- result [proc_id ] = {}
139
- for d in range (1 , 10 ):
140
- result [proc_id ][d ] = self .detect_procedure_behaviour_per_digit (proc_id , d )
141
- return result
142
-
143
- def find_target (self , proc_id , d , target ):
144
- beh = self .behaviour [proc_id ][d ]
145
- if beh [0 ] == 'jump' :
146
- _ , size , start = beh
147
- curr = self .exec_proc (proc_id , start , d )['z' ]
148
- if curr == target :
149
- print ('exact' )
150
- return start
151
- start += size * (target - 1 )
152
- print ((target - start )/ size , 'steps' )
153
- while True :
154
- start += size
155
- res = self .exec_proc (proc_id , start , d )['z' ]
156
- if res == target :
157
- print ('found' , res )
158
- return start
159
- if res > target :
160
- return None
161
- #print(' checking', res, '==', target)
162
- elif beh [0 ] == 'increase' :
163
- _ , coeff = beh
164
- start = floor (target / (coeff + 0.2 ))
165
- end = ceil (target / (coeff - 0.2 ))
166
- #print('find ', target, ' between ', (start, end),
167
- # (self.exec_proc(proc_id, start, d)['z'],
168
- # self.exec_proc(proc_id, end, d)['z'],
169
- # ))
170
- for i in range (start , end + 1 ):
171
- res = self .exec_proc (proc_id , i , d )['z' ]
172
- if res == target :
173
- print (':::found' , i )
174
- return i
175
-
176
- return None
177
- else :
178
- raise Exception ('Uknown behaviour: {}' .format (beh ))
179
-
180
-
181
- @cache
182
- def find_target_p (self , proc_id , target ):
183
- possible = []
184
- for d in range (9 , 0 , - 1 ):
185
- res = self .find_target (proc_id , d , target )
186
- if res is None :
187
- continue
188
- possible .append ((res , d ))
189
-
190
- return possible
191
-
192
-
193
-
194
- def check (self ):
195
- q = [(13 , 0 )]
196
-
197
- c = 0
198
- while q :
199
- proc_id , target = q [0 ]
200
- q = q [1 :]
201
-
202
- if proc_id == 0 :
203
- print ('FOUND!' )
204
-
205
- for tg , d in self .find_target_p (proc_id , target ):
206
- q .append ((proc_id - 1 , tg ))
207
- c += 1
208
- if c % 10 == 0 :
209
- print (c , proc_id , len (q ))
210
-
211
80
def assert_procedure (self , proc_id , proc , tests = 1000 ):
212
81
passed = 0
213
82
failed = 0
@@ -224,7 +93,9 @@ def assert_procedure(self, proc_id, proc, tests=1000):
224
93
print ('Tests: {}. Failed: {}. Passed: {}' .format ((failed + passed ), failed , passed ))
225
94
return failed
226
95
227
-
96
+ #####################################################################
97
+ # Reverse-engineered procedures and reverse procedures from the input
98
+ #####################################################################
228
99
def procedure_0 (z , w ):
229
100
return z * 26 + w + 14
230
101
@@ -442,55 +313,8 @@ def rev_procedure_13(target):
442
313
if target % 26 == (i + 7 ):
443
314
results [i ].append (target - (target % 26 ))
444
315
return results
445
-
446
-
447
- def par1 (instructions ):
448
- alu = ALU (instructions )
449
- return alu .check ()
450
-
451
-
452
- #print('Part 1: ', par1(read_input('input')))
453
-
454
- # def test_procedures(instrs):
455
- # alu = ALU(instrs)
456
-
457
- # # print(
458
- # # alu.detect_procedure_behaviour(13)
459
- # # )
460
-
461
- # # for p in range(14):
462
- # # print('Proc: ', p)
463
- # # print('==========')
464
- # # for d in range(1, 10):
465
- # # r = alu.detect_procedure_behaviour_per_digit(p, d)
466
- # # print(' >', d, r)
467
-
468
- # # print(alu.find_target_p(12, 9))
469
- # # alu.check()
470
-
471
-
472
-
473
- # #alu.assert_procedure(12, procedure_12, 10_000)
474
- # # for i, proc in enumerate(alu.procedures()):
475
- # # print('Procedure:', i)
476
- # # for p in proc:
477
- # # print(' '.join(p))
478
-
479
- # for i in range(100):
480
- # print('==', i)
481
- # for w, pos in rev_procedure_13(i).items():
482
- # #print(z, pos)
483
- # for z in pos:
484
- # r = procedure_13(z, w)
485
- # print(' >', r, (z, w))
486
- # print(' >>', alu.exec_proc(13, z, w)['z'])
487
-
488
-
489
- # test_procedures(read_input('input'))
490
- # import os
491
- # os.exit(1)
492
- # # for i in range(30):
493
- # # print(procedure_0(i, 9))
316
+ ########################################
317
+ ########################################
494
318
495
319
procedures = [
496
320
(procedure_0 , rev_procedure_0 ),
@@ -540,11 +364,39 @@ def _assert(truth, expected, actual):
540
364
541
365
preliminary_tests (read_input ('input' ))
542
366
543
- def part1 (instructions ):
544
- alu = ALU (instrcutions )
545
367
546
- q = [(0 , 0 )]
368
+ def solve (instructions , part2 = False ):
369
+ # Simple DFS, but with sorted order of traversal based on the digits
370
+ alu = ALU (instructions )
371
+
372
+ # node is (procedure, digit, target)
373
+ q = [(13 , 0 , [])]
374
+
375
+ solution = None
547
376
548
377
while q :
549
- proc_id , target = q .pop ()
550
-
378
+ proc_id , target , path = q .pop ()
379
+
380
+ if proc_id < 0 :
381
+ solution = path
382
+ break
383
+ proc , rev_proc = procedures [proc_id ]
384
+
385
+ options = rev_proc (target )
386
+ keys = sorted (options .keys ())
387
+ if part2 :
388
+ keys = reversed (keys )
389
+ for ww in keys :
390
+ for n_target in options [ww ]:
391
+ q .append ((
392
+ proc_id - 1 ,
393
+ n_target ,
394
+ [ww ] + path ,
395
+ ))
396
+
397
+ assert alu .run (solution ) == 0
398
+ return '' .join (str (p ) for p in solution )
399
+
400
+
401
+ print ('Part 1:' , solve (read_input ('input' )))
402
+ print ('Part 2:' , solve (read_input ('input' ), part2 = True ))
0 commit comments