day 16 python alternate solution (astar)
This commit is contained in:
		
							parent
							
								
									014b7daf09
								
							
						
					
					
						commit
						a54304686d
					
				
							
								
								
									
										97
									
								
								day16/search.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								day16/search.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| G = [] | ||||
| for line in open("input"): | ||||
|     words = line.split() | ||||
|     valve = words[1] | ||||
|     rate = int(''.join(x for x in words[4] if x.isdigit())) | ||||
|     edges = [x.strip(", ") for x in words[9:]] | ||||
|     G.append((valve, rate, edges)) | ||||
| 
 | ||||
| #print(G) | ||||
| 
 | ||||
| import sys, os; sys.path.append(os.path.join(os.path.dirname(__file__), "../lib")) | ||||
| import astar | ||||
| 
 | ||||
| def search(): | ||||
|     V = sorted(v for v,_,_ in G) # vertices | ||||
|     B = {v: 1<<i for i, v in enumerate(V)} # bitmasks | ||||
|     E = {B[v]: [B[e] for e in edges] for v,_,edges in G} # edge[b] -> b | ||||
|     R = {B[v]: r for v,r,_ in G} # rewards: R[b] = reward | ||||
| 
 | ||||
|     all_closed = sum(B.values()) | ||||
|     all_open = 0 | ||||
| 
 | ||||
|     minutes = 30 | ||||
|     start = (B['AA'], minutes, all_closed) | ||||
| 
 | ||||
|     # A* search minimizes costs | ||||
|     # it can't maxmize anything | ||||
|     # so we'll borrow an idea from https://github.com/morgoth1145/advent-of-code/blob/2bf7c157e37b3e0a65deedc6c88e42297d813d1d/2022/16/solution.py | ||||
|     # and instead say that the cost of moving from one node to the next | ||||
|     # is equal to the potential pressure we could have released from the closed pipes | ||||
|     # or, in other words, we'll keep track of how much pressure builds up in | ||||
|     # closed pipes instead of how much pressure is released from open pipes | ||||
| 
 | ||||
|     # let's shink the graph by finding the shortest path between | ||||
|     # every pair of rooms (floyd-warshall), and then building a graph which only has | ||||
|     # paths from the starting room to rooms with a valve | ||||
|     # and from any room with a valve to any other room with a valve | ||||
| 
 | ||||
|     # our heuristic cost has to be <= the actual cost of getting to the goal | ||||
|     # here's a simple one: | ||||
|     # we know it takes at least 1 minute to open a valve, | ||||
|     # and at least another minute to walk to the valve | ||||
|     # so we can assign at least cost 2*pressure(closed_valves) to this node | ||||
|     # (unless there is only 1 minute left) | ||||
|     def heuristic(node): | ||||
|         v, minutes, closed = node | ||||
|         if closed == all_open: | ||||
|             m = minutes | ||||
|         else: | ||||
|             m = min(minutes,1) | ||||
|         return m*pressure(closed) | ||||
| 
 | ||||
|     def pressure(bits): | ||||
|         pressure = 0 | ||||
|         for v,r in R.items(): | ||||
|             if bits&v: | ||||
|                 pressure += r | ||||
|         return pressure | ||||
| 
 | ||||
|     def is_goal(n): | ||||
|         v, minutes, closed = n | ||||
|         return minutes == 0 | ||||
| 
 | ||||
|     info = {} | ||||
|     def neighbors(n): | ||||
|         v, minutes, closed = n | ||||
|         if minutes not in info: | ||||
|             print(info) | ||||
|             info[minutes] = 0 | ||||
|         info[minutes] += 1 | ||||
|         if minutes <= 0: | ||||
|             pass | ||||
|         elif closed == all_open: | ||||
|             c = pressure(closed) * minutes | ||||
|             yield c, (v, 0, closed) | ||||
|         else: | ||||
|             c = pressure(closed) | ||||
|             if v&closed and R[v]: | ||||
|                 yield c, (v, minutes-1, closed & ~v) | ||||
|             for e in E[v]: | ||||
|                 yield c, (e, minutes-1, closed) | ||||
| 
 | ||||
| 
 | ||||
|     c, _, path = astar.search(start, is_goal, neighbors, heuristic) | ||||
|     print(c) | ||||
|     print(pressure(all_closed)*30 - c) | ||||
| 
 | ||||
|     #maxpair = [] | ||||
|     #def pairs(): | ||||
|     #    O = sorted(best.keys()) | ||||
|     #    for i in range(len(O)): | ||||
|     #        for j in range(i,len(O)): | ||||
|     #            if not O[i] & O[j]: | ||||
|     #                yield(best[O[i]]+best[O[j]]) | ||||
|     #print(max(pairs())) | ||||
| 
 | ||||
| search() | ||||
| @ -1,10 +1,14 @@ | ||||
| from heapq import heappush, heappop | ||||
| 
 | ||||
| def search(start, goal, neighbors, heuristic=None): | ||||
| def search(start, is_goal, neighbors, heuristic=None): | ||||
|     if heuristic == None: | ||||
|         def heuristic(x): | ||||
|             return 0 | ||||
|     # TODO: callable goal | ||||
|     if not callable(is_goal): | ||||
|         goal = is_goal | ||||
|         def is_goal(this): | ||||
|             return this == goal | ||||
|     if not isinstance(start, list): | ||||
|         start = [start] | ||||
|     i = 0 # tiebreaker | ||||
| @ -22,7 +26,7 @@ def search(start, goal, neighbors, heuristic=None): | ||||
| 
 | ||||
|         done[this] = (cost_so_far, prev) | ||||
| 
 | ||||
|         if this == goal: | ||||
|         if is_goal(this): | ||||
|             print("astar: visited", len(done), "nodes") | ||||
|             # reconsruct the path | ||||
|             n = this | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user