This commit is contained in:
Mikael CAPELLE 2022-12-19 18:46:16 +01:00
parent 01300e23b2
commit ddebd26db2

View File

@ -3,9 +3,13 @@
import heapq import heapq
import math import math
import sys import sys
import time
from collections import defaultdict from collections import defaultdict
from typing import Literal, TypedDict from typing import Literal, TypedDict
import numpy as np
from tqdm import tqdm
Reagent = Literal["ore", "clay", "obsidian", "geode"] Reagent = Literal["ore", "clay", "obsidian", "geode"]
REAGENTS: tuple[Reagent] = ( REAGENTS: tuple[Reagent] = (
"ore", "ore",
@ -63,7 +67,7 @@ class State:
def __lt__(self, other) -> bool: def __lt__(self, other) -> bool:
return isinstance(other, State) and tuple( return isinstance(other, State) and tuple(
(self.robots[r], self.reagents[r]) for r in REAGENTS (self.robots[r], self.reagents[r]) for r in REAGENTS
) < tuple((other.robots[r], other.reagents[r]) for r in REAGENTS) ) > tuple((other.robots[r], other.reagents[r]) for r in REAGENTS)
def __hash__(self) -> int: def __hash__(self) -> int:
return hash(tuple((self.robots[r], self.reagents[r]) for r in REAGENTS)) return hash(tuple((self.robots[r], self.reagents[r]) for r in REAGENTS))
@ -86,223 +90,244 @@ def dominates(lhs: State, rhs: State):
MAX_TIME = 24 MAX_TIME = 24
blueprint = blueprints[0] blueprint = blueprints[1]
parents: dict[State, tuple[State | None, int]] = {State(): (None, 0)} # parents: dict[State, tuple[State | None, int]] = {State(): (None, 0)}
queue = [(0, State())] # queue = [(0, State())]
visited: set[State] = set() # visited: set[State] = set()
at_time: dict[int, list[State]] = defaultdict(lambda: []) # at_time: dict[int, list[State]] = defaultdict(lambda: [])
while queue: # while queue:
time, state = heapq.heappop(queue) # time, state = heapq.heappop(queue)
if state in visited: # if state in visited:
continue
visited.add(state)
# if any(dominates(state_3, state) for state_3 in at_time[time]):
# continue # continue
at_time[time].append(state) # print(time, state)
if time > MAX_TIME: # visited.add(state)
continue
if len(queue) % 500 == 0: # at_time[time].append(state)
print(len(queue), len(visited), time)
can_build_any: bool = False # if time > MAX_TIME:
for reagent in REAGENTS: # continue
needed = blueprint[reagent]
if any(state.robots[r] == 0 for r in needed): # if len(queue) % 200 == 0:
continue # print(len(queue), len(visited), time)
time_to_complete = max( # can_build_any: bool = False
max( # for reagent in REAGENTS:
math.ceil((needed[r] - state.reagents[r]) / state.robots[r]) # needed = blueprint[reagent]
for r in needed
),
0,
)
if time + time_to_complete + 1 > MAX_TIME: # if any(state.robots[r] == 0 for r in needed):
continue # continue
wait = time_to_complete + 1 # time_to_complete = max(
# max(
# math.ceil((needed[r] - state.reagents[r]) / state.robots[r])
# for r in needed
# ),
# 0,
# )
reagents = { # # if time_to_complete != 0:
r: state.reagents[r] + wait * state.robots[r] - needed.get(r, 0) # # continue
for r in REAGENTS
}
robots = state.robots.copy() # if time + time_to_complete + 1 > MAX_TIME:
robots[reagent] += 1 # continue
state_2 = State(reagents=reagents, robots=robots) # wait = time_to_complete + 1
print(time + wait) # reagents = {
if any(dominates(state_3, state_2) for state_3 in at_time[time + wait]): # r: state.reagents[r] + wait * state.robots[r] - needed.get(r, 0)
continue # for r in REAGENTS
# }
if state_2 not in parents or parents[state_2][1] > time + wait: # robots = state.robots.copy()
parents[state_2] = (state, time + wait) # robots[reagent] += 1
heapq.heappush(queue, (time + wait, state_2))
can_build_any = True
if not can_build_any: # state_2 = State(reagents=reagents, robots=robots)
state_2 = State(
reagents={
r: state.reagents[r] + state.robots[r] * (MAX_TIME - time)
for r in REAGENTS
},
robots=state.robots,
)
if state_2 not in parents or parents[state_2][1] > time + wait: # if state_2 in visited:
parents[state_2] = (state, time + wait) # continue
heapq.heappush(queue, (time + wait, state_2))
print(len(visited)) # if any(dominates(state_v, state_2) for state_v in at_time[time + wait]):
print(max(state.reagents["geode"] for state in visited)) # continue
exit() # # print(time + wait)
# # if any(dominates(state_3, state_2) for state_3 in at_time[time + wait]):
# # print("?")
# # continue
while states: # if state_2 not in parents or parents[state_2][1] > time + wait:
state = states.pop() # parents[state_2] = (state, time + wait)
processed.append(state) # heapq.heappush(queue, (time + wait, state_2))
# can_build_any = True
# at_time[time + wait].append(state_2)
if state.time > MAX_TIME: # if not can_build_any:
continue # state_2 = State(
# reagents={
# r: state.reagents[r] + state.robots[r] * (MAX_TIME - time)
# for r in REAGENTS
# },
# robots=state.robots,
# )
if len(states) % 100 == 0: # if state_2 in visited:
print(len(states), len(processed), min((s.time for s in states), default=1)) # continue
can_build_any: bool = False # if state_2 not in parents or parents[state_2][1] > time + wait:
for reagent in REAGENTS: # parents[state_2] = (state, MAX_TIME)
needed = blueprint[reagent] # heapq.heappush(queue, (MAX_TIME, state_2))
if any(state.robots[r] == 0 for r in needed): # print(len(visited))
continue # print(max(state.reagents["geode"] for state in visited))
time_to_complete = max( # exit()
max(
math.ceil((needed[r] - state.reagents[r]) / state.robots[r])
for r in needed
),
0,
)
if state.time + time_to_complete + 1 > MAX_TIME: # while states:
continue # state = states.pop()
# processed.append(state)
wait = time_to_complete + 1 # if state.time > MAX_TIME:
# continue
reagents = { # if len(states) % 100 == 0:
r: state.reagents[r] + wait * state.robots[r] - needed.get(r, 0) # print(len(states), len(processed), min((s.time for s in states), default=1))
for r in REAGENTS
}
robots = state.robots.copy() # can_build_any: bool = False
robots[reagent] += 1 # for reagent in REAGENTS:
# needed = blueprint[reagent]
can_build_any = True # if any(state.robots[r] == 0 for r in needed):
state_2 = State(time=state.time + wait, reagents=reagents, robots=robots) # continue
# print(f"{state} -> {state_2}")
states.add(state_2)
if not any(dominates(s2, state_2) for s2 in states): # time_to_complete = max(
states.add(state) # max(
# math.ceil((needed[r] - state.reagents[r]) / state.robots[r])
# for r in needed
# ),
# 0,
# )
# print(f"can build {reagent} in {time_to_complete}") # if state.time + time_to_complete + 1 > MAX_TIME:
# continue
if not can_build_any: # wait = time_to_complete + 1
states.add(
State(
time=MAX_TIME + 1,
reagents={
r: state.reagents[r] + state.robots[r] * (MAX_TIME - state.time)
for r in REAGENTS
},
robots=state.robots,
)
)
if len(states) % 1000 == 0: # reagents = {
print("filtering") # r: state.reagents[r] + wait * state.robots[r] - needed.get(r, 0)
states = { # for r in REAGENTS
s1 # }
for s1 in states
if not any(dominates(s2, s1) for s2 in states if s2 is not s1)
}
# if len(states) > 4: # robots = state.robots.copy()
# break # robots[reagent] += 1
# break # can_build_any = True
# state_2 = State(time=state.time + wait, reagents=reagents, robots=robots)
# # print(f"{state} -> {state_2}")
# states.add(state_2)
print(len(processed)) # if not any(dominates(s2, state_2) for s2 in states):
print(max(state.reagents["geode"] for state in processed)) # states.add(state)
exit() # # print(f"can build {reagent} in {time_to_complete}")
for t in range(1, 25): # if not can_build_any:
states = set() # states.add(
for state in state_after_t[t - 1]: # State(
robots_that_can_be_built = [ # time=MAX_TIME + 1,
robot # reagents={
for robot in REAGENTS # r: state.reagents[r] + state.robots[r] * (MAX_TIME - state.time)
if all( # for r in REAGENTS
state.reagents[reagent] >= blueprint[robot].get(reagent, 0) # },
for reagent in REAGENTS # robots=state.robots,
) # )
] # )
new_states = set() # if len(states) % 1000 == 0:
# print("filtering")
# states = {
# s1
# for s1 in states
# if not any(dominates(s2, s1) for s2 in states if s2 is not s1)
# }
# new reagents # # if len(states) > 4:
reagents = { # # break
reagent: state.reagents[reagent] + state.robots[reagent]
for reagent in REAGENTS
}
# if we can build anything, there is no point in waiting # # break
if len(robots_that_can_be_built) != len(REAGENTS):
new_states.add(State(robots=state.robots, reagents=reagents))
for robot in robots_that_can_be_built: # print(len(processed))
robots = state.robots.copy() # print(max(state.reagents["geode"] for state in processed))
robots[robot] += 1
reagents = {
reagent: state.reagents[reagent]
+ state.robots[reagent]
- blueprint[robot].get(reagent, 0)
for reagent in REAGENTS
}
new_states.add(State(robots=robots, reagents=reagents))
new_states = [ # exit()
s1
for s1 in new_states
if not any(s1 is not s2 and dominates(s2, s1) for s2 in new_states)
]
states = { # for t in range(1, 25):
s1 for s1 in states if not any(dominates(s2, s1) for s2 in new_states) # states = set()
} # for state in state_after_t[t - 1]:
states.update(new_states) # robots_that_can_be_built = [
# robot
# for robot in REAGENTS
# if all(
# state.reagents[reagent] >= blueprint[robot].get(reagent, 0)
# for reagent in REAGENTS
# )
# ]
state_after_t[t] = states # new_states = set()
exit() # # new reagents
# reagents = {
# reagent: state.reagents[reagent] + state.robots[reagent]
# for reagent in REAGENTS
# }
# # if we can build anything, there is no point in waiting
# if len(robots_that_can_be_built) != len(REAGENTS):
# new_states.add(State(robots=state.robots, reagents=reagents))
# for robot in robots_that_can_be_built:
# robots = state.robots.copy()
# robots[robot] += 1
# reagents = {
# reagent: state.reagents[reagent]
# + state.robots[reagent]
# - blueprint[robot].get(reagent, 0)
# for reagent in REAGENTS
# }
# new_states.add(State(robots=robots, reagents=reagents))
# new_states = [
# s1
# for s1 in new_states
# if not any(s1 is not s2 and dominates(s2, s1) for s2 in new_states)
# ]
# states = {
# s1 for s1 in states if not any(dominates(s2, s1) for s2 in new_states)
# }
# states.update(new_states)
# state_after_t[t] = states
# exit()
MAX_TIME = 24
blueprint = blueprints[0]
state_after_t: dict[int, list[State]] = {0: [State()]}
for t in range(1, 25): for t in range(1, 25):
print(t, len(state_after_t[t - 1])) print(t, len(state_after_t[t - 1]))
state_after_t[t] = set()
bests_for_robots: dict[tuple[int, ...], set[State]] = {} bests_for_robots: dict[tuple[int, ...], list[State]] = {}
bests_for_reagents: dict[tuple[int, ...], set[State]] = {} bests_for_reagents: dict[tuple[int, ...], list[State]] = {}
state_after_t[t] = []
t1 = time.time()
for state in state_after_t[t - 1]: for state in state_after_t[t - 1]:
robots_that_can_be_built = [ robots_that_can_be_built = [
@ -324,11 +349,9 @@ for t in range(1, 25):
} }
# if we can build anything, there is no point in waiting # if we can build anything, there is no point in waiting
new_states.add(State(robots=state.robots, reagents=reagents, last=None)) new_states.add(State(robots=state.robots, reagents=reagents))
for robot in robots_that_can_be_built: for robot in robots_that_can_be_built:
if robot == state.last:
continue
robots = state.robots.copy() robots = state.robots.copy()
robots[robot] += 1 robots[robot] += 1
reagents = { reagents = {
@ -337,12 +360,12 @@ for t in range(1, 25):
- blueprint[robot].get(reagent, 0) - blueprint[robot].get(reagent, 0)
for reagent in REAGENTS for reagent in REAGENTS
} }
new_states.add(State(robots=robots, reagents=reagents, last=robot)) new_states.add(State(robots=robots, reagents=reagents))
for s1 in new_states: for s1 in new_states:
r1 = tuple(s1.robots[r] for r in REAGENTS) r1 = tuple(s1.robots[r] for r in REAGENTS)
if r1 not in bests_for_robots: if r1 not in bests_for_robots:
bests_for_robots[r1] = {s1} bests_for_robots[r1] = [s1]
else: else:
is_dominated = False is_dominated = False
for s2 in bests_for_robots[r1]: for s2 in bests_for_robots[r1]:
@ -350,11 +373,11 @@ for t in range(1, 25):
is_dominated = True is_dominated = True
break break
if not is_dominated: if not is_dominated:
bests_for_robots[r1].add(s1) bests_for_robots[r1].append(s1)
r2 = tuple(s1.reagents[r] for r in REAGENTS) r2 = tuple(s1.reagents[r] for r in REAGENTS)
if r2 not in bests_for_reagents: if r2 not in bests_for_reagents:
bests_for_reagents[r2] = {s1} bests_for_reagents[r2] = [s1]
else: else:
is_dominated = False is_dominated = False
for s2 in bests_for_reagents[r2]: for s2 in bests_for_reagents[r2]:
@ -362,20 +385,22 @@ for t in range(1, 25):
is_dominated = True is_dominated = True
break break
if not is_dominated: if not is_dominated:
bests_for_reagents[r2].add(s1) bests_for_reagents[r2].append(s1)
# state_after_t[t].extend(new_states)
t2 = time.time()
state_after_t[t] = set()
for bests in bests_for_robots.values(): for bests in bests_for_robots.values():
dominated = [False for _ in range(len(bests))] dominated = [False for _ in range(len(bests))]
for i_s1, s1 in enumerate(bests): for i_s1, s1 in enumerate(bests):
if dominated[i_s1]: if dominated[i_s1]:
continue continue
for i_s2, s2 in enumerate(bests): for i_s2, s2 in enumerate(bests[i_s1 + 1 :], start=i_s1 + 1):
if s1 is s2 or dominated[i_s2]: if dominated[i_s2]:
continue continue
if all(s1.reagents[r] >= s2.reagents[r] for r in REAGENTS): if all(s1.reagents[r] >= s2.reagents[r] for r in REAGENTS):
dominated[i_s2] = True dominated[i_s2] = True
state_after_t[t].update( state_after_t[t].extend(
s1 for i_s1, s1 in enumerate(bests) if not dominated[i_s1] s1 for i_s1, s1 in enumerate(bests) if not dominated[i_s1]
) )
for bests in bests_for_reagents.values(): for bests in bests_for_reagents.values():
@ -383,31 +408,77 @@ for t in range(1, 25):
for i_s1, s1 in enumerate(bests): for i_s1, s1 in enumerate(bests):
if dominated[i_s1]: if dominated[i_s1]:
continue continue
for i_s2, s2 in enumerate(bests): for i_s2, s2 in enumerate(bests[i_s1 + 1 :], start=i_s1 + 1):
if s1 is s2 or dominated[i_s2]: if dominated[i_s2]:
continue continue
if all(s1.robots[r] >= s2.robots[r] for r in REAGENTS): if all(s1.robots[r] >= s2.robots[r] for r in REAGENTS):
dominated[i_s2] = True dominated[i_s2] = True
state_after_t[t].update( state_after_t[t].extend(
s1 for i_s1, s1 in enumerate(bests) if not dominated[i_s1] s1 for i_s1, s1 in enumerate(bests) if not dominated[i_s1]
) )
t3 = time.time()
np_states = np.array(
[
[state.robots[r] for r in REAGENTS] + [state.reagents[r] for r in REAGENTS]
for state in state_after_t[t]
]
)
dominated = np.zeros(len(np_states), dtype=bool)
t4 = time.time()
# c = (np_states[None, :, :] <= np_states[:, None, :]).all(axis=-1)
# c[np.arange(len(np_states)), np.arange(len(np_states))] = False
# dominated = c.any(axis=0)
for i in range(len(np_states)):
if dominated[i]:
continue
dominated[i] = not (np_states[i + 1 :] <= np_states[i]).any(axis=1)
dominated[i + 1 :] = (np_states[i + 1 :] <= np_states[i]).all(axis=1)
t5 = time.time()
state_after_t[t] = list(np.array(state_after_t[t])[~dominated])
t6 = time.time()
print(
"->",
t,
len(state_after_t[t]),
dominated.sum(),
t2 - t1,
t3 - t2,
t4 - t3,
t5 - t4,
t6 - t5,
)
# print("->", len(state_after_t[t]))
# dominated = [False for _ in range(len(state_after_t[t]))] # dominated = [False for _ in range(len(state_after_t[t]))]
# print(t, "->", len(state_after_t[t])) # keep = set()
# for i_s1, s1 in enumerate(state_after_t[t]): # for i_s1, s1 in enumerate(tqdm(state_after_t[t])):
# if dominated[i_s1]: # if dominated[i_s1]:
# continue # continue
# for i_s2, s2 in enumerate(state_after_t[t]): # for i_s2, s2 in enumerate(state_after_t[t][i_s1 + 1 :], start=i_s1 + 1):
# if s1 is s2 or dominated[i_s2]: # if dominated[i_s2]:
# continue # continue
# if all(s1.robots[r] >= s2.robots[r] for r in REAGENTS) and all(
# s1.reagents[r] >= s2.reagents[r] for r in REAGENTS
# ):
# dominated[i_s2] = True
# state_after_t[t] = { # if dominates(s1, s2):
# s1 for i_s1, s1 in enumerate(state_after_t[t]) if not dominated[i_s1] # dominated[i_s2] = True
# } # elif dominates(s2, s1):
# dominated[i_s1] = True
# break
# if not dominated[i_s1]:
# keep.add(s1)
# state_after_t[t] = list(keep)
# print(len(state_after_t[t])) # print(len(state_after_t[t]))
# print(sum(dominated)) # print(sum(dominated))