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