day 17
This commit is contained in:
parent
7d2df3d1f1
commit
d93c9cfed6
|
@ -0,0 +1,181 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
|
||||
import re
|
||||
from enum import Enum
|
||||
|
||||
|
||||
RE_LINE = re.compile(r'(y|x)=([0-9]+), (x|y)=([0-9]+)\.\.([0-9]+)')
|
||||
|
||||
|
||||
class Tile(Enum):
|
||||
WALL = 0
|
||||
FALL = 1
|
||||
SPREAD = 2
|
||||
STILL = 3
|
||||
|
||||
|
||||
class Dir(Enum): # Direction
|
||||
UP = (0, -1)
|
||||
DOWN = (0, 1)
|
||||
LEFT = (-1, 0)
|
||||
RIGHT = (1, 0)
|
||||
|
||||
|
||||
def addpts(a, b):
|
||||
return (a[0] + b[0], a[1] + b[1])
|
||||
|
||||
|
||||
def loadworld(fname):
|
||||
world = {}
|
||||
minX = 9999999999999
|
||||
minY = 9999999999999
|
||||
maxX = 0
|
||||
maxY = 0
|
||||
with open(fname) as f:
|
||||
for line in f.readlines():
|
||||
axis, coord, axis2, range1, range2 = RE_LINE.search(line).groups()
|
||||
coord = int(coord)
|
||||
range2 = int(range2)
|
||||
range1 = int(range1)
|
||||
if axis == "x":
|
||||
for y in range(range1, range2 + 1):
|
||||
world[(coord, y)] = Tile.WALL
|
||||
maxX = max(maxX, coord)
|
||||
minX = min(minX, coord)
|
||||
maxY = max(maxY, range2)
|
||||
minY = min(minY, range1)
|
||||
else:
|
||||
for x in range(range1, range2 + 1):
|
||||
world[(x, coord)] = Tile.WALL
|
||||
maxX = max(maxX, range2)
|
||||
minX = min(minX, range1)
|
||||
maxY = max(maxY, coord)
|
||||
minY = min(minY, coord)
|
||||
return world, minX, maxX, minY, maxY
|
||||
|
||||
|
||||
def printworld(world, minX, maxX, minY, maxY, fall, still):
|
||||
padding = 5
|
||||
for y in range(minY - padding, maxY + 1 + padding):
|
||||
for x in range(minX - padding, maxX + 1 + padding):
|
||||
coord = (x, y)
|
||||
if coord in fall:
|
||||
print("|", end="")
|
||||
elif coord in still:
|
||||
print("~", end="")
|
||||
elif coord in world:
|
||||
print("#", end="")
|
||||
else:
|
||||
print(".", end="")
|
||||
print()
|
||||
|
||||
|
||||
def main():
|
||||
world, minX, maxX, minY, maxY = loadworld("input.txt")
|
||||
minY = 0
|
||||
# world[(500, 0)] = Tile.WALL
|
||||
|
||||
# Tiles in the falling state
|
||||
fall = set([(500, 0)])
|
||||
# Tiles that have landed somewhere and can flow left/right
|
||||
still = set()
|
||||
|
||||
printworld(world, minX, maxX, minY, maxY, fall, still)
|
||||
|
||||
while True:
|
||||
# input()
|
||||
|
||||
"""
|
||||
Particle Rules
|
||||
- Any type above AIR spawns a "fall" below it
|
||||
- Any type above WALL turns into STILL
|
||||
- Any type next to an open space and above a wall spawns one next door
|
||||
"""
|
||||
updated = False
|
||||
|
||||
for coord in list(fall):
|
||||
below = addpts(coord, Dir.DOWN.value)
|
||||
if below[1] > maxY:
|
||||
continue
|
||||
if below in fall:
|
||||
continue
|
||||
elif below not in world:
|
||||
world[below] = Tile.FALL
|
||||
fall.update([below])
|
||||
updated = True
|
||||
# print("u1")
|
||||
elif world[below] in (Tile.WALL, Tile.STILL):
|
||||
# fall.remove(coord)
|
||||
# spread.update([coord])
|
||||
|
||||
# Search left and right, spreading the water (in fall state) as we go
|
||||
# If we find contiguous floor until we hit a wall on both sides, the water is changed to still
|
||||
# Otherwise, it stays flowing
|
||||
capped = True
|
||||
added = set([coord])
|
||||
|
||||
# search left
|
||||
sleft = addpts(coord, Dir.LEFT.value)
|
||||
while True:
|
||||
if sleft in world and world[sleft] == Tile.WALL:
|
||||
break # left wall found
|
||||
added.update([sleft])
|
||||
support = addpts(sleft, Dir.DOWN.value)
|
||||
if support not in world or world[support] not in (Tile.WALL, Tile.STILL):
|
||||
capped = False
|
||||
break
|
||||
sleft = addpts(sleft, Dir.LEFT.value)
|
||||
|
||||
# search right
|
||||
sright = addpts(coord, Dir.RIGHT.value)
|
||||
while True:
|
||||
if sright in world and world[sright] == Tile.WALL:
|
||||
break # left wall found
|
||||
added.update([sright])
|
||||
support = addpts(sright, Dir.DOWN.value)
|
||||
if support not in world or world[support] not in (Tile.WALL, Tile.STILL):
|
||||
capped = False
|
||||
break
|
||||
sright = addpts(sright, Dir.RIGHT.value)
|
||||
|
||||
# If the region is sealed off (e.g. no water can flow out, fill it wit still water)
|
||||
if capped:
|
||||
for c in added:
|
||||
if c not in world:
|
||||
updated = True
|
||||
world[c] = Tile.STILL
|
||||
try:
|
||||
fall.remove(c)
|
||||
except KeyError:
|
||||
pass
|
||||
still.update(added)
|
||||
else:
|
||||
for c in added:
|
||||
if c not in world:
|
||||
updated = True
|
||||
world[c] = Tile.FALL
|
||||
fall.update(added)
|
||||
|
||||
maxwater = 0
|
||||
for w in fall:
|
||||
maxwater = max(maxwater, w[1])
|
||||
print("MaxY:", maxY, "MaxWater:", maxwater, "Fallers:", len(fall))
|
||||
|
||||
if not updated:
|
||||
printworld(world, minX, maxX, minY, maxY, fall, still)
|
||||
print("Fall =", len(fall)) # This seems to include off-map fallers (from the spring), but i don't care.
|
||||
print("Still =", len(still))
|
||||
fall -= still
|
||||
still -= fall
|
||||
total = len(still)
|
||||
for point in fall:
|
||||
if point[1] >= minY and point[1] <= maxY:
|
||||
total += 1
|
||||
print(total)
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue