day19 made parallel and much quicker
This commit is contained in:
parent
43d510ee22
commit
cee40d1003
1 changed files with 31 additions and 8 deletions
|
|
@ -3,21 +3,35 @@ from dataclasses import dataclass
|
|||
from enum import IntEnum
|
||||
from itertools import islice
|
||||
from math import prod
|
||||
from multiprocessing import Pool
|
||||
from queue import PriorityQueue
|
||||
|
||||
from typing import Iterator, Self
|
||||
from typing import Iterable, Iterator, Self
|
||||
from advent.parser.parser import P
|
||||
|
||||
day_num = 19
|
||||
|
||||
|
||||
def part1(lines: Iterator[str]) -> int:
|
||||
return sum(blueprint.number * blueprint.run(24)
|
||||
for blueprint in (Blueprint.parse(line) for line in lines))
|
||||
return sum(bp.number * geodes for bp, geodes in Processor.pool_it(24, lines))
|
||||
|
||||
|
||||
def part2(lines: Iterator[str]) -> int:
|
||||
return prod(Blueprint.parse(line).run(32) for line in islice(lines, 3))
|
||||
return prod(num for _, num in Processor.pool_it(32, islice(lines, 3)))
|
||||
|
||||
|
||||
@dataclass(slots=True, frozen=True)
|
||||
class Processor:
|
||||
rounds: int
|
||||
|
||||
@classmethod
|
||||
def pool_it(cls, rounds: int, lines: Iterator[str]) -> Iterable[tuple[Blueprint, int]]:
|
||||
with Pool() as p:
|
||||
return p.map(Processor(rounds), lines)
|
||||
|
||||
def __call__(self, line: str) -> tuple[Blueprint, int]:
|
||||
blueprint = Blueprint.parse(line)
|
||||
return blueprint, blueprint.run(self.rounds)
|
||||
|
||||
|
||||
number_parser = P.second(P.string("Blueprint "), P.unsigned())
|
||||
|
|
@ -53,7 +67,7 @@ def sub(first: Elements, second: Elements) -> Elements:
|
|||
return Elements(tuple(v1 - v2 for v1, v2 in zip(first, second)))
|
||||
|
||||
|
||||
def inc_tuple(elements: Elements, pos: Element) -> Elements:
|
||||
def inc_element(elements: Elements, pos: Element) -> Elements:
|
||||
return tuple(v + 1 if num == pos else v for num, v in enumerate(elements))
|
||||
|
||||
|
||||
|
|
@ -70,25 +84,34 @@ class Path:
|
|||
return Path(0, (0, 0, 0, 0), (0, 0, 0, 1), blueprint, [])
|
||||
|
||||
def _check(self, element: Element) -> Path | None:
|
||||
if element != Element.Geode:
|
||||
max_needed = max(req[element] for req in self.blueprint.requirements)
|
||||
if self.robots[element] >= max_needed:
|
||||
return None
|
||||
|
||||
if gt(self.material, self.blueprint.requirements[element]):
|
||||
return Path(self.time + 1,
|
||||
add(sub(self.material, self.blueprint.requirements[element]), self.robots),
|
||||
inc_tuple(self.robots, element), self.blueprint, self.path + [element])
|
||||
inc_element(self.robots, element), self.blueprint, self.path + [element])
|
||||
else:
|
||||
return None
|
||||
|
||||
def find_next(self) -> Iterator[Path]:
|
||||
if (path := self._check(Element.Geode)) is not None:
|
||||
yield path
|
||||
|
||||
if self.blueprint.max_requirement(Element.Obsidian) >= self.material[Element.Obsidian]:
|
||||
if (path := self._check(Element.Obsidian)) is not None:
|
||||
yield path
|
||||
|
||||
if self.blueprint.max_requirement(Element.Clay) >= self.material[Element.Clay]:
|
||||
if (path := self._check(Element.Clay)) is not None:
|
||||
yield path
|
||||
|
||||
if self.blueprint.max_requirement(Element.Ore) >= self.material[Element.Ore]:
|
||||
if (path := self._check(Element.Ore)) is not None:
|
||||
yield path
|
||||
|
||||
yield Path(self.time + 1, add(self.material, self.robots), self.robots, self.blueprint,
|
||||
self.path + [None])
|
||||
|
||||
|
|
@ -127,8 +150,8 @@ class Blueprint:
|
|||
seen: dict[tuple[Elements, int], Elements] = {}
|
||||
while not queue.empty():
|
||||
current = queue.get()
|
||||
if ((current.robots, current.time) in seen
|
||||
and gt(seen[(current.robots, current.time)], current.material)):
|
||||
last_seen = seen.get((current.robots, current.time))
|
||||
if last_seen is not None and gt(last_seen, current.material):
|
||||
continue
|
||||
seen[(current.robots, current.time)] = current.material
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue