Guest User

Untitled

a guest
Dec 16th, 2024
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.56 KB | None | 0 0
  1. import sys
  2. import os
  3. import re
  4.  
  5. current_file_path = os.path.abspath(os.path.dirname(__file__))
  6. two_levels_down = os.path.abspath(os.path.join(current_file_path, "../.."))
  7. sys.path.append(two_levels_down)
  8.  
  9. from utils.io import read_lines
  10. from utils.timing import timeit
  11.  
  12. # Opcodes as constants for readability
  13. OP_ADV = 0 # adv: Division, store in A
  14. OP_BXL = 1 # bxl: Bitwise XOR, literal operand
  15. OP_BST = 2 # bst: Combo operand % 8 -> B
  16. OP_JNZ = 3 # jnz: Jump if A != 0
  17. OP_BXC = 4 # bxc: Bitwise XOR B and C
  18. OP_OUT = 5 # out: Output combo operand % 8
  19. OP_BDV = 6 # bdv: Division, store in B
  20. OP_CDV = 7 # cdv: Division, store in C
  21.  
  22. # Registers as indices for cleaner access
  23. REG_A = "A"
  24. REG_B = "B"
  25. REG_C = "C"
  26.  
  27. def resolve_combo_operand(value, registers):
  28. if value == 7:
  29. raise ValueError("Invalid combo operand: 7 is reserved")
  30. return {
  31. 0: 0,
  32. 1: 1,
  33. 2: 2,
  34. 3: 3,
  35. 4: registers[REG_A],
  36. 5: registers[REG_B],
  37. 6: registers[REG_C],
  38. }[value]
  39.  
  40. def parse_input(data, debug=False):
  41. registers = {}
  42. program = []
  43.  
  44. for line in data:
  45. if line.startswith("Register A"):
  46. registers[REG_A] = int(line.split(":")[1].strip())
  47. elif line.startswith("Register B"):
  48. registers[REG_B] = int(line.split(":")[1].strip())
  49. elif line.startswith("Register C"):
  50. registers[REG_C] = int(line.split(":")[1].strip())
  51. elif line.startswith("Program"):
  52. program = list(map(int, line.split(":")[1].strip().split(",")))
  53.  
  54. if debug:
  55. print(registers, program)
  56. return registers, program
  57.  
  58. def execute_program(program, registers):
  59. instruction_pointer = 0
  60. output = []
  61. MAX_ITERATIONS = 10000 # Safety limit
  62.  
  63. while instruction_pointer < len(program) and MAX_ITERATIONS > 0:
  64. opcode = program[instruction_pointer]
  65. operand = program[instruction_pointer + 1]
  66.  
  67. print(f"IP: {instruction_pointer}, Opcode: {opcode}, Operand: {operand}, Registers: {registers}")
  68.  
  69. if opcode == OP_ADV:
  70. divisor = 2 ** resolve_combo_operand(operand, registers)
  71. registers[REG_A] //= divisor
  72. elif opcode == OP_BXL:
  73. registers[REG_B] ^= operand
  74. elif opcode == OP_BST:
  75. registers[REG_B] = resolve_combo_operand(operand, registers) % 8
  76. elif opcode == OP_JNZ:
  77. if registers[REG_A] != 0:
  78. instruction_pointer = operand
  79. print(f"JNZ Jump to {instruction_pointer}")
  80. continue # Skip the normal IP increment
  81. elif opcode == OP_BXC:
  82. registers[REG_B] ^= registers[REG_C]
  83. elif opcode == OP_OUT:
  84. value = resolve_combo_operand(operand, registers) % 8
  85. output.append(value)
  86. print(f"OUT: {value}")
  87. elif opcode == OP_BDV:
  88. divisor = 2 ** resolve_combo_operand(operand, registers)
  89. registers[REG_B] //= divisor
  90. elif opcode == OP_CDV:
  91. divisor = 2 ** resolve_combo_operand(operand, registers)
  92. registers[REG_C] //= divisor
  93. else:
  94. raise ValueError(f"Unknown opcode: {opcode}")
  95.  
  96. instruction_pointer += 2 # Move to the next instruction
  97. MAX_ITERATIONS -= 1
  98.  
  99. if MAX_ITERATIONS == 0:
  100. raise RuntimeError("Program execution exceeded maximum iterations")
  101.  
  102. print(f"Final Registers: {registers}, Output: {output}")
  103. return output
  104.  
  105. @timeit
  106. def part1(data):
  107. # Parse initial register values and program
  108. registers, program = parse_input(data)
  109.  
  110. # Execute program and collect outputs
  111. output = execute_program(program, registers)
  112.  
  113. # Join output values with commas
  114. return ",".join(map(str, output))
  115.  
  116. @timeit
  117. def part2(data):
  118. # Your solution here
  119. return None
  120.  
  121. def test_instruction_bst():
  122. # Test: If register C contains 9, the program 2,6 would set register B to 1.
  123. print("\nRunning test: BST instruction")
  124. registers = {REG_A: 0, REG_B: 0, REG_C: 9}
  125. program = [2, 6] # BST opcode with combo operand 6 (register C)
  126. output = execute_program(program, registers)
  127. if registers[REG_B] == 1 and output == []:
  128. print("PASS: BST instruction test")
  129. else:
  130. print(f"FAIL: Expected B=1, Output=[], but got B={registers[REG_B]}, Output={output}")
  131.  
  132. def test_instruction_out():
  133. # Test: If register A contains 10, the program 5,0,5,1,5,4 would output 0,1,2.
  134. print("\nRunning test: OUT instruction")
  135. registers = {REG_A: 10, REG_B: 0, REG_C: 0}
  136. program = [5, 0, 5, 1, 5, 4] # OUT opcode with combo operands 0, 1, and 4
  137. output = execute_program(program, registers)
  138. if output == [0, 1, 2]:
  139. print("PASS: OUT instruction test")
  140. else:
  141. print(f"FAIL: Expected Output=[0, 1, 2], but got Output={output}")
  142.  
  143. def test_instruction_adv_and_out():
  144. # Test: If register A contains 2024, the program 0,1,5,4,3,0 would output 4,2,5,6,7,7,7,7,3,1,0
  145. # and leave 0 in register A.
  146. print("\nRunning test: ADV and OUT instructions")
  147. registers = {REG_A: 2024, REG_B: 0, REG_C: 0}
  148. program = [0, 1, 5, 4, 3, 0] # ADV, OUT, and JNZ instructions
  149. output = execute_program(program, registers)
  150. if output == [4, 2, 5, 6, 7, 7, 7, 7, 3, 1, 0] and registers[REG_A] == 0:
  151. print("PASS: ADV and OUT instructions test")
  152. else:
  153. print(f"FAIL: Expected Output=[4, 2, 5, 6, 7, 7, 7, 7, 3, 1, 0], A=0, but got Output={output}, A={registers[REG_A]}")
  154.  
  155. def test_instruction_bxl():
  156. # Test: If register B contains 29, the program 1,7 would set register B to 26.
  157. print("\nRunning test: BXL instruction")
  158. registers = {REG_A: 0, REG_B: 29, REG_C: 0}
  159. program = [1, 7] # BXL opcode with literal operand 7
  160. output = execute_program(program, registers)
  161. if registers[REG_B] == 26 and output == []:
  162. print("PASS: BXL instruction test")
  163. else:
  164. print(f"FAIL: Expected B=26, Output=[], but got B={registers[REG_B]}, Output={output}")
  165.  
  166. def test_instruction_bxc():
  167. # Test: If register B contains 2024 and register C contains 43690, the program 4,0 would set register B to 44354.
  168. print("\nRunning test: BXC instruction")
  169. registers = {REG_A: 0, REG_B: 2024, REG_C: 43690}
  170. program = [4, 0] # BXC opcode, operand ignored
  171. output = execute_program(program, registers)
  172. if registers[REG_B] == 44354 and output == []:
  173. print("PASS: BXC instruction test")
  174. else:
  175. print(f"FAIL: Expected B=44354, Output=[], but got B={registers[REG_B]}, Output={output}")
  176.  
  177. def test_combined_example():
  178. # Combined Test: Provided example for Registers and Program
  179. # Register A: 729, Register B: 0, Register C: 0
  180. # Program: 0,1,5,4,3,0
  181. print("\nRunning test: Combined Example")
  182. registers = {REG_A: 729, REG_B: 0, REG_C: 0}
  183. program = [0, 1, 5, 4, 3, 0] # ADV, OUT, and JNZ instructions
  184. output = execute_program(program, registers)
  185. if output == [4, 6, 3, 5, 6, 3, 5, 2, 1, 0]:
  186. print("PASS: Combined Example test")
  187. else:
  188. print(f"FAIL: Expected Output=[4, 6, 3, 5, 6, 3, 5, 2, 1, 0], but got Output={output}")
  189.  
  190. if __name__ == "__main__":
  191. # test_instruction_bst()
  192. # test_instruction_out()
  193. # test_instruction_adv_and_out()
  194. # test_instruction_bxl()
  195. # test_instruction_bxc()
  196. # test_combined_example()
  197.  
  198. input_file_path = os.path.join(current_file_path, "input.txt")
  199. input_data = read_lines(input_file_path)
  200. print(f"Part 1: {part1(input_data)}")
  201. # print(f"Part 2: {part2(input_data)}")
  202.  
Advertisement
Add Comment
Please, Sign In to add comment