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
|
from utils import open_day, sliding_window
from enum import Enum, auto
from itertools import count
def point_from_str(s):
return tuple(int(d) for d in s.split(','))
class Tile(Enum):
WALL = auto()
SAND = auto()
class Sandbox:
def __init__(self, source=(500, 0)):
self.tiles = dict()
self.source = source
self.minx, self.maxx = source[0], source[0]
self.miny, self.maxy = source[1], source[1]
def __setitem__(self, key, value):
if key[0] < self.minx: self.minx = key[0]
elif key[0] > self.maxx: self.maxx = key[0]
if key[1] < self.miny: self.miny = key[1]
elif key[1] > self.maxy: self.maxy = key[1]
self.tiles[key] = value
def __getitem__(self, key):
return self.tiles[key]
def __str__(self):
lines = []
for y in range(self.miny, self.maxy + 1):
line = []
for x in range(self.minx, self.maxx + 1):
p = x, y
match self.tiles.get(p):
case Tile.WALL: line.append('#')
case Tile.SAND: line.append('o')
case None: line.append('.')
if x == self.source[0] and y == self.source[1]:
line[-1] = '+'
lines.append(''.join(line))
return '\n'.join(lines)
def simulate(self):
for units in count():
sandpos = self.source
while self.minx <= sandpos[0] <= self.maxx and \
self.miny <= sandpos[1] <= self.maxy and \
self.source not in self.tiles:
for dx in (0, -1, 1):
npos = sandpos[0] + dx, sandpos[1] + 1
if npos not in self.tiles:
sandpos = (sandpos[0] + dx, sandpos[1] + 1)
break
else:
self.tiles[sandpos] = Tile.SAND
break
else:
break
return units
sandbox = Sandbox()
with open_day(14) as f:
for line in f:
for b, e in sliding_window(map(point_from_str, line.rstrip().split(' -> ')), 2):
minlx, maxlx = min(b[0], e[0]), max(b[0], e[0])
minly, maxly = min(b[1], e[1]), max(b[1], e[1])
for x in range(minlx, maxlx + 1):
for y in range(minly, maxly + 1):
sandbox[x, y] = Tile.WALL
p1 = sandbox.simulate()
print(p1)
floory = sandbox.maxy + 2
height = floory - sandbox.source[1]
for x in range(sandbox.source[0] - height, sandbox.source[0] + height + 1):
sandbox[x, floory] = Tile.WALL
print(p1 + sandbox.simulate())
|