My Advent of Code solutions in Python. kevinyap.ca/2019/12/going-fast-in-advent-of-code/
advent-of-code python
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add solution for 2023/21

+104
+104
2023/day21.py
··· 1 + import fileinput 2 + from collections import defaultdict, deque, Counter 3 + 4 + from utils import Point 5 + 6 + 7 + PART_1_STEPS = 64 8 + PART_2_STEPS = 26501365 9 + 10 + 11 + def search(start, max_dist): 12 + horizon = deque([(start, 0)]) 13 + cost_so_far = {} 14 + 15 + while horizon: 16 + curr, dist = horizon.pop() 17 + 18 + if dist > max_dist: 19 + continue 20 + 21 + if curr in cost_so_far: 22 + continue 23 + 24 + cost_so_far[curr] = dist 25 + 26 + for n in curr.neighbours_4(): 27 + if GRAPH.get(n) != '.': 28 + continue 29 + 30 + horizon.appendleft((n, dist + 1)) 31 + 32 + return sum(1 for c, v in cost_so_far.items() if v % 2 == ((max_dist % 2))) 33 + 34 + 35 + # Read problem input. 36 + GRAPH = {} 37 + START = None 38 + for y, line in enumerate(fileinput.input()): 39 + for x, c in enumerate(line.strip()): 40 + p = Point(x, y) 41 + if c == 'S': 42 + START = p 43 + GRAPH[p] = '.' 44 + else: 45 + GRAPH[p] = c 46 + 47 + # Solve part 1. 48 + print("Part 1:", search(START, 64)) 49 + 50 + # Solve part 2. 51 + HEIGHT = y + 1 52 + WIDTH = x + 1 53 + assert HEIGHT == WIDTH 54 + 55 + assert START.x == START.y == (HEIGHT // 2) == (WIDTH // 2) 56 + RADIUS = START.x 57 + assert (PART_2_STEPS % WIDTH) == RADIUS 58 + 59 + # Define the key search-start points for blocks within the infinite diamond. 60 + EDGE_STARTS = [Point(x, y) for x, y in [(RADIUS, 0), (WIDTH - 1, RADIUS), (0, RADIUS), (RADIUS, HEIGHT - 1)]] 61 + CORNER_STARTS = [Point(x, y) for x, y in [(0, 0), (0, HEIGHT - 1), (WIDTH - 1, 0), (WIDTH - 1, HEIGHT - 1)]] 62 + 63 + # Start computing the final answer to part 2. We leverage the following properties of the problem input: 64 + # - There is a straight path from the central starting point to the outer edges of the block. 65 + # - The step length is equal to the block radius mod block length, meaning there is perfect 66 + # symmetry even on the outside edges of the diamond that gets formed. 67 + 68 + # First, add in the centre piece of the diamond. 69 + part_2 = search(START, PART_2_STEPS) 70 + 71 + # Compute how many times we need to add "full edge pieces" in with parity. 72 + num_edge_pieces = Counter() 73 + for i, x in enumerate(range(WIDTH, PART_2_STEPS - RADIUS, WIDTH), start=1): 74 + num_edge_pieces[i % 2] += 1 75 + 76 + edge_addition = 0 77 + for s in EDGE_STARTS: 78 + # Add "fully searched" edge blocks. 79 + edge_addition += num_edge_pieces[PART_2_STEPS % 2] * search(s, PART_2_STEPS) 80 + edge_addition += num_edge_pieces[(PART_2_STEPS + 1) % 2] * search(s, PART_2_STEPS + 1) 81 + 82 + # Accomodate the outer tips of the infinite diamond. 83 + edge_addition += search(s, WIDTH - 1) 84 + 85 + part_2 += edge_addition 86 + 87 + # Compute how many "corner pieces" are scattered on the edge of the diamond with parity. 88 + num_corner_pieces = Counter() # track parity 89 + for i, _ in enumerate(range(WIDTH, PART_2_STEPS - WIDTH * 1, WIDTH), start=0): 90 + num_corner_pieces[i % 2] += i 91 + 92 + corner_addition = 0 93 + for s in CORNER_STARTS: 94 + # Add "fully searched" corner blocks. 95 + corner_addition += search(s, PART_2_STEPS) * num_corner_pieces[1] 96 + corner_addition += search(s, PART_2_STEPS + 1) * num_corner_pieces[0] 97 + 98 + # Accomodate the outer edges of the infinite diamond. 99 + corner_addition += search(s, WIDTH + RADIUS - 1) * ((PART_2_STEPS // WIDTH) - 1) 100 + corner_addition += search(s, RADIUS - 1) * ((PART_2_STEPS // WIDTH)) 101 + 102 + part_2 += corner_addition 103 + 104 + print("Part 2:", part_2)