Compare commits
	
		
			No commits in common. "5a7c8ef73215bd22bfde9ca91f30b3cc1aa85cd9" and "7b40516b66b3a3bc09f7a132c7b69ff77d73a83a" have entirely different histories.
		
	
	
		
			5a7c8ef732
			...
			7b40516b66
		
	
		
@ -1,25 +0,0 @@
 | 
			
		||||
<svg width="1000" height="1000" viewBox="0 0 4000000 4000000" xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
<path d="M 325337 1156002L 1738198 2568863 L 325337 3981724 L -1087524 2568863 L 325337 1156002" style="fill:#ff0000;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3988825 -768301L 5594946 837820 L 3988825 2443941 L 2382704 837820 L 3988825 -768301" style="fill:#ff3700;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 1611311 363595L 3300890 2053174 L 1611311 3742753 L -78268 2053174 L 1611311 363595" style="fill:#ff6e00;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 101890 2603932L 1438007 3940049 L 101890 5276166 L -1234227 3940049 L 101890 2603932" style="fill:#ffa600;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3962702 2247845L 4273282 2558425 L 3962702 2869005 L 3652122 2558425 L 3962702 2247845" style="fill:#ffdd00;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 2957890 1448846L 3669857 2160813 L 2957890 2872780 L 2245923 2160813 L 2957890 1448846" style="fill:#e8ff00;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3907456 3015138L 4217928 3325610 L 3907456 3636082 L 3596984 3325610 L 3907456 3015138" style="fill:#b1ff00;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3354177 2884329L 3905767 3435919 L 3354177 3987509 L 2802587 3435919 L 3354177 2884329" style="fill:#79ff00;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3997379 2616205L 4453042 3071868 L 3997379 3527531 L 3541716 3071868 L 3997379 2616205" style="fill:#42ff00;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 145143 766120L 1093985 1714962 L 145143 2663804 L -803699 1714962 L 145143 766120" style="fill:#0bff00;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 611563 2496305L 1264122 3148864 L 611563 3801423 L -40996 3148864 L 611563 2496305" style="fill:#00ff2c;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3080405 2610557L 4374625 3904777 L 3080405 5198997 L 1786185 3904777 L 3080405 2610557" style="fill:#00ff63;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 644383 -574325L 1229440 10732 L 644383 595789 L 59326 10732 L 644383 -574325" style="fill:#00ff9b;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3229566 1177170L 3746563 1694167 L 3229566 2211164 L 2712569 1694167 L 3229566 1177170" style="fill:#00ffd2;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 1600637 2812349L 2773172 3984884 L 1600637 5157419 L 428102 3984884 L 1600637 2812349" style="fill:#00f3ff;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 2959765 2275581L 3505044 2820860 L 2959765 3366139 L 2414486 2820860 L 2959765 2275581" style="fill:#00bcff;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 2235330 2641704L 3021423 3427797 L 2235330 4213890 L 1449237 3427797 L 2235330 2641704" style="fill:#0085ff;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 2428996 -1555978L 4195855 210881 L 2428996 1977740 L 662137 210881 L 2428996 -1555978" style="fill:#004dff;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 369661 -299603L 1357069 687805 L 369661 1675213 L -617747 687805 L 369661 -299603" style="fill:#0016ff;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3558476 1372938L 4309152 2123614 L 3558476 2874290 L 2807800 2123614 L 3558476 1372938" style="fill:#2100ff;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3551529 2279143L 4097490 2825104 L 3551529 3371065 L 3005568 2825104 L 3551529 2279143" style="fill:#5800ff;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 64895 -594317L 662789 3577 L 64895 601471 L -532999 3577 L 64895 -594317" style="fill:#9000ff;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
<path d="M 3079531 1245503L 3372687 1538659 L 3079531 1831815 L 2786375 1538659 L 3079531 1245503" style="fill:#c700ff;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />
 | 
			
		||||
</svg>
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 3.9 KiB  | 
							
								
								
									
										26
									
								
								day15/svg.py
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								day15/svg.py
									
									
									
									
									
								
							@ -1,26 +0,0 @@
 | 
			
		||||
data = []
 | 
			
		||||
for line in open("input"):
 | 
			
		||||
    words = line.replace(":", "").replace(",","").split()
 | 
			
		||||
    coords = [int(w[w.index('=')+1:]) for w in words if '=' in w]
 | 
			
		||||
    data.append(coords)
 | 
			
		||||
 | 
			
		||||
import colorsys
 | 
			
		||||
def hsl(h,s,l):
 | 
			
		||||
    r,g,b = colorsys.hls_to_rgb(h/360,l,s)
 | 
			
		||||
    return int(r*255),int(g*255),int(b*255)
 | 
			
		||||
 | 
			
		||||
colors = []
 | 
			
		||||
for i in range(len(data)):
 | 
			
		||||
    hue = i*300/len(data)
 | 
			
		||||
    colors.append("#%02x%02x%02x" % hsl(hue, 1, .5))
 | 
			
		||||
 | 
			
		||||
print('<svg width="1000" height="1000" viewBox="0 0 4000000 4000000" xmlns="http://www.w3.org/2000/svg">')
 | 
			
		||||
