|
|
|
|
@@ -22,7 +22,7 @@ def generate_dungeon(builder, entrance_region_names, split_dungeon, world, playe
|
|
|
|
|
queue = collections.deque(proposed_map.items())
|
|
|
|
|
while len(queue) > 0:
|
|
|
|
|
a, b = queue.popleft()
|
|
|
|
|
if world.decoupledoors[player]:
|
|
|
|
|
if a == b or world.decoupledoors[player]:
|
|
|
|
|
connect_doors_one_way(a, b)
|
|
|
|
|
else:
|
|
|
|
|
connect_doors(a, b)
|
|
|
|
|
@@ -72,11 +72,13 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
|
|
|
|
entrance_regions = [x for x in entrance_regions if x not in excluded.keys()]
|
|
|
|
|
doors_to_connect, idx = {}, 0
|
|
|
|
|
all_regions = set()
|
|
|
|
|
bk_special = False
|
|
|
|
|
for sector in builder.sectors:
|
|
|
|
|
for door in sector.outstanding_doors:
|
|
|
|
|
doors_to_connect[door.name] = door, idx
|
|
|
|
|
idx += 1
|
|
|
|
|
all_regions.update(sector.regions)
|
|
|
|
|
bk_special |= check_for_special(sector.regions)
|
|
|
|
|
finished = False
|
|
|
|
|
# flag if standard and this is hyrule castle
|
|
|
|
|
paths = determine_paths_for_dungeon(world, player, all_regions, name)
|
|
|
|
|
@@ -95,9 +97,9 @@ def generate_dungeon_find_proposal(builder, entrance_region_names, split_dungeon
|
|
|
|
|
if hash_code not in hash_code_set:
|
|
|
|
|
hash_code_set.add(hash_code)
|
|
|
|
|
explored_state = explore_proposal(name, entrance_regions, all_regions, proposed_map, doors_to_connect,
|
|
|
|
|
world, player)
|
|
|
|
|
bk_special, world, player)
|
|
|
|
|
if check_valid(name, explored_state, proposed_map, doors_to_connect, all_regions,
|
|
|
|
|
paths, entrance_regions, world, player):
|
|
|
|
|
paths, entrance_regions, bk_special, world, player):
|
|
|
|
|
finished = True
|
|
|
|
|
else:
|
|
|
|
|
proposed_map, hash_code = modify_proposal(proposed_map, explored_state, doors_to_connect,
|
|
|
|
|
@@ -128,14 +130,14 @@ def create_random_proposal(doors_to_connect, world, player):
|
|
|
|
|
next_hook = random.choice(hooks_left)
|
|
|
|
|
primary_door = random.choice(primary_bucket[next_hook])
|
|
|
|
|
opp_hook, secondary_door = type_map[next_hook], None
|
|
|
|
|
while (secondary_door is None or secondary_door == primary_door
|
|
|
|
|
while (secondary_door is None or (secondary_door == primary_door and not world.door_self_loops[player])
|
|
|
|
|
or decouple_check(primary_bucket[next_hook], secondary_bucket[opp_hook],
|
|
|
|
|
primary_door, secondary_door, world, player)):
|
|
|
|
|
secondary_door = random.choice(secondary_bucket[opp_hook])
|
|
|
|
|
proposal[primary_door] = secondary_door
|
|
|
|
|
primary_bucket[next_hook].remove(primary_door)
|
|
|
|
|
secondary_bucket[opp_hook].remove(secondary_door)
|
|
|
|
|
if not world.decoupledoors[player]:
|
|
|
|
|
if primary_door != secondary_door and not world.decoupledoors[player]:
|
|
|
|
|
proposal[secondary_door] = primary_door
|
|
|
|
|
primary_bucket[opp_hook].remove(secondary_door)
|
|
|
|
|
secondary_bucket[next_hook].remove(primary_door)
|
|
|
|
|
@@ -200,11 +202,19 @@ def modify_proposal(proposed_map, explored_state, doors_to_connect, hash_code_se
|
|
|
|
|
unvisted_bucket[opp_hook].sort(key=lambda d: d.name)
|
|
|
|
|
new_door = random.choice(unvisted_bucket[opp_hook])
|
|
|
|
|
old_target = proposed_map[attempt]
|
|
|
|
|
proposed_map[attempt] = new_door
|
|
|
|
|
if not world.decoupledoors[player]:
|
|
|
|
|
old_attempt = proposed_map[new_door]
|
|
|
|
|
else:
|
|
|
|
|
old_attempt = next(x for x in proposed_map if proposed_map[x] == new_door)
|
|
|
|
|
# ensure nothing gets messed up when something loops with itself
|
|
|
|
|
if attempt == old_target and old_attempt == new_door:
|
|
|
|
|
old_attempt = new_door
|
|
|
|
|
old_target = attempt
|
|
|
|
|
elif attempt == old_target:
|
|
|
|
|
old_target = old_attempt
|
|
|
|
|
elif old_attempt == new_door:
|
|
|
|
|
old_attempt = old_target
|
|
|
|
|
proposed_map[attempt] = new_door
|
|
|
|
|
proposed_map[old_attempt] = old_target
|
|
|
|
|
if not world.decoupledoors[player]:
|
|
|
|
|
proposed_map[old_target] = old_attempt
|
|
|
|
|
@@ -221,21 +231,24 @@ def modify_proposal(proposed_map, explored_state, doors_to_connect, hash_code_se
|
|
|
|
|
return proposed_map, hash_code
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def explore_proposal(name, entrance_regions, all_regions, proposed_map, valid_doors, world, player):
|
|
|
|
|
def explore_proposal(name, entrance_regions, all_regions, proposed_map, valid_doors, bk_special, world, player):
|
|
|
|
|
start = ExplorationState(dungeon=name)
|
|
|
|
|
bk_relevant = (world.door_type_mode[player] == 'original' and not world.bigkeyshuffle[player]) or bk_special
|
|
|
|
|
start.big_key_special = bk_special
|
|
|
|
|
original_state = extend_reachable_state_lenient(entrance_regions, start, proposed_map,
|
|
|
|
|
all_regions, valid_doors, world, player)
|
|
|
|
|
all_regions, valid_doors, bk_relevant, world, player)
|
|
|
|
|
return original_state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def check_valid(name, exploration_state, proposed_map, doors_to_connect, all_regions,
|
|
|
|
|
paths, entrance_regions, world, player):
|
|
|
|
|
paths, entrance_regions, bk_special, world, player):
|
|
|
|
|
all_visited = set()
|
|
|
|
|
all_visited.update(exploration_state.visited_blue)
|
|
|
|
|
all_visited.update(exploration_state.visited_orange)
|
|
|
|
|
if len(all_regions.difference(all_visited)) > 0:
|
|
|
|
|
return False
|
|
|
|
|
if not valid_paths(name, paths, entrance_regions, doors_to_connect, all_regions, proposed_map, world, player):
|
|
|
|
|
if not valid_paths(name, paths, entrance_regions, doors_to_connect, all_regions, proposed_map,
|
|
|
|
|
bk_special, world, player):
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
@@ -258,7 +271,7 @@ def check_for_special(regions):
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def valid_paths(name, paths, entrance_regions, valid_doors, all_regions, proposed_map, world, player):
|
|
|
|
|
def valid_paths(name, paths, entrance_regions, valid_doors, all_regions, proposed_map, bk_special, world, player):
|
|
|
|
|
for path in paths:
|
|
|
|
|
if type(path) is tuple:
|
|
|
|
|
target = path[1]
|
|
|
|
|
@@ -270,12 +283,13 @@ def valid_paths(name, paths, entrance_regions, valid_doors, all_regions, propose
|
|
|
|
|
else:
|
|
|
|
|
target = path
|
|
|
|
|
start_regions = entrance_regions
|
|
|
|
|
if not valid_path(name, start_regions, target, valid_doors, proposed_map, all_regions, world, player):
|
|
|
|
|
if not valid_path(name, start_regions, target, valid_doors, proposed_map, all_regions,
|
|
|
|
|
bk_special, world, player):
|
|
|
|
|
return False
|
|
|
|
|
return True
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def valid_path(name, starting_regions, target, valid_doors, proposed_map, all_regions, world, player):
|
|
|
|
|
def valid_path(name, starting_regions, target, valid_doors, proposed_map, all_regions, bk_special, world, player):
|
|
|
|
|
target_regions = set()
|
|
|
|
|
if type(target) is not list:
|
|
|
|
|
for region in all_regions:
|
|
|
|
|
@@ -288,8 +302,10 @@ def valid_path(name, starting_regions, target, valid_doors, proposed_map, all_re
|
|
|
|
|
target_regions.add(region)
|
|
|
|
|
|
|
|
|
|
start = ExplorationState(dungeon=name)
|
|
|
|
|
bk_relevant = (world.door_type_mode[player] == 'original' and not world.bigkeyshuffle[player]) or bk_special
|
|
|
|
|
start.big_key_special = bk_special
|
|
|
|
|
original_state = extend_reachable_state_lenient(starting_regions, start, proposed_map, all_regions,
|
|
|
|
|
valid_doors, world, player)
|
|
|
|
|
valid_doors, bk_relevant, world, player)
|
|
|
|
|
|
|
|
|
|
for exp_door in original_state.unattached_doors:
|
|
|
|
|
if not exp_door.door.blocked or exp_door.door.trapFlag != 0:
|
|
|
|
|
@@ -523,7 +539,7 @@ class ExplorationState(object):
|
|
|
|
|
self.crystal = exp_door.crystal
|
|
|
|
|
return exp_door
|
|
|
|
|
|
|
|
|
|
def visit_region(self, region, key_region=None, key_checks=False, bk_flag=False):
|
|
|
|
|
def visit_region(self, region, key_region=None, key_checks=False, bk_relevant=False):
|
|
|
|
|
if region.type != RegionType.Dungeon:
|
|
|
|
|
self.crystal = CrystalBarrier.Orange
|
|
|
|
|
if self.crystal == CrystalBarrier.Either:
|
|
|
|
|
@@ -544,8 +560,14 @@ class ExplorationState(object):
|
|
|
|
|
self.ttl_locations += 1
|
|
|
|
|
if location not in self.found_locations:
|
|
|
|
|
self.found_locations.append(location)
|
|
|
|
|
if not bk_flag:
|
|
|
|
|
self.bk_found.add(location)
|
|
|
|
|
if bk_relevant:
|
|
|
|
|
if self.big_key_special:
|
|
|
|
|
if special_big_key_found(self):
|
|
|
|
|
self.bk_found.add(location)
|
|
|
|
|
self.re_add_big_key_doors()
|
|
|
|
|
else:
|
|
|
|
|
self.bk_found.add(location)
|
|
|
|
|
self.re_add_big_key_doors()
|
|
|
|
|
if location.name in dungeon_events and location.name not in self.events:
|
|
|
|
|
if self.flooded_key_check(location):
|
|
|
|
|
self.perform_event(location.name, key_region)
|
|
|
|
|
@@ -566,6 +588,14 @@ class ExplorationState(object):
|
|
|
|
|
return True
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def re_add_big_key_doors(self):
|
|
|
|
|
self.big_key_opened = True
|
|
|
|
|
queue = collections.deque(self.big_doors)
|
|
|
|
|
while len(queue) > 0:
|
|
|
|
|
exp_door = queue.popleft()
|
|
|
|
|
self.avail_doors.append(exp_door)
|
|
|
|
|
self.big_doors.remove(exp_door)
|
|
|
|
|
|
|
|
|
|
def perform_event(self, location_name, key_region):
|
|
|
|
|
self.events.add(location_name)
|
|
|
|
|
queue = collections.deque(self.event_doors)
|
|
|
|
|
@@ -632,7 +662,7 @@ class ExplorationState(object):
|
|
|
|
|
self.append_door_to_list(door, self.avail_doors, flag)
|
|
|
|
|
|
|
|
|
|
# same as above but traps are ignored, and flag is not used
|
|
|
|
|
def add_all_doors_check_proposed_2(self, region, proposed_map, valid_doors, world, player):
|
|
|
|
|
def add_all_doors_check_proposed_2(self, region, proposed_map, valid_doors, bk_relevant, world, player):
|
|
|
|
|
for door in get_doors(world, region, player):
|
|
|
|
|
if door in proposed_map and door.name in valid_doors:
|
|
|
|
|
self.visited_doors.add(door)
|
|
|
|
|
@@ -646,14 +676,18 @@ class ExplorationState(object):
|
|
|
|
|
other = self.find_door_in_list(door, self.unattached_doors)
|
|
|
|
|
if self.crystal != other.crystal:
|
|
|
|
|
other.crystal = CrystalBarrier.Either
|
|
|
|
|
elif door.req_event is not None and door.req_event not in self.events and not self.in_door_list(door,
|
|
|
|
|
self.event_doors):
|
|
|
|
|
elif (door.req_event is not None and door.req_event not in self.events
|
|
|
|
|
and not self.in_door_list(door, self.event_doors)):
|
|
|
|
|
self.append_door_to_list(door, self.event_doors)
|
|
|
|
|
elif (bk_relevant and (door.bigKey or door.name in get_special_big_key_doors(world, player))
|
|
|
|
|
and not self.big_key_opened):
|
|
|
|
|
if not self.in_door_list(door, self.big_doors):
|
|
|
|
|
self.append_door_to_list(door, self.big_doors)
|
|
|
|
|
elif not self.in_door_list(door, self.avail_doors):
|
|
|
|
|
self.append_door_to_list(door, self.avail_doors)
|
|
|
|
|
|
|
|
|
|
# same as above but traps are checked for
|
|
|
|
|
def add_all_doors_check_proposed_3(self, region, proposed_map, valid_doors, world, player):
|
|
|
|
|
def add_all_doors_check_proposed_3(self, region, proposed_map, valid_doors, bk_relevant, world, player):
|
|
|
|
|
for door in get_doors(world, region, player):
|
|
|
|
|
if door in proposed_map and door.name in valid_doors:
|
|
|
|
|
self.visited_doors.add(door)
|
|
|
|
|
@@ -667,9 +701,13 @@ class ExplorationState(object):
|
|
|
|
|
other = self.find_door_in_list(door, self.unattached_doors)
|
|
|
|
|
if self.crystal != other.crystal:
|
|
|
|
|
other.crystal = CrystalBarrier.Either
|
|
|
|
|
elif door.req_event is not None and door.req_event not in self.events and not self.in_door_list(door,
|
|
|
|
|
self.event_doors):
|
|
|
|
|
elif (door.req_event is not None and door.req_event not in self.events
|
|
|
|
|
and not self.in_door_list(door, self.event_doors)):
|
|
|
|
|
self.append_door_to_list(door, self.event_doors)
|
|
|
|
|
elif (bk_relevant and (door.bigKey or door.name in get_special_big_key_doors(world, player))
|
|
|
|
|
and not self.big_key_opened):
|
|
|
|
|
if not self.in_door_list(door, self.big_doors):
|
|
|
|
|
self.append_door_to_list(door, self.big_doors)
|
|
|
|
|
elif not self.in_door_list(door, self.avail_doors):
|
|
|
|
|
self.append_door_to_list(door, self.avail_doors)
|
|
|
|
|
|
|
|
|
|
@@ -855,16 +893,22 @@ def extend_reachable_state_improved(search_regions, state, proposed_map, all_reg
|
|
|
|
|
return local_state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def extend_reachable_state_lenient(search_regions, state, proposed_map, all_regions, valid_doors, world, player):
|
|
|
|
|
# bk_relevant means the big key doors need to be checks
|
|
|
|
|
def extend_reachable_state_lenient(search_regions, state, proposed_map, all_regions, valid_doors, bk_relevant,
|
|
|
|
|
world, player):
|
|
|
|
|
local_state = state.copy()
|
|
|
|
|
for region in search_regions:
|
|
|
|
|
local_state.visit_region(region)
|
|
|
|
|
local_state.visit_region(region, bk_relevant=bk_relevant)
|
|
|
|
|
if world.trap_door_mode[player] == 'vanilla':
|
|
|
|
|
local_state.add_all_doors_check_proposed_3(region, proposed_map, valid_doors, world, player)
|
|
|
|
|
local_state.add_all_doors_check_proposed_3(region, proposed_map, valid_doors, bk_relevant, world, player)
|
|
|
|
|
else:
|
|
|
|
|
local_state.add_all_doors_check_proposed_2(region, proposed_map, valid_doors, world, player)
|
|
|
|
|
local_state.add_all_doors_check_proposed_2(region, proposed_map, valid_doors, bk_relevant, world, player)
|
|
|
|
|
while len(local_state.avail_doors) > 0:
|
|
|
|
|
explorable_door = local_state.next_avail_door()
|
|
|
|
|
if explorable_door.door.bigKey:
|
|
|
|
|
if bk_relevant and (not special_big_key_found(local_state) if local_state.big_key_special
|
|
|
|
|
else local_state.count_locations_exclude_specials(world, player) == 0):
|
|
|
|
|
continue
|
|
|
|
|
if explorable_door.door in proposed_map:
|
|
|
|
|
connect_region = world.get_entrance(proposed_map[explorable_door.door].name, player).parent_region
|
|
|
|
|
else:
|
|
|
|
|
@@ -872,11 +916,13 @@ def extend_reachable_state_lenient(search_regions, state, proposed_map, all_regi
|
|
|
|
|
if connect_region is not None:
|
|
|
|
|
if (valid_region_to_explore_in_regions(connect_region, all_regions, world, player)
|
|
|
|
|
and not local_state.visited(connect_region)):
|
|
|
|
|
local_state.visit_region(connect_region)
|
|
|
|
|
local_state.visit_region(connect_region, bk_relevant=bk_relevant)
|
|
|
|
|
if world.trap_door_mode[player] == 'vanilla':
|
|
|
|
|
local_state.add_all_doors_check_proposed_3(connect_region, proposed_map, valid_doors, world, player)
|
|
|
|
|
local_state.add_all_doors_check_proposed_3(connect_region, proposed_map, valid_doors,
|
|
|
|
|
bk_relevant, world, player)
|
|
|
|
|
else:
|
|
|
|
|
local_state.add_all_doors_check_proposed_2(connect_region, proposed_map, valid_doors, world, player)
|
|
|
|
|
local_state.add_all_doors_check_proposed_2(connect_region, proposed_map, valid_doors,
|
|
|
|
|
bk_relevant, world, player)
|
|
|
|
|
return local_state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|