17_reservoir.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. #!/usr/bin/env python3
  2. import sys
  3. SAND, CLAY, DROP, STILL = 0, 1, 2, 4
  4. WALL = CLAY | STILL
  5. WATER = DROP | STILL
  6. def parse():
  7. ranges = []
  8. for line in sys.stdin:
  9. x, y = line.rstrip().split(', ')
  10. if x[0] == 'y':
  11. x, y = y, x
  12. x = tuple(map(int, x[2:].split('..'))) if '..' in x else (int(x[2:]),) * 2
  13. y = tuple(map(int, y[2:].split('..'))) if '..' in y else (int(y[2:]),) * 2
  14. ranges.append(x + y)
  15. padding = 1
  16. xmin = min(min(min(r[:2]) for r in ranges), 500) - padding
  17. ymin = min(min(r[2:]) for r in ranges)
  18. ranges = [(xstart - xmin, xend - xmin, ystart - ymin, yend - ymin)
  19. for xstart, xend, ystart, yend in ranges]
  20. w = max(max(r[:2]) for r in ranges) + 1 + 2 * padding
  21. h = max(max(r[2:]) for r in ranges) + 1
  22. grid = w * h * [SAND]
  23. for xstart, xend, ystart, yend in ranges:
  24. for y in range(ystart, yend + 1):
  25. for x in range(xstart, xend + 1):
  26. grid[y * w + x] = CLAY
  27. grid[500 - xmin] = DROP
  28. return grid, w
  29. def show(grid, w):
  30. source = grid.index(DROP)
  31. print('.' * source + '+' + '.' * (w - source - 1))
  32. for y in range(len(grid) // w):
  33. print(''.join('.#| ~'[x] for x in grid[y * w:(y + 1) * w]))
  34. def flow(grid, w):
  35. h = len(grid) // w
  36. def expand(water, step):
  37. water += step
  38. while grid[water + w] & WALL and not grid[water] & WALL:
  39. water += step
  40. return water
  41. def drop(water):
  42. while water // w < h - 1:
  43. water += w
  44. if grid[water] == DROP:
  45. break
  46. elif grid[water] & WALL:
  47. # floor hit, rise while trapped between walls of clay
  48. lwall = rwall = True
  49. while lwall and rwall:
  50. water -= w
  51. left = expand(water, -1)
  52. right = expand(water, 1)
  53. lwall = grid[left] & WALL
  54. rwall = grid[right] & WALL
  55. fill = STILL if lwall and rwall else DROP
  56. for i in range(left + 1, right):
  57. grid[i] = fill
  58. # break condition above checks fow clay and water, but we only
  59. # keep flowing through sand
  60. lflow = grid[left] == SAND
  61. rflow = grid[right] == SAND
  62. if lflow and rflow:
  63. # flow continues on both ends, fork
  64. grid[left] = DROP
  65. drop(left)
  66. water = right
  67. elif lflow:
  68. water = left
  69. elif rflow:
  70. water = right
  71. else:
  72. break
  73. # drop down
  74. grid[water] = DROP
  75. drop(grid.index(DROP))
  76. grid, w = parse()
  77. flow(grid, w)
  78. #show(grid, w)
  79. print(sum(1 for x in grid if x & WATER))
  80. print(grid.count(STILL))