for i,(sx,sy,bx,by) in enumerate(data):
 | 
			
		||||
    dx = abs(sx-bx)
 | 
			
		||||
    dy = abs(sy-by)
 | 
			
		||||
    d = dx+dy
 | 
			
		||||
    path = [(sx+d,sy),(sx,sy+d),(sx-d,sy),(sx,sy-d)]
 | 
			
		||||
    s = "M %d %d" % (path[-1]) + " ".join("L %d %d" % (x,y) for x,y in path)
 | 
			
		||||
    print('<path d="%s" style="fill:%s;fill-opacity:0.5;stroke:#000;stroke-width:5000px" />' % (s,colors[i]))
 | 
			
		||||
#print('<circle cx="3257428" cy="2573243" r="10000" fill="red" />')
 | 
			
		||||
print('</svg>')
 | 
			
		||||
							
								
								
									
										223
									
								
								day16/bad.py
									
									
									
									
									
								
							
							
						
						
									
										223
									
								
								day16/bad.py
									
									
									
									
									
								
							@ -1,223 +0,0 @@
 | 
			
		||||
G = []
 | 
			
		||||
for line in open("input"):
 | 
			
		||||
    words = line.split()
 | 
			
		||||
    valve = words[1]
 | 
			
		||||
    rate = words[4]
 | 
			
		||||
    edges = [x.strip(", ") for x in words[9:]]
 | 
			
		||||
 | 
			
		||||
    print(rate)
 | 
			
		||||
    rate = rate.strip(";")
 | 
			
		||||
    rate = rate[rate.index("=")+1:]
 | 
			
		||||
    rate = int(rate)
 | 
			
		||||
    G.append((valve, rate, edges))
 | 
			
		||||
 | 
			
		||||
print(G)
 | 
			
		||||
 | 
			
		||||
def save():
 | 
			
		||||
    seen = set()
 | 
			
		||||
    with open("input.dot", "w") as f:
 | 
			
		||||
        print("graph {", file=f)
 | 
			
		||||
        for v, r, E in G:
 | 
			
		||||
            if r:
 | 
			
		||||
                print('%s [label="%s (%s)"]' % (v, v, r), file=f)
 | 
			
		||||
            for e in E:
 | 
			
		||||
                if (e,v) not in seen:
 | 
			
		||||
                    print("%s -- %s" % (v,e), file=f)
 | 
			
		||||
                    seen.add((v,e))
 | 
			
		||||
        print("}", file=f)
 | 
			
		||||
 | 
			
		||||
#save()
 | 
			
		||||
 | 
			
		||||
#import astar
 | 
			
		||||
 | 
			
		||||
def part1():
 | 
			
		||||
    score = {'AA': (0, 0, [])}
 | 
			
		||||
 | 
			
		||||
    minutes = 30
 | 
			
		||||
    for _ in range(minutes):
 | 
			
		||||
        minutes -= 1
 | 
			
		||||
        next = {}
 | 
			
		||||
        for v, r, E in G:
 | 
			
		||||
            vo = v + 'o'
 | 
			
		||||
            s = []
 | 
			
		||||
            o = []
 | 
			
		||||
            if vo in score:
 | 
			
		||||
                reward, flow, open = score[vo]
 | 
			
		||||
                o.append((reward, flow, open))
 | 
			
		||||
            if v in score:
 | 
			
		||||
                reward, flow, open = score[v]
 | 
			
		||||
                # stay, don't open valve
 | 
			
		||||
                s.append((reward, flow, open))
 | 
			
		||||
                # stay in place, open valve
 | 
			
		||||
                if v not in open:
 | 
			
		||||
                    reward += r*minutes
 | 
			
		||||
                    o.append((reward, flow+r, open+[v]))
 | 
			
		||||
            # move here from somewhere else
 | 
			
		||||
            for e in E:
 | 
			
		||||
                if e in score:
 | 
			
		||||
                    if v in score[e][-1]:
 | 
			
		||||
                        o.append(score[e])
 | 
			
		||||
                    else:
 | 
			
		||||
                        s.append(score[e])
 | 
			
		||||
                eo = e+'o'
 | 
			
		||||
                if eo in score:
 | 
			
		||||
                    if v in score[eo][-1]:
 | 
			
		||||
                        o.append(score[eo])
 | 
			
		||||
                    else:
 | 
			
		||||
                        s.append(score[eo])
 | 
			
		||||
            if s:
 | 
			
		||||
                next[v] = max(s)
 | 
			
		||||
            if o:
 | 
			
		||||
                next[vo] = max(o)
 | 
			
		||||
        score = next
 | 
			
		||||
        print("%d minutes left" % minutes)
 | 
			
		||||
        for v, (r, flow, open) in sorted(score.items(), key=lambda x: x[1]):
 | 
			
		||||
            print("\t", v, r, "\t", ",".join(open))
 | 
			
		||||
    print(max(r for r,_,_ in score.values()))
 | 
			
		||||
 | 
			
		||||
