summaryrefslogtreecommitdiff
path: root/2024/aoc2024-d16-wash_attempt.py
blob: 4d70b4838536366d4ae33727bcc42b2ee182cced (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#advent of code 2024
#day 16
#version 2 - slightly cleaned up
#don't know anymore, can't explain - keeping the unrevised version because it's hilariously dogshit
#part 1: simple shortest path search
#part 2: keep the path searching function running until all paths with distance equal to the shortest path are registered
#keep track of distance for each node, kinda like djikstra, distance to each node is stored in a dictionary
#with only exception that it stores all of the possible 
#in short: linked list, in format: node_1:set_of_previous_nodes, previous nodes have shortest possible path to that node
#keep a map of previous steps, that is Position_now:set of positions that could be used to reach 
#I also commented out a check to prevent going backwards (take step north, then take step south) 
#but I guess that was taken care of during other checks like how big is the distance so it didnt matter
#the entries in queues with distance greater than registered shortest path are discarded
#the check is repeated (probably unnecessarily) before queuepush
#my other issue: there could be more than 1 directions from which I could reach the E
#while I registered all of them, durign testing I used only one of them
#turns out there weren't multiple ends (all valid paths reach E from the same direction)
#sidenote: i think i solved that earlier, because I was getting similar values, but I didn't bother to check

#assumptions: 
#only 1 starting position
#i think the code will work for multiple ends (E) as well, as long as the puzzle wants the shortest path to the closest end

import heapq

grid = {}; 
xMin, yMin, xMax, yMax = 0,0,0,0; #could get rid of it but w/e
dirs = [(1,0),(0,1),(-1,0),(0,-1)]; #east, south, west, north

f = open("16.in","r");
for y,l in enumerate(f):
	yMax = max(y,yMax);
	for x,c in enumerate(l.strip()):
		if c == "S": 
			start = (x,y,0);
			c = "."
		grid[(x,y)]=c;
		xMax = max(xMax,x);
f.close();

def getAdjacent(coordinates):
	adj = [];
	xp, yp, dp = coordinates;
	for i,d in enumerate(dirs):
		dx,dy = d;
		if grid.get((xp+dx,yp+dy),"#") != "#": 
			adj.append((xp+dx,yp+dy,i));
	return adj;

def pathfinding(startingpos):
	ends = set(); #max 4, coordinates of E but coming from each direction
	BigNumber = xMax*yMax*100000000000; #used to initialize Shortest Path to have something to compare with
	ShortestPath = BigNumber;
	pathmap,pathdis = {},{};
	queue = [];
	heapq.heappush(queue,(0,startingpos));
	pathmap[startingpos] = set();
	while queue:
		distance, coords = heapq.heappop(queue);
		if distance > ShortestPath: continue;
		px,py,pd = coords;
		if grid.get((px,py),"#") == "E":
			ShortestPath = min(ShortestPath,distance); #this should be updated only once
			if distance == ShortestPath: 
				ends.add(coords); #always the same E, just from different direction
		NextValid = getAdjacent((px,py,pd));
		for NV in NextValid: #for each valid adjacent node (valid here means it's not a wall)
			next_dist = distance+1+1000*(NV[2]!=pd);
			prev_dist = pathdis.get(NV,BigNumber);
			if next_dist > prev_dist: 
				continue;
			if next_dist < prev_dist: 
				pathmap[NV] = set(); #reset the set of positions with shortest path to it
				pathdis[NV] = next_dist; #update the shortest path to node NV
			pathmap[NV].add(coords);
			heapq.heappush(queue,(next_dist,NV));
	return ShortestPath, pathmap, ends;

def FindSpots(startpos,CurrentNode,pathmap):
	validpaths = set();
	validpaths.add((CurrentNode[0],CurrentNode[1]));
	if CurrentNode == startpos:
		return validpaths;
	NextPositions = pathmap[CurrentNode]; #read the set of nodes that connected to this node
	for np in NextPositions:
		validpaths.update(FindSpots(startpos,np,pathmap));
	return validpaths;

ValidTiles = set(); #tiles that are on one of the best paths through the maze
part1, PathMap, PathEnds = pathfinding(start); 
for EndPosition in PathEnds:
	ValidTiles.update(FindSpots(start,EndPosition,PathMap));
part2 = len(ValidTiles);
''' #map print
for y in range(yMax+1):
	for x in range(xMax+1):
		c = grid[(x,y)];
		if (x,y) in ValidTiles: c = str("O");
		print(c, end="");
	print(); #'''
print("part 1 =", part1);
print("part 2 =", part2);