summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--14.py91
1 files changed, 91 insertions, 0 deletions
diff --git a/14.py b/14.py
new file mode 100644
index 0000000..6f49e37
--- /dev/null
+++ b/14.py
@@ -0,0 +1,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())