def part2():
 | 
			
		||||
    H = {x[0]: x for x in G}
 | 
			
		||||
    def actions(v, open):
 | 
			
		||||
        _, r, E = H[v]
 | 
			
		||||
        if v in open:
 | 
			
		||||
            yield (v, 0, [])
 | 
			
		||||
        else:
 | 
			
		||||
            yield (v, 0, [])
 | 
			
		||||
            yield (v+'o', r, [v])
 | 
			
		||||
        # move to somewhere else
 | 
			
		||||
        for e in E:
 | 
			
		||||
            if e in open:
 | 
			
		||||
                e = e+'o'
 | 
			
		||||
            yield (e, 0, [])
 | 
			
		||||
 | 
			
		||||
    score = {('AA', 'AA'): (0, 0, [])}
 | 
			
		||||
    minutes = 26
 | 
			
		||||
    for _ in range(minutes):
 | 
			
		||||
        next = {}
 | 
			
		||||
        minutes -= 1
 | 
			
		||||
 | 
			
		||||
        for (me, elephant) in score:
 | 
			
		||||
            reward, flow, open = score[(me, elephant)]
 | 
			
		||||
            for v, r, o in actions(me.strip('o'), open):
 | 
			
		||||
                for v1, r1, o1 in actions(elephant.strip('o'), open+o):
 | 
			
		||||
                    reward_ = reward + (r+r1)*minutes
 | 
			
		||||
                    flow_ = flow + r + r1
 | 
			
		||||
                    open_ = open + o + o1
 | 
			
		||||
                next.setdefault((v, v1), []).append((reward_, flow_, open_))
 | 
			
		||||
 | 
			
		||||
        for k in next:
 | 
			
		||||
            next[k] = max(next[k])
 | 
			
		||||
 | 
			
		||||
        score = next
 | 
			
		||||
        print("%d minutes left" % minutes)
 | 
			
		||||
        for v, (r, flow, open) in sorted(score.items(), key=lambda x: x[1]):
 | 
			
		||||
            print("\t", v, r, "\t", ",".join(open))
 | 
			
		||||
    print(max(r for r,_,_ in score.values()))
 | 
			
		||||
 | 
			
		||||
#part2()
 | 
			
		||||
 | 
			
		||||
def part2_b():
 | 
			
		||||
    score = {'AA': (0, 0, [])}
 | 
			
		||||
 | 
			
		||||
    minutes = 26
 | 
			
		||||
    for _ in range(minutes):
 | 
			
		||||
        minutes -= 1
 | 
			
		||||
        next = {}
 | 
			
		||||
        for v, r, E in G:
 | 
			
		||||
            vo = v + 'o'
 | 
			
		||||
            s = []
 | 
			
		||||
            o = []
 | 
			
		||||
            if vo in score:
 | 
			
		||||
                reward, flow, open = score[vo]
 | 
			
		||||
                o.append((reward, flow, open))
 | 
			
		||||
            if v in score:
 | 
			
		||||
                reward, flow, open = score[v]
 | 
			
		||||
                # stay, don't open valve
 | 
			
		||||
                s.append((reward, flow, open))
 | 
			
		||||
                # stay in place, open valve
 | 
			
		||||
                if v not in open:
 | 
			
		||||
                    reward += r*minutes
 | 
			
		||||
                    o.append((reward, flow+r, open+[v]))
 | 
			
		||||
            # move here from somewhere else
 | 
			
		||||
            for e in E:
 | 
			
		||||
                if e in score:
 | 
			
		||||
                    if v in score[e][-1]:
 | 
			
		||||
                        o.append(score[e])
 | 
			
		||||
                    else:
 | 
			
		||||
                        s.append(score[e])
 | 
			
		||||
                eo = e+'o'
 | 
			
		||||
                if eo in score:
 | 
			
		||||
                    if v in score[eo][-1]:
 | 
			
		||||
                        o.append(score[eo])
 | 
			
		||||
                    else:
 | 
			
		||||
                        s.append(score[eo])
 | 
			
		||||
            if s:
 | 
			
		||||
                next[v] = max(s)
 | 
			
		||||
            if o:
 | 
			
		||||
                next[vo] = max(o)
 | 
			
		||||
        score = next
 | 
			
		||||
        print("%d minutes left" % minutes)
 | 
			
		||||
        for v, (r, flow, open) in sorted(score.items(), key=lambda x: x[1]):
 | 
			
		||||
            print("\t", v, r, "\t", ",".join(open))
 | 
			
		||||
 | 
			
		||||
    maxpair = []
 | 
			
		||||
    for r,_,open in score.values():
 | 
			
		||||
        o = frozenset(open)
 | 
			
		||||
        for s,_,open in score.values():
 | 
			
		||||
            if o.isdisjoint(open):
 | 
			
		||||
                maxpair.append(r+s)
 | 
			
		||||
    print(max(maxpair))
 | 
			
		||||
 | 
			
		||||
#part2_b()
 | 
			
		||||
 | 
			
		||||
