Skip to content

Commit 469ef23

Browse files
committed
Day 17, optimise for improved speed
1 parent d269858 commit 469ef23

File tree

1 file changed

+44
-18
lines changed

1 file changed

+44
-18
lines changed

solutions/day17.py

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ def get_combo_value(self, operand: int) -> int:
4848
"""
4949
if 0 <= operand <= 3:
5050
return operand
51-
51+
5252
elif operand == 4:
5353
return self.registers["A"]
54-
54+
5555
elif operand == 5:
5656
return self.registers["B"]
57-
57+
5858
elif operand == 6:
5959
return self.registers["C"]
60-
60+
6161
else:
6262
raise ValueError(f"Invalid combo operand: {operand}")
6363

@@ -100,7 +100,7 @@ def execute_instruction(self, program: List[int]) -> bool:
100100
if self.registers["A"] != 0:
101101
self.instruction_pointer = operand
102102
return True
103-
103+
104104
elif opcode == 4: # bxc
105105
self.registers["B"] ^= self.registers["C"]
106106

@@ -116,32 +116,41 @@ def execute_instruction(self, program: List[int]) -> bool:
116116
self.instruction_pointer += 2
117117
return True
118118

119-
def run(self, program: List[int]) -> str:
119+
def run(self, program: List[int]) -> List[int]:
120120
"""Run the entire program until completion.
121121
122122
Args:
123123
program (List[int]): List of integers representing the program instructions
124124
125125
Returns:
126-
str: Comma-separated string of output values generated during execution
126+
List[int]: List of output values generated during execution
127127
"""
128+
self.output = [] # Reset output before running
128129
while self.execute_instruction(program):
129130
pass
130131

131-
return ",".join(map(str, self.output))
132+
return self.output
132133

133-
def check_output(self, program: List[int]) -> bool:
134+
def check_output(self, program: List[int], partial: bool = False) -> bool:
134135
"""Check if the program's output matches its own instructions.
135136
136137
Args:
137138
program (List[int]): List of integers representing the program instructions
139+
partial (bool, optional): If True, allow checking partial output matches.
140+
Defaults to False.
138141
139142
Returns:
140-
bool: True if program output exactly matches the input program, False otherwise
143+
bool: True if program output exactly matches the input program
144+
(or matches partially if partial=True), False otherwise
141145
"""
142-
output_str = self.run(program)
143-
output_numbers = [int(x) for x in output_str.split(",")]
144-
return output_numbers == program
146+
output = self.run(program)
147+
if len(output) > len(program):
148+
return False
149+
150+
if partial:
151+
return output == program[: len(output)]
152+
153+
return output == program
145154

146155

147156
class Solution(SolutionBase):
@@ -194,25 +203,42 @@ def part1(self, data: List[str]) -> str:
194203
"""
195204
reg_a, reg_b, reg_c, program = self.parse_data(data)
196205
computer = ThreeBitComputer(reg_a, reg_b, reg_c)
197-
return computer.run(program)
206+
return ",".join(map(str, computer.run(program)))
198207

199208
def part2(self, data: List[str]) -> int:
200209
"""Find lowest positive value for register A that makes program output itself.
201210
211+
Uses mathematical patterns based on powers of 8 to efficiently find the solution.
212+
Each digit in the output corresponds to a power of 8, allowing us to adjust the
213+
input value A systematically rather than trying every possibility.
214+
202215
Args:
203216
data (List[str]): Input lines containing register values and program
204217
205218
Returns:
206219
int: Lowest positive value for register A that causes program to output
207220
a copy of its own instructions
221+
222+
Raises:
223+
ValueError: If the program generates output longer than itself
208224
"""
209225
_, reg_b, reg_c, program = self.parse_data(data)
210226

211-
# Start from 1 as we need lowest positive value
212-
a = 1
227+
# Calculate initial value based on program length and powers of 8
228+
a = sum(7 * 8**i for i in range(len(program) - 1)) + 1
229+
213230
while True:
214231
computer = ThreeBitComputer(a, reg_b, reg_c)
215-
if computer.check_output(program):
232+
output = computer.run(program)
233+
234+
if len(output) > len(program):
235+
raise ValueError("Output longer than program")
236+
237+
if output == program:
216238
return a
217239

218-
a += 1
240+
# Find position of first mismatch and adjust A accordingly
241+
for i in range(len(output) - 1, -1, -1):
242+
if output[i] != program[i]:
243+
a += 8**i # Adjust A by power of 8 at mismatch position
244+
break

0 commit comments

Comments
 (0)