Skip to content

Commit af9ed83

Browse files
committed
Its not much but its honest work
1 parent 5bc8fd2 commit af9ed83

File tree

1 file changed

+226
-0
lines changed

1 file changed

+226
-0
lines changed

2024/17.py

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
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

Comments
 (0)