def part2_c():
 | 
			
		||||
    V = sorted(v for v,_,_ in G)
 | 
			
		||||
    B = {v: 1<<i for i, v in enumerate(V)}
 | 
			
		||||
    E = {B[v]: [B[e] for e in edges] for v,_,edges in G}
 | 
			
		||||
    R = {B[v]: r for v,r,_ in G}
 | 
			
		||||
 | 
			
		||||
    all_open = sum(B.values())
 | 
			
		||||
 | 
			
		||||
    aa = B['AA']
 | 
			
		||||
    states = [(aa, 0, 0)]
 | 
			
		||||
    minutes = 26
 | 
			
		||||
    for _ in range(minutes):
 | 
			
		||||
        print(minutes, len(states))
 | 
			
		||||
        minutes -= 1
 | 
			
		||||
        next = []
 | 
			
		||||
        seen = set()
 | 
			
		||||
        states.sort(key=lambda x: -x[2])
 | 
			
		||||
        for v, open, reward in states:
 | 
			
		||||
            if (v,open) in seen:
 | 
			
		||||
                continue
 | 
			
		||||
            seen.add((v,open))
 | 
			
		||||
            #next.append((v, open, reward))
 | 
			
		||||
            if open == all_open:
 | 
			
		||||
                next.append((v, open, reward))
 | 
			
		||||
            else:
 | 
			
		||||
                #next.append((v, open, reward))
 | 
			
		||||
                r = R[v]
 | 
			
		||||
                if r and not v&open:
 | 
			
		||||
                    next.append((v, open|v, reward+r*minutes))
 | 
			
		||||
                for e in E[v]:
 | 
			
		||||
                    next.append((e, open, reward))
 | 
			
		||||
        states = next
 | 
			
		||||
 | 
			
		||||
    print(max(r for _,_,r in states))
 | 
			
		||||
 | 
			
		||||
    best = {}
 | 
			
		||||
    for _,o,r in states:
 | 
			
		||||
        if o in best and best[o] >= r:
 | 
			
		||||
            continue
 | 
			
		||||
        best[o] = r
 | 
			
		||||
 | 
			
		||||
    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()))
 | 
			
		||||
 | 
			
		||||
part2_c()
 | 
			
		||||
							
								
								
									
										56
									
								
								day16/input
									
									
									
									
									
								
							
							
						
						
									
										56
									
								
								day16/input
									
									
									
									
									
								
							@ -1,56 +0,0 @@
 | 
			
		||||
Valve JI has flow rate=21; tunnels lead to valves WI, XG
 | 
			
		||||
Valve DM has flow rate=3; tunnels lead to valves JX, NG, AW, BY, PF
 | 
			
		||||
Valve AZ has flow rate=0; tunnels lead to valves FJ, VC
 | 
			
		||||
Valve YQ has flow rate=0; tunnels lead to valves TE, OP
 | 
			
		||||
Valve WI has flow rate=0; tunnels lead to valves JI, VC
 | 
			
		||||
Valve NE has flow rate=0; tunnels lead to valves ZK, AA
 | 
			
		||||
Valve FM has flow rate=0; tunnels lead to valves LC, DU
 | 
			
		||||
Valve QI has flow rate=0; tunnels lead to valves TE, JW
 | 
			
		||||
Valve OY has flow rate=0; tunnels lead to valves XS, VF
 | 
			
		||||
Valve XS has flow rate=18; tunnels lead to valves RR, OY, SV, NQ
 | 
			
		||||
Valve NU has flow rate=0; tunnels lead to valves IZ, BD
 | 
			
		||||
Valve JX has flow rate=0; tunnels lead to valves DM, ZK
 | 
			
		||||
Valve WT has flow rate=23; tunnels lead to valves OV, QJ
 | 
			
		||||
Valve KM has flow rate=0; tunnels lead to valves TE, OL
 | 
			
		||||
Valve NG has flow rate=0; tunnels lead to valves II, DM
 | 
			
		||||
Valve FJ has flow rate=0; tunnels lead to valves AZ, II
 | 
			
		||||
Valve QR has flow rate=0; tunnels lead to valves ZK, KI
 | 
			
		||||
Valve KI has flow rate=9; tunnels lead to valves ZZ, DI, TL, AJ, QR
 | 
			
		||||
Valve ON has flow rate=0; tunnels lead to valves LC, QT
 | 
			
		||||
Valve AW has flow rate=0; tunnels lead to valves DM, AA
 | 
			
		||||
Valve HI has flow rate=0; tunnels lead to valves TE, VC
 | 
			
		||||
Valve XG has flow rate=0; tunnels lead to valves II, JI
 | 
			
		||||
Valve II has flow rate=19; tunnels lead to valves LF, NG, OL, FJ, XG
 | 
			
		||||
Valve VC has flow rate=24; tunnels lead to valves WI, HI, AZ
 | 
			
		||||
Valve VJ has flow rate=0; tunnels lead to valves UG, AA
 | 
			
		||||
Valve IZ has flow rate=0; tunnels lead to valves VF, NU
 | 
			
		||||
Valve EJ has flow rate=0; tunnels lead to valves ZK, LC
 | 
			
		||||
Valve DU has flow rate=12; tunnels lead to valves TC, UG, FM
 | 
			
		||||
Valve ZK has flow rate=10; tunnels lead to valves JX, EJ, JW, QR, NE
 | 
			
		||||
Valve XF has flow rate=25; tunnels lead to valves OP, VT
 | 
			
		||||
Valve LC has flow rate=4; tunnels lead to valves FM, EJ, ON, AJ, PF
 | 
			
		||||
Valve SV has flow rate=0; tunnels lead to valves XS, IY
 | 
			
		||||
Valve LF has flow rate=0; tunnels lead to valves II, OV
 | 
			
		||||
Valve DI has flow rate=0; tunnels lead to valves KI, BY
 | 
			
		||||
Valve OP has flow rate=0; tunnels lead to valves YQ, XF
 | 
			
		||||
Valve NQ has flow rate=0; tunnels lead to valves TC, XS
 | 
			
		||||
