day11 - pypy needs 25m ...
This commit is contained in:
parent
f2334dec1d
commit
bd123ac952
200
day11.py
Normal file
200
day11.py
Normal file
@ -0,0 +1,200 @@
|
||||
from __future__ import annotations
|
||||
import itertools
|
||||
from collections import deque
|
||||
from enum import Enum
|
||||
from tools.aoc import AOCDay
|
||||
from typing import Any
|
||||
|
||||
|
||||
class ItemType(int, Enum):
|
||||
GENERATOR = 0
|
||||
MICROCHIP = 1
|
||||
|
||||
|
||||
class Item:
|
||||
def __init__(self, element: str, typ: ItemType):
|
||||
self.element = element
|
||||
self.type = typ
|
||||
|
||||
def compatible(self, other: Item | Generator | Microchip) -> bool:
|
||||
if self.type == other.type or self.element == other.element:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return "%s(%s)" % (self.__class__.__name__, self.element)
|
||||
|
||||
def __repr__(self):
|
||||
return str(self)
|
||||
|
||||
|
||||
class Generator(Item):
|
||||
def __init__(self, element: str):
|
||||
super().__init__(element, ItemType.GENERATOR)
|
||||
|
||||
|
||||
class Microchip(Item):
|
||||
def __init__(self, element: str):
|
||||
super().__init__(element, ItemType.MICROCHIP)
|
||||
|
||||
|
||||
def is_valid(group: list[Generator | Microchip]) -> bool:
|
||||
generators = set()
|
||||
microchips = set()
|
||||
for item in group:
|
||||
if item.type == ItemType.GENERATOR:
|
||||
generators.add(item.element)
|
||||
else:
|
||||
microchips.add(item.element)
|
||||
|
||||
if not generators or not microchips:
|
||||
return True
|
||||
|
||||
return all(m in generators for m in microchips)
|
||||
|
||||
|
||||
def get_legal_moves(
|
||||
from_floor: int, floors: list[list[Generator | Microchip]], debug: bool = False
|
||||
) -> set[tuple[int, Microchip | Generator, ...]]:
|
||||
targets = []
|
||||
if 0 < from_floor:
|
||||
targets.append(from_floor - 1)
|
||||
if from_floor < len(floors) - 1:
|
||||
targets.append(from_floor + 1)
|
||||
|
||||
moves = set()
|
||||
if len(floors[from_floor]) == 1:
|
||||
for target in targets:
|
||||
if is_valid(floors[target] + [floors[from_floor][0]]):
|
||||
moves.add((target, floors[from_floor][0]))
|
||||
else:
|
||||
for a, b in itertools.combinations(floors[from_floor], 2):
|
||||
if not a.compatible(b):
|
||||
continue
|
||||
|
||||
for target in targets:
|
||||
if is_valid(floors[target] + [a]):
|
||||
moves.add((target, a))
|
||||
if is_valid(floors[target] + [b]):
|
||||
moves.add((target, b))
|
||||
if is_valid(floors[target] + [a, b]):
|
||||
moves.add((target, a, b))
|
||||
|
||||
return moves
|
||||
|
||||
|
||||
def move_items(
|
||||
floors: list[list[Generator | Microchip]], from_floor: int, move: tuple[int, Microchip | Generator, ...]
|
||||
) -> None:
|
||||
to_floor = move[0]
|
||||
for item in move[1:]:
|
||||
floors[from_floor].remove(item)
|
||||
floors[to_floor].append(item)
|
||||
|
||||
|
||||
def floor_hash(floor: int, floors: list[list[Generator | Microchip]]) -> str:
|
||||
return (
|
||||
str(floor)
|
||||
+ "@"
|
||||
+ ";".join(
|
||||
"%d:%s"
|
||||
% (
|
||||
i,
|
||||
",".join(
|
||||
x.__class__.__name__[0] + x.element[0]
|
||||
for x in sorted(floors[i], key=lambda f: (f.__class__.__name__, f.element))
|
||||
),
|
||||
)
|
||||
for i in range(len(floors))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def copy_floors(floors: list[list[Generator | Microchip]]) -> list[list[Generator | Microchip]]:
|
||||
return [list(floors[x]) for x in range(len(floors))]
|
||||
|
||||
|
||||
def all_on_floor(floors: list[list[Generator | Microchip]], target_floor: int) -> bool:
|
||||
all_floors_empty = True
|
||||
for i in range(len(floors)):
|
||||
if i != target_floor and len(floors[i]) > 0:
|
||||
all_floors_empty = False
|
||||
|
||||
return all_floors_empty
|
||||
|
||||
|
||||
def get_min_steps(floors: list[list[Generator | Microchip]]) -> int:
|
||||
q = deque([(0, 3, floors)])
|
||||
seen = set()
|
||||
while q:
|
||||
dist, cur_floor_id, cur_floors = q.popleft()
|
||||
if cur_floor_id == 0 and all_on_floor(cur_floors, cur_floor_id):
|
||||
return dist
|
||||
|
||||
cur_floor_hash = floor_hash(cur_floor_id, cur_floors)
|
||||
if cur_floor_hash in seen:
|
||||
continue
|
||||
seen.add(cur_floor_hash)
|
||||
|
||||
for move in get_legal_moves(cur_floor_id, cur_floors):
|
||||
nxt_floors = copy_floors(cur_floors)
|
||||
move_items(nxt_floors, cur_floor_id, move)
|
||||
q.append((dist + 1, move[0], nxt_floors))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
class Day(AOCDay):
|
||||
inputs = [
|
||||
[
|
||||
(11, "input11_test"),
|
||||
(37, "input11"),
|
||||
],
|
||||
[
|
||||
(61, "input11"),
|
||||
],
|
||||
]
|
||||
|
||||
def parse_input(self, p2: bool = False) -> list[list[Generator | Microchip]]:
|
||||
floors = []
|
||||
for line in reversed(self.getInput()):
|
||||
_, contents = line[:-1].split(" contains ")
|
||||
if contents == "nothing relevant":
|
||||
floors.append([])
|
||||
continue
|
||||
|
||||
floor_contents = []
|
||||
contents = contents.replace(", and ", ", ")
|
||||
contents = contents.replace(" and ", ", ")
|
||||
contents = contents.split(", ")
|
||||
for content in contents:
|
||||
if not content:
|
||||
continue
|
||||
_, element, typ = content.split()
|
||||
if typ == "generator":
|
||||
floor_contents.append(Generator(element))
|
||||
else:
|
||||
element = element.split("-")[0]
|
||||
floor_contents.append(Microchip(element))
|
||||
|
||||
floors.append(floor_contents)
|
||||
|
||||
if p2:
|
||||
floors[3].append(Generator("elerium"))
|
||||
floors[3].append(Microchip("elerium"))
|
||||
floors[3].append(Generator("dilithium"))
|
||||
floors[3].append(Microchip("dilithium"))
|
||||
|
||||
return floors
|
||||
|
||||
def part1(self) -> Any:
|
||||
return get_min_steps(self.parse_input())
|
||||
|
||||
def part2(self) -> Any:
|
||||
return get_min_steps(self.parse_input(p2=True))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
day = Day(2016, 11)
|
||||
day.run(verbose=True)
|
||||
4
inputs/input11
Normal file
4
inputs/input11
Normal file
@ -0,0 +1,4 @@
|
||||
The first floor contains a strontium generator, a strontium-compatible microchip, a plutonium generator, and a plutonium-compatible microchip.
|
||||
The second floor contains a thulium generator, a ruthenium generator, a ruthenium-compatible microchip, a curium generator, and a curium-compatible microchip.
|
||||
The third floor contains a thulium-compatible microchip.
|
||||
The fourth floor contains nothing relevant.
|
||||
4
inputs/input11_test
Normal file
4
inputs/input11_test
Normal file
@ -0,0 +1,4 @@
|
||||
The first floor contains a hydrogen-compatible microchip and a lithium-compatible microchip.
|
||||
The second floor contains a hydrogen generator.
|
||||
The third floor contains a lithium generator.
|
||||
The fourth floor contains nothing relevant.
|
||||
Loading…
Reference in New Issue
Block a user