solution.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. #!/usr/bin/python3
  2. import sys
  3. directions = {
  4. 'north': (-1, 0),
  5. 'east': (0, 1),
  6. 'south': (1, 0),
  7. 'west': (0, -1),
  8. }
  9. pipes = {
  10. '.': [],
  11. '|': [directions['north'], directions['south']],
  12. '-': [directions['east'], directions['west']],
  13. 'L': [directions['north'], directions['east']],
  14. 'J': [directions['north'], directions['west']],
  15. '7': [directions['south'], directions['west']],
  16. 'F': [directions['south'], directions['east']],
  17. 'S': [directions['north'], directions['east'], directions['south'], directions['west']],
  18. }
  19. def print_grid(grid, format_int=False):
  20. for line in grid:
  21. if format_int:
  22. print(' '.join(['{:2d}'.format(n) for n in line]))
  23. else:
  24. print(line)
  25. rows = len(grid)
  26. cols = len(grid[0])
  27. print(f'{cols}x{rows} grid ({cols - 2}x{rows - 2} without pad)')
  28. def construct_grid(f):
  29. grid = []
  30. for line in map(str.strip, f):
  31. grid.append(f'.{line}.')
  32. empty_line = '.' * len(grid[0])
  33. grid.insert(0, empty_line)
  34. grid.append(empty_line)
  35. return grid
  36. def find_start(grid):
  37. for row in range(len(grid)):
  38. for col in range(len(grid[row])):
  39. if grid[row][col] == 'S':
  40. return (row, col)
  41. return (-1, -1)
  42. def connected(grid, start, end):
  43. start_row = start[0]
  44. start_col = start[1]
  45. end_row = end[0]
  46. end_col = end[1]
  47. start_char = grid[start_row][start_col]
  48. end_char = grid[end_row][end_col]
  49. for start_pipe_off_r, start_pipe_off_c in pipes[start_char]:
  50. if end_row == start_row + start_pipe_off_r and end_col == start_col + start_pipe_off_c:
  51. for end_pipe_off_r, end_pipe_off_c in pipes[end_char]:
  52. if start_row == end_row + end_pipe_off_r and start_col == end_col + end_pipe_off_c:
  53. return True
  54. return False
  55. def get_valid_neighbors(grid, nodes):
  56. valid = set()
  57. for row, col in nodes:
  58. for off_r in range(-1, 2):
  59. for off_c in range(-1, 2):
  60. if abs(off_r) + abs(off_c) != 1:
  61. continue
  62. #if connected(grid[row][col], grid[row + off_r][col + off_c]):
  63. if connected(grid, (row, col), (row + off_r, col + off_c)):
  64. valid.add((row + off_r, col + off_c))
  65. return valid
  66. def get_connected_outside(distances, outsides, bounds):
  67. connected = set()
  68. min_row, max_row, min_col, max_col = bounds
  69. for row, col in outsides:
  70. for off_r in range(-1, 2):
  71. for off_c in range(-1, 2):
  72. row_t = row + off_r
  73. col_t = col + off_c
  74. if row_t < min_row or row_t >= max_row:
  75. continue
  76. if col_t < min_col or col_t >= max_col:
  77. continue
  78. if distances[row_t][col_t] > 0:
  79. continue
  80. connected.add((row_t, col_t))
  81. return connected
  82. def get_connected_inside(distances, outside):
  83. inside = [[0 for col in row] for row in distances]
  84. for row in range(len(inside)):
  85. for col in range(len(inside[row])):
  86. if distances[row][col] == 0 and outside[row][col] == 0:
  87. inside[row][col] = 1
  88. return inside
  89. #def get_connected_inside(grid):
  90. #inside = [[0 for col in row] for row in grid]
  91. #north_south_rays = [[0 for col in row] for row in grid]
  92. #east_west_rays = [[0 for col in row] for row in grid]
  93. #south_north_rays = [[0 for col in row] for row in grid]
  94. #west_east_rays = [[0 for col in row] for row in grid]
  95. #for row in range(1, len(inside)):
  96. #for col in range(len(inside[row])):
  97. #if grid[row - 1][col] != '.':
  98. #north_south_rays[row][col] = north_south_rays[row - 1][col] + 1
  99. #else:
  100. #north_south_rays[row][col] = north_south_rays[row - 1][col]
  101. #for row in range(len(inside)):
  102. #for col in range(1, len(inside[row])):
  103. #if grid[row][col - 1] != '.':
  104. #west_east_rays[row][col] = west_east_rays[row][col - 1] + 1
  105. #else:
  106. #west_east_rays[row][col] = west_east_rays[row][col - 1]
  107. #for row in range(len(inside) - 1):
  108. #for col in range(len(inside[row])):
  109. #if grid[row + 1][col] != '.':
  110. #south_north_rays[row][col] = south_north_rays[row + 1][col] + 1
  111. #else:
  112. #south_north_rays[row][col] = south_north_rays[row + 1][col]
  113. #for row in range(len(inside)):
  114. #for col in range(1, len(inside[row])):
  115. #if grid[row][col - 1] != '.':
  116. #west_east_rays[row][col] = west_east_rays[row][col - 1] + 1
  117. #else:
  118. #west_east_rays[row][col] = west_east_rays[row][col - 1]
  119. #rays = [[0 for col in row] for row in grid]
  120. #for row in range(len(inside)):
  121. #for col in range(len(inside[row])):
  122. #rays[row][col] = west_east_rays[row][col] + north_south_rays[row][col]
  123. #print_grid(west_east_rays)
  124. #print_grid(north_south_rays)
  125. #print_grid(rays)
  126. #return inside
  127. def part1():
  128. print('part 1')
  129. grid = construct_grid(sys.stdin)
  130. print_grid(grid)
  131. start = find_start(grid)
  132. print(f'start is {start}')
  133. distances = [[0 for col in row] for row in grid]
  134. distances[start[0]][start[1]] = 1
  135. print_grid(distances)
  136. processed = set()
  137. nodes = set([start])
  138. while len(nodes) > 0:
  139. old = nodes.copy()
  140. for row, col in old:
  141. neighbors = get_valid_neighbors(grid, old)
  142. for row_n, col_n in neighbors:
  143. if (row_n, col_n) not in processed:
  144. nodes.add((row_n, col_n))
  145. if distances[row_n][col_n] != 0:
  146. distances[row_n][col_n] = min(distances[row_n][col_n], distances[row][col] + 1)
  147. else:
  148. distances[row_n][col_n] = distances[row][col] + 1
  149. processed.add((row, col))
  150. nodes.remove((row, col))
  151. print_grid(distances)
  152. maximum = 0
  153. for row in distances:
  154. for col in row:
  155. maximum = max(maximum, col)
  156. print(f'max is {maximum - 1}')
  157. def part2():
  158. print('part 2')
  159. grid = construct_grid(sys.stdin)
  160. print_grid(grid)
  161. start = find_start(grid)
  162. print(f'start is {start}')
  163. distances = [[0 for col in row] for row in grid]
  164. distances[start[0]][start[1]] = 1
  165. print_grid(distances)
  166. processed = set()
  167. nodes = set([start])
  168. while len(nodes) > 0:
  169. old = nodes.copy()
  170. for row, col in old:
  171. neighbors = get_valid_neighbors(grid, old)
  172. for row_n, col_n in neighbors:
  173. if (row_n, col_n) not in processed:
  174. nodes.add((row_n, col_n))
  175. if distances[row_n][col_n] != 0:
  176. distances[row_n][col_n] = min(distances[row_n][col_n], distances[row][col] + 1)
  177. else:
  178. distances[row_n][col_n] = distances[row][col] + 1
  179. processed.add((row, col))
  180. nodes.remove((row, col))
  181. print_grid(distances, format_int=True)
  182. maximum = 0
  183. for row in distances:
  184. for col in row:
  185. maximum = max(maximum, col)
  186. print(f'max is {maximum - 1}')
  187. outside_grid = [[0 for col in row] for row in distances]
  188. processed = set()
  189. outsides = set([(1, 1)])
  190. bounds = (0, len(distances), 0, len(distances[0]))
  191. while len(outsides) > 0:
  192. old = outsides.copy()
  193. for row, col in old:
  194. neighbors = get_connected_outside(distances, outsides, bounds)
  195. #print(old, neighbors)
  196. for row_n, col_n in neighbors:
  197. if (row_n, col_n) not in processed:
  198. outside_grid[row_n][col_n] = 1
  199. outsides.add((row_n, col_n))
  200. processed.add((row, col))
  201. outsides.remove((row, col))
  202. #print_grid(outside_grid, format_int=True)
  203. inside_grid = get_connected_inside(distances, outside_grid)
  204. #print_grid(inside_grid, format_int=True)
  205. total = 0
  206. for row in inside_grid:
  207. total += sum(row)
  208. print(f'total of {total} inside tiles')
  209. if sys.argv[1] in '1':
  210. part1()
  211. else:
  212. part2()