Skip to content

Commit

Permalink
day 23: solve part 2 via brute force w/ optimized graph
Browse files Browse the repository at this point in the history
  • Loading branch information
golubitsky committed Aug 30, 2024
1 parent b1e913c commit 133ca1b
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 10 deletions.
85 changes: 75 additions & 10 deletions dsa/advent-of-code/2023/day_23.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sys
import string
from itertools import cycle
from collections import defaultdict

VECTORS = [(0, 1), (0, -1), (-1, 0), (1, 0)]
VECTOR_BY_CHAR = {
Expand Down Expand Up @@ -84,6 +84,54 @@ def is_path(y2, x2):
return result


def precompute_optimized_graph(grid, part):
graph = defaultdict(list) # pos => [(pos, distance)]

def find_junctions():
junctions = set()
for y, row in enumerate(grid):
for x, _ in enumerate(row):
if grid[y][x] == "#":
continue
pos = (y, x)
if len(neighbors(pos, grid, part)) > 2:
junctions.add(pos)
return junctions

def find_next_junction_or_end(pos, path, step_count=1):
if pos == junction:
return

path.add(pos)

if pos in junctions or pos == end_pos(grid):
return (pos, step_count)

n = [x for x in neighbors(pos, grid, part) if x not in path]

if len(n) == 0:
return
if len(n) != 1:
print("unexpected condition")
exit()
return find_next_junction_or_end(n[0], path, step_count + 1)

junctions = find_junctions()

# compute distances between all junctions, including end pos
for junction in junctions:
for neighbor in neighbors(junction, grid, part):
next_junction = find_next_junction_or_end(neighbor, {junction})
if next_junction is not None:
graph[junction].append(next_junction)

start = start_pos(grid)
next_junction = find_next_junction_or_end(neighbors(start, grid, part)[0], {start})
graph[start].append(next_junction)

return graph


def find_all_path_lengths(start_pos, end_pos, grid, part):
all_path_lengths = []

Expand All @@ -98,24 +146,41 @@ def search(pos, path):
new_path.add(adj_pos)
search(adj_pos, new_path)

path = set()
path.add(start_pos)
search(start_pos, path)
search(start_pos, path={start_pos})

return all_path_lengths


def find_all_path_lengths_optimized(start_pos, end_pos, graph):
all_path_lengths = []

def search(pos, path, distance=0):
if pos == end_pos:
all_path_lengths.append(distance)
return

for adj_pos, d in graph[pos]:
if adj_pos not in path:
new_path = path.copy()
new_path.add(adj_pos)
search(adj_pos, new_path, distance + d)

search(start_pos, path={start_pos})

return all_path_lengths


def solution(data, part):
grid = parsed(data)
graph = precompute_optimized_graph(grid, part)

all_path_lengths = find_all_path_lengths(start_pos(grid), end_pos(grid), grid, part)

start_node_count = 1
print(sorted(all_path_lengths)[-1] - start_node_count)
all_path_lengths = find_all_path_lengths_optimized(
start_pos(grid), end_pos(grid), graph
)
print(max(all_path_lengths))


if __name__ == "__main__":
sys.setrecursionlimit(10000)
with open("day_23_input.txt", "r") as file:
data = file.readlines()
solution(data, part=1)
solution(data, part=2)
24 changes: 24 additions & 0 deletions dsa/advent-of-code/2023/day_23_sample_with_axes.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
01234567890123456789012
0 #.#####################
1 #.......#########...###
2 #######.#########.#.###
3 ###.....#.>X>.###.#.###
4 ###v#####.#v#.###.#.###
5 ###X>...#.#.#.....#...#
6 ###v###.#.#.#########.#
7 ###...#.#.#.......#...#
8 #####.#.#.#######.#.###
9 #.....#.#.#.......#...#
0 #.#####.#.#.#########v#
1 #.#...#...#...###...>X#
2 #.#.#v#######v###.###v#
3 #...#.>.#...>X>.#.###.#
4 #####v#.#.###v#.#.###.#
5 #.....#...#...#.#.#...#
6 #.#########.###.#.#.###
7 #...###...#...#...#.###
8 ###.###.#.###v#####v###
9 #...#...#.#.>X>.#.>X###
0 #.###.###.#.###.#.#v###
1 #.....###...###...#...#
2 #####################.#

0 comments on commit 133ca1b

Please sign in to comment.