adventofcode2022/day19/sol.py

144 lines
4.5 KiB
Python
Raw Normal View History

2022-12-19 10:51:40 +00:00
import math
2022-12-19 07:08:55 +00:00
import re
2022-12-19 19:42:47 +00:00
2022-12-19 07:08:55 +00:00
sample = {
1:{
2022-12-19 19:42:47 +00:00
'ore': [4, 0, 0, 0],
'clay': [2, 0, 0, 0],
'obsidian': [3, 14, 0, 0],
'geode': [2, 0, 7, 0],
},
2: {
'ore': [2, 0, 0, 0],
'clay': [3, 0, 0, 0],
'obsidian': [3, 8, 0, 0],
'geode': [3, 0, 12, 0],
}
}
robot_number = {'ore': 0, 'clay': 1, 'obsidian': 2, 'geode': 3}
2022-12-19 07:08:55 +00:00
def parse(line):
line = re.sub(r'[^\d]+', ' ', line)
idx, ore1, ore2, ore3, clay3, ore4, obs4 = map(int, line.split())
return idx, {
'ore': [ore1, 0,0,0],
'clay': [ore2, 0,0,0],
'obsidian': [ore3,clay3,0,0],
'geode': [ore4,0,obs4,0],
}
2022-12-19 19:15:09 +00:00
def simulate(blueprint, minutes=24):
2022-12-19 19:42:47 +00:00
rmax = [max(x[i] for x in blueprint.values()) for i in range(3)] + [9999]
2022-12-19 07:08:55 +00:00
2022-12-19 19:42:47 +00:00
limit = None
if minutes >= 32:
limit = 100000
2022-12-19 07:08:55 +00:00
2022-12-19 19:42:47 +00:00
items = list(blueprint.items())
items.reverse()
2022-12-19 07:08:55 +00:00
2022-12-19 19:42:47 +00:00
print(items)
2022-12-19 09:55:09 +00:00
2022-12-19 10:51:40 +00:00
buckets = [[] for _ in range(minutes+1)]
def enqueue(t, robots, resources):
2022-12-19 19:42:47 +00:00
assert t > 0
if t <= 0 or t >= len(buckets):
2022-12-19 10:51:40 +00:00
return
x = resources[3]
buckets[t].append((x,robots, resources))
2022-12-19 19:42:47 +00:00
enqueue(minutes, robots=[1,0,0,0], resources=[0,0,0,0])
2022-12-19 10:51:40 +00:00
max_geodes = 0
2022-12-19 19:42:47 +00:00
2022-12-19 07:08:55 +00:00
for _ in range(minutes):
2022-12-19 10:51:40 +00:00
buckets[minutes].sort(reverse=True)
2022-12-19 19:42:47 +00:00
#print(minutes, buckets[minutes][:1])
for _, robots, resources in buckets[minutes][:limit]:
2022-12-19 20:51:41 +00:00
if 1:
2022-12-19 10:51:40 +00:00
geodes = robots[3]*minutes + resources[3]
if geodes > max_geodes:
max_geodes = geodes
2022-12-19 20:51:41 +00:00
elif geodes < max_geodes:
# if the number of geodes we could get if we stopped building robots
# is less than the max such number we've seen so far,
# then prune this node.
#
# this seems like it would be too greedy a rule, but
# somehow it actually works. (i think this is equivalant to
# the rule "always build a geode robot as soon as you can"
# and its validity depends on the input data. (in fact it
# *doesn't* work for the first sample blueprint for part 2.
# but it works on my input so w/e)
#
# decreases the search space enormously
continue
2022-12-19 19:42:47 +00:00
for robot, cost in items:
2022-12-19 07:08:55 +00:00
i = robot_number[robot]
2022-12-19 10:51:40 +00:00
if robots[i] >= rmax[i]:
2022-12-19 09:55:09 +00:00
# don't build more of 1 robot than we can spend in 1 minute
continue
2022-12-19 20:51:41 +00:00
if robot != 'geode' and robots[i]*minutes + resources[i] >= minutes*rmax[i]:
# insight from reddit: if we have enough resources on hand and enough
# mining capacity to build any robot that needs it every turn until
# time is up, then we don't need to build any more of that kind of robot
#
# this provides a minor speed-up over just doing the simpler check
continue
2022-12-19 10:51:40 +00:00
# figure out how soon we can afford it
wait = 0
for x,y,r in zip(resources, cost, robots):
if y == 0:
continue
if r <= 0:
wait = 9999
break
if y > x:
wait = max(wait, int(math.ceil((y - x)/r)))
2022-12-19 19:42:47 +00:00
if wait+1 >= minutes:
2022-12-19 07:08:55 +00:00
continue
2022-12-19 10:51:40 +00:00
new_resources = [x+(wait+1)*r-y for x,y,r in zip(resources, cost, robots)]
2022-12-19 19:42:47 +00:00
new_robots = list(robots)
new_robots[i] += 1
2022-12-19 10:51:40 +00:00
enqueue(minutes-wait-1, new_robots, new_resources)
2022-12-19 07:08:55 +00:00
2022-12-19 19:42:47 +00:00
print(minutes, max_geodes, [len(x) for x in buckets[:minutes+1]])
buckets[minutes] = [] # clear old buckets to save memory
2022-12-19 07:08:55 +00:00
2022-12-19 10:51:40 +00:00
minutes -= 1
2022-12-19 07:08:55 +00:00
2022-12-19 10:51:40 +00:00
return max_geodes
2022-12-19 07:08:55 +00:00
input = {}
with open('input') as f:
for line in f:
idx, bp = parse(line)
input[idx] = bp
def solve(input):
t = 0
for idx, B in input.items():
g = simulate(B)
t += idx*g
2022-12-19 19:42:47 +00:00
print("blueprint", idx, "max geodes is", g)
2022-12-19 07:08:55 +00:00
print("---")
print(t)
return t
2022-12-19 19:15:09 +00:00
def solve2(input):
t = 1
for idx, B in input.items():
if idx <= 3:
g = simulate(B, minutes=32)
t *= g
print(t)
return t
2022-12-19 19:42:47 +00:00
assert solve(sample) == 33
2022-12-19 20:51:41 +00:00
solve(input)
2022-12-19 19:15:09 +00:00
solve2(input)
2022-12-19 07:08:55 +00:00