2019 day 13

This commit is contained in:
2025-08-04 13:48:20 +02:00
parent 05a62c5eb4
commit f76375d835
3 changed files with 138 additions and 20 deletions

View File

@@ -60,6 +60,6 @@ if __name__ == "__main__":
import fileinput import fileinput
with fileinput.input() as f: with fileinput.input() as f:
main(f, part=1) main(f, part=1)
with fileinput.input() as f: #with fileinput.input() as f:
#main(f, part=2) # FIXME unable to run both parts simultaneously #main(f, part=2) # FIXME unable to run both parts simultaneously

117
2019/day13/day13.py Normal file
View File

@@ -0,0 +1,117 @@
#!/usr/bin/env python3
# TODO replace PYTHONPATH hack with a proper solution, like making intcode an
# installed module https://stackoverflow.com/a/50194143
import sys
import time
from itertools import zip_longest
from pathlib import Path
from collections import defaultdict, Counter
from dataclasses import dataclass
sys.path.append(str(Path(__file__).absolute().parent.parent / "intcode"))
from intcode import interpret_intcode, Interpreter
@dataclass
class State:
grid: ...
score = 0
bounds = None
paddle_pos = None
ball_pos = None
stopped = False
def grouper(n, iterable):
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return zip_longest(*args)
def part1(program):
interpreter = interpret_intcode(program)
game = { (x, y): tile for x, y, tile in grouper(3, interpreter.stdout) }
print("Part 1: ", len([x for x in game.values() if x == 2]))
def parse_map(output):
grid = {}
score = None
for x, y, tile in grouper(3, output):
if (x, y) == (-1, 0):
score = tile
else:
grid[x,y] = tile
paddle = next((k for k, v in grid.items() if v == 3), None)
ball = next((k for k, v in grid.items() if v == 4), None)
bounds = (max(x for x, y in grid.keys()),
max(y for x, y in grid.keys()))
return grid, bounds, score, paddle, ball
def update_state(state: State, stdout):
grid, bounds, score, paddle, ball = parse_map(stdout)
if ball is None:
state.stopped = True
if state.bounds is None: # only set bounds the first time
state.bounds = bounds
if score is not None:
state.score = score
if paddle is not None:
state.paddle_pos = paddle
if ball is not None:
state.ball_pos = ball
if grid is None:
state.grid = grid
else:
# merge grid
for (x, y), v in grid.items():
state.grid[x,y] = v
return state
def part2(program):
program[0] = 2
interpreter = Interpreter(program)
state = State({})
while not interpreter.halted:
interpreter.interpret(break_on_output=False, break_on_input=True)
state = update_state(state, interpreter.stdout)
if state.stopped:
break
#print_map(state)
interpreter.stdout.clear()
paddle_x, ball_x = state.paddle_pos[0], state.ball_pos[0]
if paddle_x < ball_x: # move right
interpreter.stdin.append(1)
elif paddle_x > ball_x: # move left
interpreter.stdin.append(-1)
else:
interpreter.stdin.append(0)
print("Part 2: ", state.score)
def print_map(state):
clear = "\033[2J"
tiles = { 0: " ", 1: "#", 2: "~", 3: "_", 4: "O" }
max_x, max_y = state.bounds
print(clear)
for y in range(max_y + 1):
l = []
for x in range(max_x + 1):
tile = state.grid[x,y]
l.append(tiles[tile])
print("".join(l))
time.sleep(1/60)
def main(inp):
program = [int(x) for x in inp.readline().rstrip().split(",")]
part1(program)
part2(program)
if __name__ == "__main__":
import sys
infile = sys.argv[1] if len(sys.argv) > 1 else "example.txt"
with open(infile) as raw_input:
main(raw_input)

View File

@@ -165,25 +165,26 @@ class Interpreter:
def __repr__(self): def __repr__(self):
return f"Interpreter(ip={self.ip}, stdin={self.stdin}, stdout={self.stdout}, halted={self.halted})" return f"Interpreter(ip={self.ip}, stdin={self.stdin}, stdout={self.stdout}, halted={self.halted})"
def next_instruction(self): def interpret(self, break_on_input=False, break_on_output=True):
instruction = Instruction(self.memory, self.ip, self.base) while not self.halted:
if instruction.operation == Operation.INPUT: # fetch next instruction
instruction.input = self.stdin.pop(0) instruction = Instruction(self.memory, self.ip, self.base)
self.ip = instruction.handle() # pause if INP + break on input
self.base = instruction.base if instruction.operation == Operation.INPUT:
logging.debug(f"IP {self.ip}") if break_on_input and self.stdin == []:
if instruction.output is not None: break
self.stdout.append(instruction.output) else:
elif instruction.halted: instruction.input = self.stdin.pop(0)
self.halted = True # execute instruction
return instruction self.ip = instruction.handle()
self.base = instruction.base
def interpret(self, break_on_output=True): logging.debug(f"IP {self.ip}")
while instruction := self.next_instruction(): if instruction.output is not None:
if self.halted: self.stdout.append(instruction.output)
break if break_on_output:
if break_on_output and instruction.output is not None: break
break if instruction.halted:
self.halted = True
def interpret_intcode(program, stdin=[]): def interpret_intcode(program, stdin=[]):