Valve QJ has flow rate=0; tunnels lead to valves VT, WT
 | 
			
		||||
Valve IY has flow rate=22; tunnel leads to valve SV
 | 
			
		||||
Valve AJ has flow rate=0; tunnels lead to valves LC, KI
 | 
			
		||||
Valve TE has flow rate=11; tunnels lead to valves QI, HI, KM, YQ
 | 
			
		||||
Valve ZZ has flow rate=0; tunnels lead to valves KI, AA
 | 
			
		||||
Valve VT has flow rate=0; tunnels lead to valves XF, QJ
 | 
			
		||||
Valve OL has flow rate=0; tunnels lead to valves KM, II
 | 
			
		||||
Valve TC has flow rate=0; tunnels lead to valves NQ, DU
 | 
			
		||||
Valve TL has flow rate=0; tunnels lead to valves VF, KI
 | 
			
		||||
Valve QT has flow rate=0; tunnels lead to valves AA, ON
 | 
			
		||||
Valve BY has flow rate=0; tunnels lead to valves DM, DI
 | 
			
		||||
Valve OV has flow rate=0; tunnels lead to valves LF, WT
 | 
			
		||||
Valve VN has flow rate=0; tunnels lead to valves RR, BD
 | 
			
		||||
Valve VF has flow rate=13; tunnels lead to valves OY, IZ, TL
 | 
			
		||||
Valve BD has flow rate=17; tunnels lead to valves NU, VN
 | 
			
		||||
Valve UG has flow rate=0; tunnels lead to valves VJ, DU
 | 
			
		||||
Valve PF has flow rate=0; tunnels lead to valves LC, DM
 | 
			
		||||
Valve RR has flow rate=0; tunnels lead to valves XS, VN
 | 
			
		||||
Valve AA has flow rate=0; tunnels lead to valves QT, ZZ, AW, VJ, NE
 | 
			
		||||
Valve JW has flow rate=0; tunnels lead to valves ZK, QI
 | 
			
		||||
							
								
								
									
										10
									
								
								day16/sample
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								day16/sample
									
									
									
									
									
								
							@ -1,10 +0,0 @@
 | 
			
		||||
Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
 | 
			
		||||
Valve BB has flow rate=13; tunnels lead to valves CC, AA
 | 
			
		||||
Valve CC has flow rate=2; tunnels lead to valves DD, BB
 | 
			
		||||
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
 | 
			
		||||
Valve EE has flow rate=3; tunnels lead to valves FF, DD
 | 
			
		||||
Valve FF has flow rate=0; tunnels lead to valves EE, GG
 | 
			
		||||
Valve GG has flow rate=0; tunnels lead to valves FF, HH
 | 
			
		||||
Valve HH has flow rate=22; tunnel leads to valve GG
 | 
			
		||||
Valve II has flow rate=0; tunnels lead to valves AA, JJ
 | 
			
		||||
