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 | from heapq import heappush, heappop | ||||||
| 
 | 
 | ||||||
| def search(start, goal, neighbors, heuristic=None): | def search(start, is_goal, neighbors, heuristic=None): | ||||||
|     if heuristic == None: |     if heuristic == None: | ||||||
|         def heuristic(x): |         def heuristic(x): | ||||||
|             return 0 |             return 0 | ||||||
|     # TODO: callable goal |     # TODO: callable goal | ||||||
|  |     if not callable(is_goal): | ||||||
|  |         goal = is_goal | ||||||
|  |         def is_goal(this): | ||||||
|  |             return this == goal | ||||||
|     if not isinstance(start, list): |     if not isinstance(start, list): | ||||||
|         start = [start] |         start = [start] | ||||||
|     i = 0 # tiebreaker |     i = 0 # tiebreaker | ||||||
| @ -22,7 +26,7 @@ def search(start, goal, neighbors, heuristic=None): | |||||||
| 
 | 
 | ||||||
|         done[this] = (cost_so_far, prev) |         done[this] = (cost_so_far, prev) | ||||||
| 
 | 
 | ||||||
|         if this == goal: |         if is_goal(this): | ||||||
|             print("astar: visited", len(done), "nodes") |             print("astar: visited", len(done), "nodes") | ||||||
|             # reconsruct the path |             # reconsruct the path | ||||||
|             n = this |             n = this | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user