day19 - p2 takes about 1,5h
This commit is contained in:
parent
e10d256638
commit
265f481472
86
day19.py
86
day19.py
@ -1,7 +1,10 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from heapq import heappush, heappop
|
||||||
from tools.aoc import AOCDay
|
from tools.aoc import AOCDay
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
from tools.int_seq import triangular
|
||||||
|
|
||||||
|
|
||||||
class Material(Enum):
|
class Material(Enum):
|
||||||
ORE = 1
|
ORE = 1
|
||||||
@ -17,12 +20,6 @@ class Robot:
|
|||||||
self.min_viable = min_viable
|
self.min_viable = min_viable
|
||||||
|
|
||||||
|
|
||||||
class Blueprint:
|
|
||||||
def __init__(self, bp_id: int, robots: dict):
|
|
||||||
self.bp_id = bp_id
|
|
||||||
self.robots = robots
|
|
||||||
|
|
||||||
|
|
||||||
class Inventory:
|
class Inventory:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.robots = {
|
self.robots = {
|
||||||
@ -43,19 +40,10 @@ class Inventory:
|
|||||||
for mat, amount in self.robots.items():
|
for mat, amount in self.robots.items():
|
||||||
self.materials[mat] += amount
|
self.materials[mat] += amount
|
||||||
|
|
||||||
def queue_build_robot(self, robot: Robot) -> None:
|
def build_robot(self, robot: Robot) -> None:
|
||||||
for cost_mat, cost_amount in robot.costs.items():
|
for cost_mat, cost_amount in robot.costs.items():
|
||||||
self.materials[cost_mat] -= cost_amount
|
self.materials[cost_mat] -= cost_amount
|
||||||
self.__build_queue.append(robot)
|
|
||||||
|
|
||||||
def build_queued_robots(self) -> None:
|
|
||||||
for robot in self.__build_queue:
|
|
||||||
self.robots[robot.produces] += 1
|
self.robots[robot.produces] += 1
|
||||||
self.__build_queue = []
|
|
||||||
|
|
||||||
def build_robot(self, robot: Robot) -> None:
|
|
||||||
self.queue_build_robot(robot)
|
|
||||||
self.build_queued_robots()
|
|
||||||
|
|
||||||
def copy(self) -> 'Inventory':
|
def copy(self) -> 'Inventory':
|
||||||
new_inv = Inventory()
|
new_inv = Inventory()
|
||||||
@ -63,10 +51,27 @@ class Inventory:
|
|||||||
new_inv.materials = self.materials.copy()
|
new_inv.materials = self.materials.copy()
|
||||||
return new_inv
|
return new_inv
|
||||||
|
|
||||||
|
def __lt__(self, other: 'Inventory') -> bool:
|
||||||
|
return self.materials[Material.GEODE] < other.materials[Material.GEODE]
|
||||||
|
|
||||||
|
|
||||||
|
class Blueprint:
|
||||||
|
def __init__(self, bp_id: int, robots: dict):
|
||||||
|
self.bp_id = bp_id
|
||||||
|
self.robots = robots
|
||||||
|
self.max_cost = {
|
||||||
|
Material.ORE: max(robot.costs[Material.ORE] for _, robot in self.robots.items()),
|
||||||
|
Material.CLAY: self.robots[Material.OBSIDIAN].costs[Material.CLAY],
|
||||||
|
Material.OBSIDIAN: self.robots[Material.GEODE].costs[Material.OBSIDIAN],
|
||||||
|
Material.GEODE: 1e9
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def pass_minute(bp: Blueprint, inv: Inventory, ignore: list) -> (Inventory, list):
|
def pass_minute(bp: Blueprint, inv: Inventory, ignore: list) -> (Inventory, list):
|
||||||
could_build = []
|
could_build = []
|
||||||
for mat in Material:
|
for mat in Material:
|
||||||
|
if inv.robots[mat] >= bp.max_cost[mat]:
|
||||||
|
continue
|
||||||
for robot_mat, robot_cost in bp.robots[mat].costs.items():
|
for robot_mat, robot_cost in bp.robots[mat].costs.items():
|
||||||
if inv.materials[robot_mat] < robot_cost:
|
if inv.materials[robot_mat] < robot_cost:
|
||||||
break
|
break
|
||||||
@ -79,31 +84,32 @@ def pass_minute(bp: Blueprint, inv: Inventory, ignore: list) -> (Inventory, list
|
|||||||
return inv, could_build
|
return inv, could_build
|
||||||
|
|
||||||
|
|
||||||
def get_quality(bp: Blueprint, inv: Inventory, ignore: list, time_left: int = 24) -> int:
|
def get_bfs_quality(bp: Blueprint, max_time: int = 24) -> int:
|
||||||
for m in range(time_left):
|
q = []
|
||||||
inv, could_build = pass_minute(bp, inv, ignore)
|
max_geode = 0
|
||||||
|
heappush(q, (max_time, Inventory(), [], 0))
|
||||||
|
while q:
|
||||||
|
time_left, inv, ignore, no_build = heappop(q)
|
||||||
|
|
||||||
|
inv, could_build = pass_minute(bp, inv, ignore)
|
||||||
|
time_left -= 1
|
||||||
|
|
||||||
|
if time_left == 0:
|
||||||
|
if inv.materials[Material.GEODE] > max_geode:
|
||||||
|
max_geode = inv.materials[Material.GEODE]
|
||||||
|
continue
|
||||||
|
|
||||||
if could_build and time_left - m > 1:
|
|
||||||
max_quality = 0
|
|
||||||
build_a_bot = False
|
|
||||||
for robot in could_build:
|
for robot in could_build:
|
||||||
if robot.min_viable > time_left - m:
|
if robot.min_viable > time_left:
|
||||||
continue
|
continue
|
||||||
sub_inv = inv.copy()
|
sub_inv = inv.copy()
|
||||||
sub_inv.build_robot(robot)
|
sub_inv.build_robot(robot)
|
||||||
sub_quality = get_quality(bp, sub_inv, [], time_left - m - 1)
|
heappush(q, (time_left, sub_inv, [], 0))
|
||||||
if sub_quality > max_quality:
|
|
||||||
max_quality = sub_quality
|
|
||||||
build_a_bot = True
|
|
||||||
|
|
||||||
if build_a_bot:
|
if no_build < bp.max_cost[Material.ORE]:
|
||||||
sub_quality = get_quality(bp, inv, could_build, time_left - m - 1)
|
heappush(q, (time_left, inv, could_build, no_build + 1))
|
||||||
if sub_quality > max_quality:
|
|
||||||
max_quality = sub_quality
|
|
||||||
|
|
||||||
return max_quality
|
return max_geode
|
||||||
|
|
||||||
return inv.materials[Material.GEODE]
|
|
||||||
|
|
||||||
|
|
||||||
class Day(AOCDay):
|
class Day(AOCDay):
|
||||||
@ -113,8 +119,8 @@ class Day(AOCDay):
|
|||||||
(1616, "input19"),
|
(1616, "input19"),
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
(56*62, "input19_test"),
|
#(56*62, "input19_test"),
|
||||||
(None, "input19"),
|
(8990, "input19"),
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -143,18 +149,18 @@ class Day(AOCDay):
|
|||||||
blueprints = self.get_blueprints()
|
blueprints = self.get_blueprints()
|
||||||
score = 0
|
score = 0
|
||||||
for b in blueprints:
|
for b in blueprints:
|
||||||
quality = get_quality(b, Inventory(), [])
|
quality = get_bfs_quality(b)
|
||||||
print("BP", b.bp_id, "Q", quality)
|
|
||||||
score += quality * b.bp_id
|
score += quality * b.bp_id
|
||||||
|
print("BP", b.bp_id, "Q", quality, "S", score)
|
||||||
return score
|
return score
|
||||||
|
|
||||||
def part2(self) -> Any:
|
def part2(self) -> Any:
|
||||||
blueprints = self.get_blueprints()
|
blueprints = self.get_blueprints()
|
||||||
score = 1
|
score = 1
|
||||||
for b in blueprints[:3]:
|
for b in blueprints[:3]:
|
||||||
quality = get_quality(b, Inventory(), [], 32)
|
quality = get_bfs_quality(b, 32)
|
||||||
print("BP", b.bp_id, "Q", quality)
|
|
||||||
score *= quality
|
score *= quality
|
||||||
|
print("BP", b.bp_id, "Q", quality, "S", score)
|
||||||
return score
|
return score
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user