Valve JJ has flow rate=21; tunnel leads to valve II
 | 
			
		||||
							
								
								
									
										216
									
								
								day16/sol.py
									
									
									
									
									
								
							
							
						
						
									
										216
									
								
								day16/sol.py
									
									
									
									
									
								
							@ -1,216 +0,0 @@
 | 
			
		||||
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 solve():
 | 
			
		||||
    G.sort(key=lambda x: (-x[1],x[0]))
 | 
			
		||||
    B = {v: 1<<i for i,(v,_,_) in enumerate(G)} # bitmasks
 | 
			
		||||
    E = {B[v]: [B[e] for e in edges] for v,_,edges in G} # E[b] = edges of b
 | 
			
		||||
    R = {B[v]: r for v,r,_ in G if r} # R[b] -> rate
 | 
			
		||||
 | 
			
		||||
    AA = B['AA']
 | 
			
		||||
 | 
			
		||||
    all_closed = sum(R.keys())
 | 
			
		||||
    all_open = 0
 | 
			
		||||
 | 
			
		||||
    # TODO: memoize this
 | 
			
		||||
    def pressure(bits):
 | 
			
		||||
        pressure = 0
 | 
			
		||||
        for v,r in R.items():
 | 
			
		||||
            if bits&v:
 | 
			
		||||
                pressure += r
 | 
			
		||||
        return pressure
 | 
			
		||||
 | 
			
		||||
    assert pressure(all_open) == 0
 | 
			
		||||
    max_pressure = pressure(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 use that to build a
 | 
			
		||||
    # weighted graph which only has paths from any room to rooms with a valve.
 | 
			
		||||
    #
 | 
			
		||||
    # not only does this make our search space smaller,
 | 
			
		||||
    # it also helps by making it so that the cost changes on every step
 | 
			
		||||
    # (since opening a valve is the only thing that actually changes the pressure)
 | 
			
		||||
    # giving A* a much clearer signal about which paths are worth exploring
 | 
			
		||||
    dist = {}
 | 
			
		||||
    for v in E:
 | 
			
		||||
        dist[v,v] = 0
 | 
			
		||||
        for e in E[v]:
 | 
			
		||||
            dist[v,e] = 1
 | 
			
		||||
            dist[e,v] = 1
 | 
			
		||||
    for t in E:
 | 
			
		||||
        for u in E:
 | 
			
		||||
            for v in E:
 | 
			
		||||
                if (u,t) in dist and (t,v) in dist:
 | 
			
		||||
                    dist[u,v] = min(dist.get((u,v),999999), dist[u,t] + dist[t,v])
 | 
			
		||||
 | 
			
		||||
    W = {} # weighted edges
 | 
			
		||||
    for u in E:
 | 
			
		||||
        W[u] = []
 | 
			
		||||
        for v in R:
 | 
			
		||||
            if (u,v) in dist:
 | 
			
		||||
                W[u].append((v, dist[u,v]))
 | 
			
		||||
 | 
			
		||||
    print(W[AA])
 | 
			
		||||
 | 
			
		||||
    # 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)
 | 
			
		||||
    #
 | 
			
		||||
    # we can keep doing that until there are no valves left to open
 | 
			
		||||
    # or there is no time left.
 | 
			
		||||
    #
 | 
			
		||||
    # note that the nodes with the largest flow rate are assigned
 | 
			
		||||
    # the lowest position in the bitmap, so clearing the bits from
 | 
			
		||||
    # low to high will always give us the optimal order
 | 
			
		||||
    def heuristic(node):
 | 
			
		||||
        v, minutes, closed = node
 | 
			
		||||
        # assume we can open a valve every 2 minutes
 | 
			
		||||
        # how much would that cost?
 | 
			
		||||
        c = 0
 | 
			
		||||
        while closed and minutes > 0:
 | 
			
		||||
            c += pressure(closed) * min(minutes,2)
 | 
			
		||||
            closed &= (closed-1)
 | 
			
		||||
            minutes -= 2
 | 
			
		||||
        return c
 | 
			
		||||
 | 
			
		||||
    def is_goal(node):
 | 
			
		||||
        v, minutes, closed = node
 | 
			
		||||
        return minutes == 0 or closed == all_open
 | 
			
		||||
 | 
			
		||||
    info = {}
 | 
			
		||||
    def neighbors(node):
 | 
			
		||||
        v, minutes, closed = node
 | 
			
		||||
        if minutes not in info:
 | 
			
		||||
            print(info)
 | 
			
		||||
            info[minutes] = 0
 | 
			
		||||
        info[minutes] += 1
 | 
			
		||||
        if minutes <= 0:
 | 
			
		||||
            pass
 | 
			
		||||
        elif closed == all_open:
 | 
			
		||||
            yield 0, (v, 0, closed)
 | 
			
		||||
        else:
 | 
			
		||||
            # move to a closed valve (or maybe stay in
 | 
			
		||||
            # the same spot) and open it
 | 
			
		||||
            can_move = False
 | 
			
		||||
            for e, dist in W[v]:
 | 
			
		||||
                t = dist + 1
 | 
			
		||||
                if e&closed and t <= minutes:
 | 
			
		||||
                    can_move = True
 | 
			
		||||
                    c = pressure(closed)*t
 | 
			
		||||
                    yield c, (e, minutes-t, closed&~e)
 | 
			
		||||
            # wait til the end
 | 
			
		||||
            if can_move == False:
 | 
			
		||||
                yield pressure(closed)*minutes, (v, 0, closed)
 | 
			
		||||
 | 
			
		||||
    minutes = 30
 | 
			
		||||
    start = (AA, minutes, all_closed)
 | 
			
		||||
 | 
			
		||||
    c, _, path = astar.search(start, is_goal, neighbors, heuristic)
 | 
			
		||||
    print(c)
 | 
			
		||||
    print(max_pressure*minutes - c)
 | 
			
		||||
 | 
			
		||||
    def heuristic2(node):
 | 
			
		||||
        if is_goal2(node):
 | 
			
		||||
            return 0
 | 
			
		||||
        v1, v2, min1, min2, closed = node
 | 
			
		||||
        # if the players are out of sync, assume the other player
 | 
			
		||||
        # will close one valve when they catch up
 | 
			
		||||
        if min1 != min2:
 | 
			
		||||
            closed &= closed - 1
 | 
			
		||||
        # assume we can open a valve every minute remaining
 | 
			
		||||
        # how much would that cost?
 | 
			
		||||
        c = 0
 | 
			
		||||
        pr = pressure(closed)
 | 
			
		||||
        m = min(min1, min2)
 | 
			
		||||
        while closed and m > 0:
 | 
			
		||||
            c += pr
 | 
			
		||||
            tmp = closed
 | 
			
		||||
            closed &= (closed-1)
 | 
			
		||||
            pr -= R[tmp-closed]
 | 
			
		||||
            m -= 1
 | 
			
		||||
        return c
 | 
			
		||||
 | 
			
		||||
    def is_goal2(node):
 | 
			
		||||
        _, _, min1, min2, closed = node
 | 
			
		||||
        return min1 == 0 and min2 == 0 or closed == all_open
 | 
			
		||||
 | 
			
		||||
    def neighbors2(node):
 | 
			
		||||
        v1, v2, min1, min2, closed = node
 | 
			
		||||
 | 
			
		||||
        if min(min1,min2) not in info:
 | 
			
		||||
            print(info)
 | 
			
		||||
        info.setdefault(min1, 0)
 | 
			
		||||
        info.setdefault(min2, 0)
 | 
			
		||||
        info[min1] += 1
 | 
			
		||||
        info[min2] += 1
 | 
			
		||||
 | 
			
		||||
        if min1 <= 0 and min2 <= 0:
 | 
			
		||||
            pass
 | 
			
		||||
        elif closed == all_open:
 | 
			
		||||
            yield 0, (v1, v2, 0, 0, closed)
 | 
			
		||||
        else:
 | 
			
		||||
            moved = False
 | 
			
		||||
            # either player can move
 | 
			
		||||
            # but we can't open a valve that would take less time to open
 | 
			
		||||
            # than the other player has already used. we've already paid
 | 
			
		||||
            # the cost for _not_ opening those valves and we can't
 | 
			
		||||
            # retroactively change that (no time travel)
 | 
			
		||||
 | 
			
		||||
            # if both players are in the same spot and have the same amount of
 | 
			
		||||
            # time remaining, then only let one of them move in order to break
 | 
			
		||||
            # symmetries (this can only happen in the start state)
 | 
			
		||||
 | 
			
		||||
            # TODO: are there more symmetries we can break?
 | 
			
		||||
 | 
			
		||||
            # move to a closed valve and open it
 | 
			
		||||
            discount1 = max(min1-min2, 0)
 | 
			
		||||
            for e, dist in W[v1]:
 | 
			
		||||
                t = dist + 1
 | 
			
		||||
                if e&closed and discount1 <= t <= min1:
 | 
			
		||||
                    moved = True
 | 
			
		||||
                    c = pressure(closed)*(t-discount1)
 | 
			
		||||
                    yield c, (e, v2, min1-t, min2, closed&~e)
 | 
			
		||||
            if (v1, min1) != (v2, min2):
 | 
			
		||||
                discount2 = max(min2-min1, 0)
 | 
			
		||||
                for e, dist in W[v2]:
 | 
			
		||||
                    t = dist + 1
 | 
			
		||||
                    if e&closed and discount2 <= t <= min2:
 | 
			
		||||
                        moved = True
 | 
			
		||||
                        c = pressure(closed)*(t-discount2)
 | 
			
		||||
                        yield c, (v1, e, min1, min2-t, closed&~e)
 | 
			
		||||
 | 
			
		||||
            # are there no moves left?
 | 
			
		||||
            # then wait out the timer
 | 
			
		||||
            if not moved:
 | 
			
		||||
                yield pressure(closed)*min(min1,min2), (v1, v2, 0, 0, closed)
 | 
			
		||||
 | 
			
		||||
    minutes = 26
 | 
			
		||||
    start2 = (AA, AA, minutes, minutes, all_closed)
 | 
			
		||||
    info.clear()
 | 
			
		||||
    c, _, path = astar.search(start2, is_goal2, neighbors2, heuristic2)
 | 
			
		||||
    print(c)
 | 
			
		||||
    print(max_pressure*minutes - c)
 | 
			
		||||
 | 
			
		||||
solve()
 | 
			
		||||
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										122
									
								
								day17/sol.py
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								day17/sol.py
									
									
									
									
									
								
							@ -1,122 +0,0 @@
 | 
			
		||||
import itertools
 | 
			
		||||
rocks = [
 | 
			
		||||
    0b00011110,
 | 
			
		||||
    0b00001000_00011100_00001000,
 | 
			
		||||
    0b00000100_00000100_00011100,
 | 
			
		||||
    0b00010000_00010000_00010000_00010000,
 | 
			
		||||
    0b00011000_00011000,
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
wall = 0b10000000_10000000_10000000_10000000_10000000
 | 
			
		||||
 | 
			
		||||
jets = ">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>"
 | 
			
		||||
jets = open("input").read().strip()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def ceil(n):
 | 
			
		||||
    if n%8 != 0:
 | 
			
		||||
        n += 8 - n%8
 | 
			
		||||
    return n
 | 
			
		||||
 | 
			
		||||
def drop(a, r, jets):
 | 
			
		||||
    h = ceil(a.bit_length()) + 8*3
 | 
			
		||||
    numjets = 0
 | 
			
		||||
    while True:
 | 
			
		||||
        # push left or right
 | 
			
		||||
        j = next(jets)
 | 
			
		||||
        if j == '<':
 | 
			
		||||
            x = (r<<1)
 | 
			
		||||
            if x & wall == 0:
 | 
			
		||||
                if x & (a>>h) == 0:
 | 
			
		||||
                    r = x
 | 
			
		||||
        elif j == '>':
 | 
			
		||||
            x = (r<<7)
 | 
			
		||||
            if x & wall == 0:
 | 
			
		||||
                x >>= 8
 | 
			
		||||
                if x & (a>>h) == 0:
 | 
			
		||||
                    r = x
 | 
			
		||||
        numjets += 1
 | 
			
		||||
 | 
			
		||||
        # continue falling?
 | 
			
		||||
        if r & (a>>(h-8)) == 0:
 | 
			
		||||
            h -= 8
 | 
			
		||||
            assert h > 0
 | 
			
		||||
        else:
 | 
			
		||||
            # stop
 | 
			
		||||
            a |= (r<<h)
 | 
			
		||||
            return a, numjets
 | 
			
		||||
 | 
			
		||||
def head(a):
 | 
			
		||||
    # technically this isn't sufficient.
 | 
			
		||||
    # you could imagine a stack of overhangs like
 | 
			
		||||
    #
 | 
			
		||||
    #  |#     #|
 | 
			
		||||
    #  |####  #|
 | 
			
		||||
    #  |#     #|
 | 
			
		||||
    #  |#     #|
 | 
			
		||||
    #  |#   ###|
 | 
			
		||||
    #  |#     #|
 | 
			
		||||
    #
 | 
			
		||||
    # that a rock might be able to navigate through indefinitely,
 | 
			
		||||
    # but our stacks are much messier than that, so no need to worry.
 | 
			
		||||
    #
 | 
			
		||||
    # we could probably just take the top 10 or so rows and call it good.
 | 
			
		||||
    h = ceil(a.bit_length())
 | 
			
		||||
    x = 0x80
 | 
			
		||||
    while h >= 8:
 | 
			
		||||
        h -= 8
 | 
			
		||||
        x |= (a>>h)&0xff
 | 
			
		||||
        if x == 0xff:
 | 
			
		||||
            return a>>h
 | 
			
		||||
    return a
 | 
			
		||||
 | 
			
		||||
def height(a):
 | 
			
		||||
    return ceil(a.bit_length())//8
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Part 1
 | 
			
		||||
a = 0b11111111
 | 
			
		||||
jeti = itertools.cycle(jets)
 | 
			
		||||
rocki = itertools.cycle(rocks)
 | 
			
		||||
for _ in range(2022):
 | 
			
		||||
    r = next(rocki)
 | 
			
		||||
    a, _ = drop(a,r,jeti)
 | 
			
		||||
print(height(a>>8))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Part 2
 | 
			
		||||
a = 0b11111111
 | 
			
		||||
stopped = 0
 | 
			
		||||
jeti = itertools.cycle(jets)
 | 
			
		||||
rocki = itertools.cycle(rocks)
 | 
			
		||||
i = 0
 | 
			
		||||
j = 0
 | 
			
		||||
seen = dict()
 | 
			
		||||
 | 
			
		||||
try:
 | 
			
		||||
    while True:
 | 
			
		||||
        r = next(rocki)
 | 
			
		||||
        a, n = drop(a,r,jeti)
 | 
			
		||||
        stopped += 1
 | 
			
		||||
        i = (i+1) % len(rocks)
 | 
			
		||||
        j = (j+n) % len(jets)
 | 
			
		||||
        if (i,j,head(a)) in seen:
 | 
			
		||||
            break
 | 
			
		||||
        seen[(i,j,head(a))] = (a,stopped)
 | 
			
		||||
        #for i in reversed(range(0,a.bit_length(),8)):
 | 
			
		||||
        #    print("{:08b}".format((a>>i)&0xff))
 | 
			
		||||
        #print()
 | 
			
		||||
except:
 | 
			
		||||
    print(i, j, stopped)
 | 
			
		||||
    raise
 | 
			
		||||
 | 
			
		||||
b, old = seen[(i,j,head(a))]
 | 
			
		||||
sdiff = stopped - old
 | 
			
		||||
hdiff = height(a) - height(b)
 | 
			
		||||
 | 
			
		||||
goal = 10**12
 | 
			
		||||
rounds, extra = divmod(goal - stopped, sdiff)
 | 
			
		||||
for _ in range(extra):
 | 
			
		||||
    r = next(rocki)
 | 
			
		||||
    a, n = drop(a,r,jeti)
 | 
			
		||||
print(rounds*hdiff + height(a)-1)
 | 
			
		||||
							
								
								
									
										11
									
								
								lib/astar.py
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								lib/astar.py
									
									
									
									
									
								
							@ -1,13 +1,10 @@
 | 
			
		||||
from heapq import heappush, heappop
 | 
			
		||||
 | 
			
		||||
def search(start, is_goal, neighbors, heuristic=None):
 | 
			
		||||
def search(start, goal, neighbors, heuristic=None):
 | 
			
		||||
    if heuristic == None:
 | 
			
		||||
        def heuristic(x):
 | 
			
		||||
            return 0
 | 
			
		||||
    if not callable(is_goal):
 | 
			
		||||
        goal = is_goal
 | 
			
		||||
        def is_goal(this):
 | 
			
		||||
            return this == goal
 | 
			
		||||
    # TODO: callable goal
 | 
			
		||||
    if not isinstance(start, list):
 | 
			
		||||
        start = [start]
 | 
			
		||||
    i = 0 # tiebreaker
 | 
			
		||||
@ -25,7 +22,7 @@ def search(start, is_goal, neighbors, heuristic=None):
 | 
			
		||||
 | 
			
		||||
        done[this] = (cost_so_far, prev)
 | 
			
		||||
 | 
			
		||||
        if is_goal(this):
 | 
			
		||||
        if this == goal:
 | 
			
		||||
            print("astar: visited", len(done), "nodes")
 | 
			
		||||
            # reconsruct the path
 | 
			
		||||
            n = this
 | 
			
		||||
@ -43,7 +40,7 @@ def search(start, is_goal, neighbors, heuristic=None):
 | 
			
		||||
                i += 1
 | 
			
		||||
                heappush(q, (c, i, n, this))
 | 
			
		||||
 | 
			
		||||
    return float('inf'), None, []
 | 
			
		||||
    return float('inf'), None
 | 
			
		||||
 | 
			
		||||
def test():
 | 
			
		||||
    data = [
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user