-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathq13.py
130 lines (108 loc) · 3.12 KB
/
q13.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from dataclasses import dataclass, field
from enum import Enum
from intcode import IntcodeSim
from typing import *
import util
class Tile(Enum):
empty = 0
wall = 1
block = 2
bat = 3
ball = 4
class Screen():
chars = {
Tile.empty: ' ',
Tile.wall: '%',
Tile.block: 'E',
Tile.bat: '=',
Tile.ball: '*',
}
canvas: List[List[Tile]] = []
width: int
height: int
def __init__(self, width: int = 40, height: int =25):
self.canvas = []
self.width = width
self.height = height
for y in range(height):
row = [Tile.empty] * width
self.canvas.append(row)
def set(self, x: int, y: int, tile: Tile):
self.canvas[y][x] = tile
def get(self, x: int, y: int) -> Tile:
return self.canvas[y][x]
def render(self) -> str:
rows = []
for y in range(self.height):
row = ""
for x in range(self.width):
row += self.chars[self.get(x,y)]
rows.append(row)
return "\n".join(rows)
def countTiles(self, tile: Tile) -> int:
count = 0
for y in range(self.height):
count += self.canvas[y].count(tile)
return count
def part1():
screen = Screen()
state = []
def handleOutput(output: int):
nonlocal state
if len(state) < 3:
state.append(output)
if len(state) == 3:
x, y, tileNo = state
state = []
screen.set(x, y, Tile(tileNo))
i = IntcodeSim.fromFile("inputs/q13")
i.outputFn = handleOutput
i.run()
renderedScreen = screen.render()
print(renderedScreen)
blockTiles = screen.countTiles(Tile.block)
print(blockTiles)
# 286
def part2():
screen = Screen()
@dataclass
class State():
args: List[int] = field(default_factory=list)
score: int = 0
step: int = 0
ballX: Optional[int] = None
batX: Optional[int] = None
state = State()
def handleOutput(output: int) -> None:
if len(state.args) < 2:
# Record x,y co-ordinates for next call
state.args.append(output)
return
x, y = state.args
state.args.clear()
if x == -1 and y == 0:
state.score = output
else:
tile = Tile(output)
screen.set(x, y, tile)
# Keep track of bat/ball position
if tile == Tile.bat:
state.batX = x
elif tile == Tile.ball:
state.ballX = x
state.step += 1
# Wait for the machine to output the whole screen once
# before we start rendering
if state.step > 900:
print(screen.render())
print("Score: " + str(state.score))
def handleInput() -> int:
return util.sign(state.ballX - state.batX)
i = IntcodeSim.fromFile("inputs/q13")
# Set machine to 'play for free' mode
i.setMemory(0, 2)
i.inputFn = handleInput
i.outputFn = handleOutput
i.run()
print(f"finished with score: {state.score}")
part2()