Skip to content

Commit a3d152c

Browse files
committed
💾 💻 🛑 🔥 Day 24, Arithmetic Logic Unit HCF.
1 parent f2422e0 commit a3d152c

File tree

2 files changed

+39
-186
lines changed

2 files changed

+39
-186
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@
2626
* [Day 21 - Dirac Dice :game_die: :atom:](day21-dirac-dice)
2727
* [Day 22 - Reactor Reboot :radioactive: :recycle:](day22-reactor-reboot)
2828
* [Day 23 - Amphipod :crab: :lobster: :shrimp: :lobster: :crab:](day23-amphipod)
29+
* [Day 24 - Arithmetic Logic Unit :floppy_disk: :computer: :stop_sign: :fire:](day24-arithmetic-logic-unit)

day24-arithmetic-logic-unit/solution.py

Lines changed: 38 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ class ALU:
1818
def __init__(self, instrcutions):
1919
self.instructions = instrcutions
2020
self._procedures = self.procedures()
21-
self.behaviour = self.detect_procedures_behaviour()
2221

2322
def procedures(self):
2423
procs = []
@@ -78,136 +77,6 @@ def run(self, inputs):
7877
z = state['z']
7978
return z
8079

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-
21180
def assert_procedure(self, proc_id, proc, tests=1000):
21281
passed = 0
21382
failed = 0
@@ -224,7 +93,9 @@ def assert_procedure(self, proc_id, proc, tests=1000):
22493
print('Tests: {}. Failed: {}. Passed: {}'.format((failed+passed), failed, passed))
22594
return failed
22695

227-
96+
#####################################################################
97+
# Reverse-engineered procedures and reverse procedures from the input
98+
#####################################################################
22899
def procedure_0(z, w):
229100
return z * 26 + w + 14
230101

@@ -442,55 +313,8 @@ def rev_procedure_13(target):
442313
if target%26 == (i+7):
443314
results[i].append(target - (target%26))
444315
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+
########################################
494318

495319
procedures = [
496320
(procedure_0, rev_procedure_0),
@@ -540,11 +364,39 @@ def _assert(truth, expected, actual):
540364

541365
preliminary_tests(read_input('input'))
542366

543-
def part1(instructions):
544-
alu = ALU(instrcutions)
545367

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
547376

548377
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

Comments
 (0)