|
| 1 | +# -------------------------------- Input data ---------------------------------------- # |
| 2 | +import os, pathfinding |
| 3 | + |
| 4 | +test_data = {} |
| 5 | + |
| 6 | +test = 1 |
| 7 | +test_data[test] = { |
| 8 | + "input": """depth: 510 |
| 9 | +target: 10,10""", |
| 10 | + "expected": ["114", "45"], |
| 11 | +} |
| 12 | + |
| 13 | +test = "real" |
| 14 | +input_file = os.path.join( |
| 15 | + os.path.dirname(__file__), |
| 16 | + "Inputs", |
| 17 | + os.path.basename(__file__).replace(".py", ".txt"), |
| 18 | +) |
| 19 | +test_data[test] = { |
| 20 | + "input": open(input_file, "r+").read().strip(), |
| 21 | + "expected": ["6256", "Unknown"], |
| 22 | +} |
| 23 | + |
| 24 | +# -------------------------------- Control program execution ------------------------- # |
| 25 | + |
| 26 | +case_to_test = "real" |
| 27 | +part_to_test = 2 |
| 28 | + |
| 29 | +# -------------------------------- Initialize some variables ------------------------- # |
| 30 | + |
| 31 | +puzzle_input = test_data[case_to_test]["input"] |
| 32 | +puzzle_expected_result = test_data[case_to_test]["expected"][part_to_test - 1] |
| 33 | +puzzle_actual_result = "Unknown" |
| 34 | + |
| 35 | + |
| 36 | +# -------------------------------- Actual code execution ----------------------------- # |
| 37 | + |
| 38 | +_, depth = puzzle_input.splitlines()[0].split(" ") |
| 39 | +_, target = puzzle_input.splitlines()[1].split(" ") |
| 40 | + |
| 41 | +depth = int(depth) |
| 42 | +max_x, max_y = map(int, target.split(",")) |
| 43 | +target = max_x - 1j * max_y |
| 44 | + |
| 45 | +geological = {0: 0} |
| 46 | +erosion = {0: 0} |
| 47 | +for x in range(max_x + 1): |
| 48 | + geological[x] = x * 16807 |
| 49 | + erosion[x] = (geological[x] + depth) % 20183 |
| 50 | +for y in range(max_y + 1): |
| 51 | + geological[-1j * y] = y * 48271 |
| 52 | + erosion[-1j * y] = (geological[-1j * y] + depth) % 20183 |
| 53 | + |
| 54 | +for x in range(1, max_x + 1): |
| 55 | + for y in range(1, max_y + 1): |
| 56 | + geological[x - 1j * y] = ( |
| 57 | + erosion[x - 1 - 1j * y] * erosion[x - 1j * (y - 1)] |
| 58 | + ) % 20183 |
| 59 | + erosion[x - 1j * y] = (geological[x - 1j * y] + depth) % 20183 |
| 60 | + |
| 61 | +geological[target] = 0 |
| 62 | +erosion[target] = 0 |
| 63 | + |
| 64 | +terrain = {x: erosion[x] % 3 for x in erosion} |
| 65 | + |
| 66 | +if part_to_test == 1: |
| 67 | + puzzle_actual_result = sum(terrain.values()) |
| 68 | + |
| 69 | +else: |
| 70 | + neither, climbing, torch = 0, 1, 2 |
| 71 | + rocky, wet, narrow = 0, 1, 2 |
| 72 | + |
| 73 | + # Override the neighbors function |
| 74 | + def neighbors(self, vertex): |
| 75 | + north = (0, 1) |
| 76 | + south = (0, -1) |
| 77 | + west = (-1, 0) |
| 78 | + east = (1, 0) |
| 79 | + directions_straight = [north, south, west, east] |
| 80 | + |
| 81 | + neighbors = {} |
| 82 | + for dir in directions_straight: |
| 83 | + target = (vertex[0] + dir[0], vertex[1] + dir[1], vertex[2]) |
| 84 | + if target in self.vertices: |
| 85 | + neighbors[target] = 1 |
| 86 | + for tool in (neither, climbing, torch): |
| 87 | + target = (vertex[0], vertex[1], tool) |
| 88 | + if target in self.vertices and tool != vertex[1]: |
| 89 | + neighbors[target] = 7 |
| 90 | + |
| 91 | + return neighbors |
| 92 | + |
| 93 | + # Add some coordinates around the target |
| 94 | + padding = 10 if case_to_test == 1 else 50 |
| 95 | + for x in range(max_x, max_x + padding): |
| 96 | + geological[x] = x * 16807 |
| 97 | + erosion[x] = (geological[x] + depth) % 20183 |
| 98 | + for y in range(max_y, max_y + padding): |
| 99 | + geological[-1j * y] = y * 48271 |
| 100 | + erosion[-1j * y] = (geological[-1j * y] + depth) % 20183 |
| 101 | + for x in range(1, max_x + padding): |
| 102 | + for y in range(1, max_y + padding): |
| 103 | + if x - 1j * y in geological: |
| 104 | + continue |
| 105 | + geological[x - 1j * y] = ( |
| 106 | + erosion[x - 1 - 1j * y] * erosion[x - 1j * (y - 1)] |
| 107 | + ) % 20183 |
| 108 | + erosion[x - 1j * y] = (geological[x - 1j * y] + depth) % 20183 |
| 109 | + |
| 110 | + terrain = {x: erosion[x] % 3 for x in erosion} |
| 111 | + del erosion |
| 112 | + del geological |
| 113 | + |
| 114 | + # Then run pathfinding algo |
| 115 | + pathfinding.WeightedGraph.neighbors = neighbors |
| 116 | + vertices = [ |
| 117 | + (x.real, x.imag, neither) for x in terrain if terrain[x] in (wet, narrow) |
| 118 | + ] |
| 119 | + vertices += [ |
| 120 | + (x.real, x.imag, climbing) for x in terrain if terrain[x] in (rocky, wet) |
| 121 | + ] |
| 122 | + vertices += [ |
| 123 | + (x.real, x.imag, torch) for x in terrain if terrain[x] in (rocky, narrow) |
| 124 | + ] |
| 125 | + graph = pathfinding.WeightedGraph(vertices) |
| 126 | + |
| 127 | + graph.dijkstra((0, 0, torch), (max_x, -max_y, torch)) |
| 128 | + |
| 129 | + puzzle_actual_result = graph.distance_from_start[(max_x, -max_y, torch)] |
| 130 | + |
| 131 | +# 979 is too high |
| 132 | + |
| 133 | + |
| 134 | +# -------------------------------- Outputs / results --------------------------------- # |
| 135 | + |
| 136 | +print("Expected result : " + str(puzzle_expected_result)) |
| 137 | +print("Actual result : " + str(puzzle_actual_result)) |
0 commit comments