Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import sys
- import os
- import re
- current_file_path = os.path.abspath(os.path.dirname(__file__))
- two_levels_down = os.path.abspath(os.path.join(current_file_path, "../.."))
- sys.path.append(two_levels_down)
- from utils.io import read_lines
- from utils.timing import timeit
- # Opcodes as constants for readability
- OP_ADV = 0 # adv: Division, store in A
- OP_BXL = 1 # bxl: Bitwise XOR, literal operand
- OP_BST = 2 # bst: Combo operand % 8 -> B
- OP_JNZ = 3 # jnz: Jump if A != 0
- OP_BXC = 4 # bxc: Bitwise XOR B and C
- OP_OUT = 5 # out: Output combo operand % 8
- OP_BDV = 6 # bdv: Division, store in B
- OP_CDV = 7 # cdv: Division, store in C
- # Registers as indices for cleaner access
- REG_A = "A"
- REG_B = "B"
- REG_C = "C"
- def resolve_combo_operand(value, registers):
- if value == 7:
- raise ValueError("Invalid combo operand: 7 is reserved")
- return {
- 0: 0,
- 1: 1,
- 2: 2,
- 3: 3,
- 4: registers[REG_A],
- 5: registers[REG_B],
- 6: registers[REG_C],
- }[value]
- def parse_input(data, debug=False):
- registers = {}
- program = []
- for line in data:
- if line.startswith("Register A"):
- registers[REG_A] = int(line.split(":")[1].strip())
- elif line.startswith("Register B"):
- registers[REG_B] = int(line.split(":")[1].strip())
- elif line.startswith("Register C"):
- registers[REG_C] = int(line.split(":")[1].strip())
- elif line.startswith("Program"):
- program = list(map(int, line.split(":")[1].strip().split(",")))
- if debug:
- print(registers, program)
- return registers, program
- def execute_program(program, registers):
- instruction_pointer = 0
- output = []
- MAX_ITERATIONS = 10000 # Safety limit
- while instruction_pointer < len(program) and MAX_ITERATIONS > 0:
- opcode = program[instruction_pointer]
- operand = program[instruction_pointer + 1]
- print(f"IP: {instruction_pointer}, Opcode: {opcode}, Operand: {operand}, Registers: {registers}")
- if opcode == OP_ADV:
- divisor = 2 ** resolve_combo_operand(operand, registers)
- registers[REG_A] //= divisor
- elif opcode == OP_BXL:
- registers[REG_B] ^= operand
- elif opcode == OP_BST:
- registers[REG_B] = resolve_combo_operand(operand, registers) % 8
- elif opcode == OP_JNZ:
- if registers[REG_A] != 0:
- instruction_pointer = operand
- print(f"JNZ Jump to {instruction_pointer}")
- continue # Skip the normal IP increment
- elif opcode == OP_BXC:
- registers[REG_B] ^= registers[REG_C]
- elif opcode == OP_OUT:
- value = resolve_combo_operand(operand, registers) % 8
- output.append(value)
- print(f"OUT: {value}")
- elif opcode == OP_BDV:
- divisor = 2 ** resolve_combo_operand(operand, registers)
- registers[REG_B] //= divisor
- elif opcode == OP_CDV:
- divisor = 2 ** resolve_combo_operand(operand, registers)
- registers[REG_C] //= divisor
- else:
- raise ValueError(f"Unknown opcode: {opcode}")
- instruction_pointer += 2 # Move to the next instruction
- MAX_ITERATIONS -= 1
- if MAX_ITERATIONS == 0:
- raise RuntimeError("Program execution exceeded maximum iterations")
- print(f"Final Registers: {registers}, Output: {output}")
- return output
- @timeit
- def part1(data):
- # Parse initial register values and program
- registers, program = parse_input(data)
- # Execute program and collect outputs
- output = execute_program(program, registers)
- # Join output values with commas
- return ",".join(map(str, output))
- @timeit
- def part2(data):
- # Your solution here
- return None
- def test_instruction_bst():
- # Test: If register C contains 9, the program 2,6 would set register B to 1.
- print("\nRunning test: BST instruction")
- registers = {REG_A: 0, REG_B: 0, REG_C: 9}
- program = [2, 6] # BST opcode with combo operand 6 (register C)
- output = execute_program(program, registers)
- if registers[REG_B] == 1 and output == []:
- print("PASS: BST instruction test")
- else:
- print(f"FAIL: Expected B=1, Output=[], but got B={registers[REG_B]}, Output={output}")
- def test_instruction_out():
- # Test: If register A contains 10, the program 5,0,5,1,5,4 would output 0,1,2.
- print("\nRunning test: OUT instruction")
- registers = {REG_A: 10, REG_B: 0, REG_C: 0}
- program = [5, 0, 5, 1, 5, 4] # OUT opcode with combo operands 0, 1, and 4
- output = execute_program(program, registers)
- if output == [0, 1, 2]:
- print("PASS: OUT instruction test")
- else:
- print(f"FAIL: Expected Output=[0, 1, 2], but got Output={output}")
- def test_instruction_adv_and_out():
- # 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
- # and leave 0 in register A.
- print("\nRunning test: ADV and OUT instructions")
- registers = {REG_A: 2024, REG_B: 0, REG_C: 0}
- program = [0, 1, 5, 4, 3, 0] # ADV, OUT, and JNZ instructions
- output = execute_program(program, registers)
- if output == [4, 2, 5, 6, 7, 7, 7, 7, 3, 1, 0] and registers[REG_A] == 0:
- print("PASS: ADV and OUT instructions test")
- else:
- 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]}")
- def test_instruction_bxl():
- # Test: If register B contains 29, the program 1,7 would set register B to 26.
- print("\nRunning test: BXL instruction")
- registers = {REG_A: 0, REG_B: 29, REG_C: 0}
- program = [1, 7] # BXL opcode with literal operand 7
- output = execute_program(program, registers)
- if registers[REG_B] == 26 and output == []:
- print("PASS: BXL instruction test")
- else:
- print(f"FAIL: Expected B=26, Output=[], but got B={registers[REG_B]}, Output={output}")
- def test_instruction_bxc():
- # Test: If register B contains 2024 and register C contains 43690, the program 4,0 would set register B to 44354.
- print("\nRunning test: BXC instruction")
- registers = {REG_A: 0, REG_B: 2024, REG_C: 43690}
- program = [4, 0] # BXC opcode, operand ignored
- output = execute_program(program, registers)
- if registers[REG_B] == 44354 and output == []:
- print("PASS: BXC instruction test")
- else:
- print(f"FAIL: Expected B=44354, Output=[], but got B={registers[REG_B]}, Output={output}")
- def test_combined_example():
- # Combined Test: Provided example for Registers and Program
- # Register A: 729, Register B: 0, Register C: 0
- # Program: 0,1,5,4,3,0
- print("\nRunning test: Combined Example")
- registers = {REG_A: 729, REG_B: 0, REG_C: 0}
- program = [0, 1, 5, 4, 3, 0] # ADV, OUT, and JNZ instructions
- output = execute_program(program, registers)
- if output == [4, 6, 3, 5, 6, 3, 5, 2, 1, 0]:
- print("PASS: Combined Example test")
- else:
- print(f"FAIL: Expected Output=[4, 6, 3, 5, 6, 3, 5, 2, 1, 0], but got Output={output}")
- if __name__ == "__main__":
- # test_instruction_bst()
- # test_instruction_out()
- # test_instruction_adv_and_out()
- # test_instruction_bxl()
- # test_instruction_bxc()
- # test_combined_example()
- input_file_path = os.path.join(current_file_path, "input.txt")
- input_data = read_lines(input_file_path)
- print(f"Part 1: {part1(input_data)}")
- # print(f"Part 2: {part2(input_data)}")
Advertisement
Add Comment
Please, Sign In to add comment