|
| 1 | + |
| 2 | + |
| 3 | +# This v1 works for part 1, not part 2 |
| 4 | +# Since it's also quite slow, I've done a v2 that should be better |
| 5 | + |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | + |
| 10 | + |
| 11 | +# -------------------------------- Input data -------------------------------- # |
| 12 | +import os, pathfinding, re |
| 13 | + |
| 14 | +test_data = {} |
| 15 | + |
| 16 | +test = 1 |
| 17 | +test_data[test] = {"input": """/->-\\ |
| 18 | +| | /----\\ |
| 19 | +| /-+--+-\ | |
| 20 | +| | | | v | |
| 21 | +\-+-/ \-+--/ |
| 22 | + \------/ """, |
| 23 | + "expected": ['Unknown', 'Unknown'], |
| 24 | + } |
| 25 | + |
| 26 | +test += 1 |
| 27 | +test_data[test] = {"input": r"""/>-<\ |
| 28 | +| | |
| 29 | +| /<+-\ |
| 30 | +| | | v |
| 31 | +\>+</ | |
| 32 | + | ^ |
| 33 | + \<->/""", |
| 34 | + "expected": ['Unknown', 'Unknown'], |
| 35 | + } |
| 36 | + |
| 37 | +test = 'real' |
| 38 | +input_file = os.path.join(os.path.dirname(__file__), 'Inputs', os.path.basename(__file__).replace('.py', '.txt')) |
| 39 | +test_data[test] = {"input": open(input_file, "r+").read(), |
| 40 | + "expected": ['124,130', '99, 96'], |
| 41 | + } |
| 42 | + |
| 43 | +# -------------------------------- Control program execution -------------------------------- # |
| 44 | + |
| 45 | +case_to_test = 'real' |
| 46 | +part_to_test = 2 |
| 47 | + |
| 48 | +# -------------------------------- Initialize some variables -------------------------------- # |
| 49 | + |
| 50 | +puzzle_input = test_data[case_to_test]['input'] |
| 51 | +puzzle_expected_result = test_data[case_to_test]['expected'][part_to_test-1] |
| 52 | +puzzle_actual_result = 'Unknown' |
| 53 | + |
| 54 | + |
| 55 | +# -------------------------------- Actual code execution -------------------------------- # |
| 56 | + |
| 57 | + |
| 58 | +def grid_to_vertices (self, grid, wall = '#'): |
| 59 | + self.vertices = [] |
| 60 | + track = {} |
| 61 | + y = 0 |
| 62 | + |
| 63 | + for line in grid.splitlines(): |
| 64 | + line = line.replace('^', '|').replace('v', '|').replace('>', '-').replace('<', '-') |
| 65 | + for x in range(len(line)): |
| 66 | + if line[x] != wall: |
| 67 | + self.vertices.append((x, y)) |
| 68 | + track[(x, y)] = line[x] |
| 69 | + |
| 70 | + y += 1 |
| 71 | + |
| 72 | + directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] |
| 73 | + right, left, down, up = directions |
| 74 | + |
| 75 | + for coords in self.vertices: |
| 76 | + for direction in directions: |
| 77 | + x, y = coords[0] + direction[0], coords[1] + direction[1] |
| 78 | + |
| 79 | + if track[coords] == '-' and direction in [up, down]: |
| 80 | + continue |
| 81 | + if track[coords] == '|' and direction in [left, right]: |
| 82 | + continue |
| 83 | + |
| 84 | + if (x, y) in self.vertices: |
| 85 | + if track[coords] in ('\\', '/'): |
| 86 | + if track[(x, y)] in ('\\', '/'): |
| 87 | + continue |
| 88 | + if track[(x, y)] == '-' and direction in [up, down]: |
| 89 | + continue |
| 90 | + elif track[(x, y)] == '|' and direction in [left, right]: |
| 91 | + continue |
| 92 | + if coords in self.edges: |
| 93 | + self.edges[(coords)].append((x, y)) |
| 94 | + else: |
| 95 | + self.edges[(coords)] = [(x, y)] |
| 96 | + |
| 97 | + return True |
| 98 | + |
| 99 | +pathfinding.Graph.grid_to_vertices = grid_to_vertices |
| 100 | + |
| 101 | +def turn_left (direction): |
| 102 | + return (direction[1], -direction[0]) |
| 103 | + |
| 104 | +def turn_right (direction): |
| 105 | + return (-direction[1], direction[0]) |
| 106 | + |
| 107 | + |
| 108 | + |
| 109 | + |
| 110 | + |
| 111 | +# Analyze grid |
| 112 | +grid = puzzle_input |
| 113 | +graph = pathfinding.Graph() |
| 114 | +graph.grid_to_vertices(puzzle_input, ' ') |
| 115 | + |
| 116 | +intersections = graph.grid_search(grid, '+')['+'] |
| 117 | + |
| 118 | +directions = {'^': (0, -1), '>': (1, 0), '<': (-1, 0), 'v': (0, 1)} |
| 119 | +dirs = {(0, -1): '^', (1, 0): '>', (-1, 0): '<', (0, 1): 'v'} |
| 120 | + |
| 121 | + |
| 122 | +# Find carts |
| 123 | +list_carts = graph.grid_search(grid, ('^', '<', '>', 'v')) |
| 124 | +carts = [] |
| 125 | +cart_positions = [] |
| 126 | +for direction in list_carts: |
| 127 | + dir = directions[direction] |
| 128 | + for cart in list_carts[direction]: |
| 129 | + carts.append((cart, dir, 0)) |
| 130 | + cart_positions.append(list_carts[direction]) |
| 131 | +del list_carts |
| 132 | +carts = sorted(carts, key=lambda x: (x[0][1], x[0][0])) |
| 133 | + |
| 134 | +# Run them! |
| 135 | +subtick = 0 |
| 136 | +tick = 0 |
| 137 | + |
| 138 | +nb_carts = len(carts) |
| 139 | +collision = 0 |
| 140 | +while True: |
| 141 | + cart = carts.pop(0) |
| 142 | + cart_positions.pop(0) |
| 143 | + pos, dir, choice = cart |
| 144 | + new_pos = (pos[0] + dir[0], pos[1] + dir[1]) |
| 145 | + |
| 146 | + print (pos, choice, dirs[dir]) |
| 147 | + |
| 148 | + |
| 149 | + # We need to turn |
| 150 | + if new_pos not in graph.edges[pos]: |
| 151 | + options = [((pos[0] + x[0], pos[1] + x[1]), x) |
| 152 | + for x in directions.values() |
| 153 | + if x != (-dir[0], -dir[1]) |
| 154 | + and (pos[0] + x[0], pos[1] + x[1]) in graph.edges[pos]] |
| 155 | + new_pos, dir = options[0] |
| 156 | + |
| 157 | + # Intersection |
| 158 | + if new_pos in intersections: |
| 159 | + if choice % 3 == 0: |
| 160 | + dir = turn_left(dir) |
| 161 | + elif choice % 3 == 2: |
| 162 | + dir = turn_right(dir) |
| 163 | + choice += 1 |
| 164 | + choice %= 3 |
| 165 | + |
| 166 | + new_cart = (new_pos, dir, choice) |
| 167 | + |
| 168 | + |
| 169 | + |
| 170 | + |
| 171 | + |
| 172 | + # Check collisions |
| 173 | + if new_cart[0] in cart_positions: |
| 174 | + if part_to_test == 1: |
| 175 | + puzzle_actual_result = new_cart[0] |
| 176 | + break |
| 177 | + else: |
| 178 | + print ('collision', new_cart[0]) |
| 179 | + collision += 1 |
| 180 | + carts = [c for c in carts if c[0] != new_cart[0]] |
| 181 | + cart_positions = [c[0] for c in carts] |
| 182 | + else: |
| 183 | + carts.append(new_cart) |
| 184 | + cart_positions.append(new_cart[0]) |
| 185 | + |
| 186 | + |
| 187 | + # Count ticks + sort carts |
| 188 | + subtick += 1 |
| 189 | + if subtick == nb_carts - collision: |
| 190 | + tick += 1 |
| 191 | + subtick = 0 |
| 192 | + collision = 0 |
| 193 | + nb_carts = len(carts) |
| 194 | + carts = sorted(carts, key=lambda x: (x[0][1], x[0][0])) |
| 195 | + cart_positions = [c[0] for c in carts] |
| 196 | + |
| 197 | + print ('End of tick', tick, ' - Remaining', len(carts)) |
| 198 | + if len(carts) == 1: |
| 199 | + break |
| 200 | + |
| 201 | +if part_to_test == 2: |
| 202 | + puzzle_actual_result = carts |
| 203 | +#99, 96 |
| 204 | +# -------------------------------- Outputs / results -------------------------------- # |
| 205 | + |
| 206 | +print ('Expected result : ' + str(puzzle_expected_result)) |
| 207 | +print ('Actual result : ' + str(puzzle_actual_result)) |
| 208 | + |
| 209 | + |
| 210 | + |
| 211 | + |
0 commit comments