some final touches
This commit is contained in:
parent
0843624f17
commit
cf05072def
3 changed files with 77 additions and 101 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from abc import ABC, abstractmethod, abstractproperty
|
from abc import ABC, abstractmethod
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from itertools import product
|
from itertools import product
|
||||||
from queue import PriorityQueue
|
from queue import PriorityQueue
|
||||||
|
|
@ -81,21 +81,21 @@ class Actor:
|
||||||
class SystemProgress(ABC):
|
class SystemProgress(ABC):
|
||||||
max_time: int
|
max_time: int
|
||||||
prev_time: int
|
prev_time: int
|
||||||
next_time: int
|
time: int
|
||||||
pressure: int
|
pressure: int
|
||||||
flow_rate: int
|
flow_rate: int
|
||||||
closed_valves: frozenset[Valve]
|
closed_valves: frozenset[Valve]
|
||||||
path: str
|
path: frozenset[tuple[str, int, bool]]
|
||||||
|
|
||||||
def one_actor(self, actor: Actor) -> Iterator[Actor]:
|
def one_actor(self, actor: Actor) -> Iterator[Actor]:
|
||||||
if actor.finished or actor.next_time != self.next_time:
|
if actor.finished or actor.next_time != self.time:
|
||||||
yield actor
|
yield actor
|
||||||
elif not self.closed_valves:
|
elif not self.closed_valves:
|
||||||
yield Actor(actor.position, self.max_time, True)
|
yield Actor(actor.position, self.max_time, True)
|
||||||
else:
|
else:
|
||||||
reached_any_target = False
|
reached_any_target = False
|
||||||
for target in self.closed_valves:
|
for target in self.closed_valves:
|
||||||
finished = self.next_time + actor.position.travel_time(target.name) + 1
|
finished = self.time + actor.position.travel_time(target.name) + 1
|
||||||
if finished < self.max_time:
|
if finished < self.max_time:
|
||||||
reached_any_target = True
|
reached_any_target = True
|
||||||
yield Actor(target, finished, False)
|
yield Actor(target, finished, False)
|
||||||
|
|
@ -109,22 +109,19 @@ class SystemProgress(ABC):
|
||||||
match num_actors:
|
match num_actors:
|
||||||
case 1:
|
case 1:
|
||||||
return OneActorProgress(max_time, 0, 0, pressure, flow_rate, closed_valves,
|
return OneActorProgress(max_time, 0, 0, pressure, flow_rate, closed_valves,
|
||||||
start.name, Actor(start, 0, False))
|
frozenset(), Actor(start, 0, False))
|
||||||
case 2:
|
case 2:
|
||||||
return TwoActorProgress(max_time, 0, 0, pressure, flow_rate, closed_valves,
|
return TwoActorProgress(max_time, 0, 0, pressure, flow_rate, closed_valves,
|
||||||
start.name, Actor(start, 0, False), Actor(start, 0, False))
|
frozenset(), Actor(start, 0, False), Actor(start, 0, False))
|
||||||
case _:
|
case _:
|
||||||
assert False, "Unreachable"
|
assert False, "Unreachable"
|
||||||
|
|
||||||
def __lt__(self, other: OneActorProgress) -> bool:
|
def __lt__(self, other: OneActorProgress) -> bool:
|
||||||
mx = min(self.next_time, other.next_time)
|
if self.time != other.time:
|
||||||
return self.pressure_at_time(mx) > other.pressure_at_time(mx)
|
return self.time < other.time
|
||||||
|
return self.pressure > other.pressure
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def pressure_at_time(self, time: int) -> int:
|
|
||||||
...
|
|
||||||
|
|
||||||
@abstractproperty
|
|
||||||
def max_possible_pressure(self) -> int:
|
def max_possible_pressure(self) -> int:
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
@ -137,38 +134,29 @@ class SystemProgress(ABC):
|
||||||
class OneActorProgress(SystemProgress):
|
class OneActorProgress(SystemProgress):
|
||||||
actor: Actor
|
actor: Actor
|
||||||
|
|
||||||
def pressure_at_time(self, time: int) -> int:
|
|
||||||
pressure = self.pressure + self.flow_rate * (time - self.prev_time)
|
|
||||||
if time > self.next_time:
|
|
||||||
pressure += self.actor.position.flow_rate * (time - self.actor.next_time + 1)
|
|
||||||
return pressure
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_possible_pressure(self) -> int:
|
def max_possible_pressure(self) -> int:
|
||||||
closed = sum(valve.flow_rate for valve in self.closed_valves)
|
closed = sum(valve.flow_rate for valve in self.closed_valves)
|
||||||
return self.pressure + (self.flow_rate + self.actor.position.flow_rate
|
return self.pressure + (self.flow_rate + closed) * (self.max_time - self.time)
|
||||||
+ closed) * (self.max_time - self.next_time)
|
|
||||||
|
|
||||||
def open_valves(self) -> Iterator[SystemProgress]:
|
def open_valves(self) -> Iterator[SystemProgress]:
|
||||||
flow_rate = self.flow_rate + self.actor.position.flow_rate
|
|
||||||
pressure = self.pressure + self.flow_rate * (self.next_time - self.prev_time)
|
|
||||||
for actor in self.one_actor(self.actor):
|
for actor in self.one_actor(self.actor):
|
||||||
closed_valves = self.closed_valves
|
closed_valves = self.closed_valves
|
||||||
if not actor.finished:
|
if not actor.finished:
|
||||||
closed_valves = closed_valves.difference({actor.position})
|
closed_valves = closed_valves.difference({actor.position})
|
||||||
path = f"{self.path} {pressure=}\n" \
|
flow_rate = self.flow_rate + actor.position.flow_rate
|
||||||
f"1: {actor.next_time}->{actor.position.name}\n" \
|
else:
|
||||||
f" +{actor.position.flow_rate}\n"
|
flow_rate = self.flow_rate
|
||||||
yield OneActorProgress(
|
next = OneActorProgress(
|
||||||
max_time=self.max_time,
|
max_time=self.max_time,
|
||||||
prev_time=self.next_time,
|
prev_time=self.time,
|
||||||
next_time=actor.next_time,
|
time=actor.next_time,
|
||||||
flow_rate=flow_rate,
|
flow_rate=flow_rate,
|
||||||
pressure=pressure,
|
pressure=self.pressure + self.flow_rate * (actor.next_time - self.time),
|
||||||
closed_valves=closed_valves,
|
closed_valves=closed_valves,
|
||||||
actor=actor,
|
actor=actor,
|
||||||
path=path
|
path=self.path | {(actor.position.name, actor.next_time, True)}
|
||||||
)
|
)
|
||||||
|
yield next
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True, frozen=True)
|
@dataclass(slots=True, frozen=True)
|
||||||
|
|
@ -176,75 +164,68 @@ class TwoActorProgress(SystemProgress):
|
||||||
actor1: Actor
|
actor1: Actor
|
||||||
actor2: Actor
|
actor2: Actor
|
||||||
|
|
||||||
def pressure_at_time(self, time: int) -> int:
|
|
||||||
pressure = self.pressure + self.flow_rate * (time - self.prev_time)
|
|
||||||
if time > self.next_time:
|
|
||||||
if self.actor1.next_time <= time:
|
|
||||||
pressure += self.actor1.position.flow_rate * (time - self.actor1.next_time + 1)
|
|
||||||
if self.actor2.next_time <= time:
|
|
||||||
pressure += self.actor2.position.flow_rate * (time - self.actor2.next_time + 1)
|
|
||||||
return pressure
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_possible_pressure(self) -> int:
|
def max_possible_pressure(self) -> int:
|
||||||
closed = sum(valve.flow_rate for valve in self.closed_valves) * \
|
closed = sum(valve.flow_rate for valve in self.closed_valves)
|
||||||
(self.max_time - self.next_time - 1)
|
|
||||||
flow = self.flow_rate * (self.max_time - self.next_time - 1)
|
other = 0
|
||||||
if not self.actor1.finished:
|
if self.actor1.next_time != self.time and not self.actor1.finished:
|
||||||
actor1 = self.actor1.position.flow_rate * (self.max_time - self.actor1.next_time)
|
other += self.actor1.position.flow_rate * (self.max_time - self.actor1.next_time)
|
||||||
else:
|
if self.actor2.next_time != self.time and not self.actor2.finished:
|
||||||
actor1 = 0
|
other += self.actor2.position.flow_rate * (self.max_time - self.actor2.next_time)
|
||||||
if not self.actor2.finished:
|
|
||||||
actor2 = self.actor2.position.flow_rate * (self.max_time - self.actor2.next_time)
|
return self.pressure + (self.flow_rate + closed) * (self.max_time - self.time) + other
|
||||||
else:
|
|
||||||
actor2 = 0
|
|
||||||
return self.pressure + actor1 + actor2 + closed + flow
|
|
||||||
|
|
||||||
def open_valves(self) -> Iterator[SystemProgress]:
|
def open_valves(self) -> Iterator[SystemProgress]:
|
||||||
pressure = self.pressure + self.flow_rate * (self.next_time - self.prev_time)
|
if self.actor1.next_time == self.time:
|
||||||
|
actor1_actions = self.one_actor(self.actor1)
|
||||||
|
else:
|
||||||
|
actor1_actions = [self.actor1]
|
||||||
|
|
||||||
flow_rate = self.flow_rate
|
if self.actor2.next_time == self.time:
|
||||||
if self.actor1.next_time == self.next_time:
|
actor2_actions = self.one_actor(self.actor2)
|
||||||
flow_rate += self.actor1.position.flow_rate
|
else:
|
||||||
if self.actor2.next_time == self.next_time:
|
actor2_actions = [self.actor2]
|
||||||
flow_rate += self.actor2.position.flow_rate
|
|
||||||
|
|
||||||
actor1_actions = list(self.one_actor(self.actor1))
|
|
||||||
actor2_actions = list(self.one_actor(self.actor2))
|
|
||||||
|
|
||||||
for actor1, actor2 in product(actor1_actions, actor2_actions):
|
for actor1, actor2 in product(actor1_actions, actor2_actions):
|
||||||
if not actor1.finished and not actor2.finished and actor1.position == actor2.position:
|
if not actor1.finished and not actor2.finished and actor1.position == actor2.position:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
closed_valves = self.closed_valves
|
closed_valves = self.closed_valves
|
||||||
if not actor1.finished:
|
flow_rate = self.flow_rate
|
||||||
closed_valves = closed_valves.difference({actor1.position})
|
|
||||||
if not actor2.finished:
|
|
||||||
closed_valves = closed_valves.difference({actor2.position})
|
|
||||||
|
|
||||||
next_time = min(actor1.next_time, actor2.next_time)
|
next_time = min(actor1.next_time, actor2.next_time)
|
||||||
|
|
||||||
path = self.path
|
path: set[tuple[str, int, bool]] = set(self.path)
|
||||||
next_flow = 0
|
|
||||||
if actor1.next_time == next_time:
|
|
||||||
path += f" -> (S:{actor1.next_time}/{actor1.position.name})"
|
|
||||||
next_flow += actor1.position.flow_rate
|
|
||||||
if actor2.next_time == next_time:
|
|
||||||
path += f" -> (E:{actor2.next_time}/{actor2.position.name})"
|
|
||||||
next_flow += actor2.position.flow_rate
|
|
||||||
next_flow = 0
|
|
||||||
|
|
||||||
yield TwoActorProgress(
|
if not actor1.finished:
|
||||||
|
closed_valves = closed_valves.difference({actor1.position})
|
||||||
|
if actor1.next_time == next_time:
|
||||||
|
flow_rate += actor1.position.flow_rate
|
||||||
|
path -= {(actor2.position.name, actor1.next_time, False)}
|
||||||
|
path.add((actor1.position.name, actor1.next_time, True))
|
||||||
|
else:
|
||||||
|
path.add((actor1.position.name, actor1.next_time, False))
|
||||||
|
|
||||||
|
if not actor2.finished:
|
||||||
|
closed_valves = closed_valves.difference({actor2.position})
|
||||||
|
if actor2.next_time == next_time:
|
||||||
|
flow_rate += actor2.position.flow_rate
|
||||||
|
path -= {(actor2.position.name, actor1.next_time, False)}
|
||||||
|
path.add((actor2.position.name, actor2.next_time, True))
|
||||||
|
else:
|
||||||
|
path.add((actor2.position.name, actor2.next_time, False))
|
||||||
|
|
||||||
|
next = TwoActorProgress(
|
||||||
max_time=self.max_time,
|
max_time=self.max_time,
|
||||||
prev_time=self.next_time,
|
prev_time=self.time,
|
||||||
next_time=next_time,
|
time=next_time,
|
||||||
flow_rate=flow_rate,
|
flow_rate=flow_rate,
|
||||||
pressure=pressure,
|
pressure=self.pressure + self.flow_rate * (next_time - self.time),
|
||||||
closed_valves=closed_valves,
|
closed_valves=closed_valves,
|
||||||
path=path,
|
|
||||||
actor1=actor1,
|
actor1=actor1,
|
||||||
actor2=actor2,
|
actor2=actor2,
|
||||||
|
path=frozenset(path)
|
||||||
)
|
)
|
||||||
|
yield next
|
||||||
|
|
||||||
|
|
||||||
@dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
|
|
@ -261,9 +242,7 @@ class Network:
|
||||||
for follow in raw.following:
|
for follow in raw.following:
|
||||||
current.following.append(valves[follow])
|
current.following.append(valves[follow])
|
||||||
|
|
||||||
network = Network(valves)
|
return Network(valves)
|
||||||
# network.create_paths()
|
|
||||||
return network
|
|
||||||
|
|
||||||
def under_pressure(self, minutes: int, number_actors: Literal[1] | Literal[2]) -> int:
|
def under_pressure(self, minutes: int, number_actors: Literal[1] | Literal[2]) -> int:
|
||||||
closed_valves = [valve for valve in self.valves.values() if valve.flow_rate > 0]
|
closed_valves = [valve for valve in self.valves.values() if valve.flow_rate > 0]
|
||||||
|
|
@ -277,23 +256,20 @@ class Network:
|
||||||
start=start,
|
start=start,
|
||||||
num_actors=number_actors
|
num_actors=number_actors
|
||||||
))
|
))
|
||||||
max_system: SystemProgress | None = None
|
max_pressure = 0
|
||||||
tick = 0
|
seen: set[frozenset[tuple[str, int, bool]]] = set()
|
||||||
max_counter = 0
|
|
||||||
while not queue.empty():
|
while not queue.empty():
|
||||||
tick += 1
|
|
||||||
current = queue.get()
|
current = queue.get()
|
||||||
if current.next_time == minutes:
|
if current.time == minutes:
|
||||||
if max_system is None or max_system.pressure < current.pressure:
|
return current.pressure
|
||||||
max_system = current
|
if max_pressure > current.max_possible_pressure():
|
||||||
max_counter += 1
|
|
||||||
continue
|
continue
|
||||||
|
if current.path in seen:
|
||||||
|
continue
|
||||||
|
seen.add(current.path)
|
||||||
|
max_pressure = max(max_pressure, current.pressure)
|
||||||
|
|
||||||
for next in current.open_valves():
|
for next in current.open_valves():
|
||||||
if max_system is None or next.max_possible_pressure >= max_system.pressure:
|
queue.put(next)
|
||||||
queue.put(next)
|
|
||||||
|
|
||||||
if max_system is None:
|
raise Exception("No best System found")
|
||||||
raise Exception("No best System found")
|
|
||||||
|
|
||||||
return max_system.pressure
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ class Cave:
|
||||||
old = self.cave[block.y]
|
old = self.cave[block.y]
|
||||||
self.cave[block.y] = old[:block.x] + '#' + old[block.x + 1:]
|
self.cave[block.y] = old[:block.x] + '#' + old[block.x + 1:]
|
||||||
|
|
||||||
@ classmethod
|
@classmethod
|
||||||
def create(cls, width: int, gas_pushes: str) -> Self:
|
def create(cls, width: int, gas_pushes: str) -> Self:
|
||||||
cave = []
|
cave = []
|
||||||
return cls(width, cave, cycle(gas_pushes), cycle(Pattern(pattern) for pattern in patterns))
|
return cls(width, cave, cycle(gas_pushes), cycle(Pattern(pattern) for pattern in patterns))
|
||||||
|
|
|
||||||
|
|
@ -304,7 +304,7 @@ class PasswordCubeJungle(PasswordJungle):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
@ dataclass(slots=True)
|
@dataclass(slots=True)
|
||||||
class PasswordSimpleJungle(PasswordJungle):
|
class PasswordSimpleJungle(PasswordJungle):
|
||||||
def wrap(self, player: Player) -> Player:
|
def wrap(self, player: Player) -> Player:
|
||||||
match player.facing:
|
match player.facing:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue