From e10c765bc28a91b031920bddfe410e003550cfea Mon Sep 17 00:00:00 2001 From: aerinon Date: Tue, 3 Aug 2021 12:16:29 -0600 Subject: [PATCH] Key logic --- KeyDoorShuffle.py | 69 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index e7bcae11..7d550d1b 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -275,7 +275,7 @@ def analyze_dungeon(key_layout, world, player): # try to relax the rules here? - smallest requirement that doesn't force a softlock child_queue = deque() for child in key_counter.child_doors.keys(): - if not child.bigKey or not key_layout.big_key_special or big_avail: + if can_open_door_by_counter(child, key_counter, key_layout, world, player): odd_counter = create_odd_key_counter(child, key_counter, key_layout, world, player) empty_flag = empty_counter(odd_counter) child_queue.append((child, odd_counter, empty_flag)) @@ -460,7 +460,8 @@ def refine_placement_rules(key_layout, max_ctr): rule_b = temp if rule_a.bk_conditional_set and rule_b.check_locations_w_bk: common_needed = min(rule_a.needed_keys_wo_bk, rule_b.needed_keys_w_bk) - if len(rule_b.check_locations_w_bk & rule_a.check_locations_wo_bk) < common_needed: + common_locs = len(rule_b.check_locations_w_bk & rule_a.check_locations_wo_bk) + if (common_needed - common_locs) * 2 > key_layout.max_chests: key_logic.bk_restricted.update(rule_a.bk_conditional_set) rules_to_remove.append(rule_a) changed = True @@ -821,7 +822,7 @@ def check_for_self_lock_key(rule, door, parent_counter, key_layout, world, playe def find_inverted_counter(door, parent_counter, key_layout, world, player): # open all doors in counter - counter = open_all_counter(parent_counter, key_layout, door=door) + counter = open_all_counter(parent_counter, key_layout, world, player, door=door) max_counter = find_max_counter(key_layout) # find the difference inverted_counter = KeyCounter(key_layout.max_chests) @@ -837,7 +838,7 @@ def find_inverted_counter(door, parent_counter, key_layout, world, player): return inverted_counter -def open_all_counter(parent_counter, key_layout, door=None, skipBk=False): +def open_all_counter(parent_counter, key_layout, world, player, door=None, skipBk=False): changed = True counter = parent_counter proposed_doors = dict.fromkeys(parent_counter.open_doors.keys()) @@ -849,13 +850,11 @@ def open_all_counter(parent_counter, key_layout, door=None, skipBk=False): if skipBk: if not child.bigKey: doors_to_open[child] = None - elif not child.bigKey or not key_layout.big_key_special or counter.big_key_opened: + elif can_open_door_by_counter(child, counter, key_layout, world, player): doors_to_open[child] = None if len(doors_to_open.keys()) > 0: proposed_doors = {**proposed_doors, **doors_to_open} - bk_hint = counter.big_key_opened - for d in doors_to_open.keys(): - bk_hint = bk_hint or d.bigKey + bk_hint = counter.big_key_opened or any(d.bigKey for d in doors_to_open.keys()) counter = find_counter(proposed_doors, bk_hint, key_layout, True) changed = True return counter @@ -1485,12 +1484,28 @@ def cnt_avail_small_locations(free_locations, key_only, state, world, player): return state.key_locations - state.used_smalls +def cnt_avail_small_locations_by_ctr(free_locations, counter, layout, world, player): + if not world.keyshuffle[player] and not world.retro[player]: + bk_adj = 1 if counter.big_key_opened and not layout.big_key_special else 0 + avail_chest_keys = min(free_locations - bk_adj, layout.max_chests) + return max(0, avail_chest_keys + len(counter.key_only_locations) - counter.used_keys) + return layout.max_chests + len(counter.key_only_locations) - counter.used_keys + + def cnt_avail_big_locations(ttl_locations, state, world, player): if not world.bigkeyshuffle[player]: return max(0, ttl_locations - state.used_locations) if not state.big_key_special else 0 return 1 if not state.big_key_special else 0 +def cnt_avail_big_locations_by_ctr(ttl_locations, counter, layout, world, player): + if not world.bigkeyshuffle[player]: + bk_adj = 1 if counter.big_key_opened and not layout.big_key_special else 0 + used_locations = max(0, counter.used_keys - len(counter.key_only_locations)) + bk_adj + return max(0, ttl_locations - used_locations) if not layout.big_key_special else 0 + return 1 if not layout.big_key_special else 0 + + def create_key_counters(key_layout, world, player): key_counters = {} key_layout.found_doors.clear() @@ -1500,12 +1515,11 @@ def create_key_counters(key_layout, world, player): state.key_locations = len(world.get_dungeon(key_layout.sector.name, player).small_keys) else: state.key_locations = world.dungeon_layouts[player][key_layout.sector.name].key_doors_num - state.big_key_special, special_region = False, None + state.big_key_special = False for region in key_layout.sector.regions: for location in region.locations: if location.forced_big_key(): state.big_key_special = True - special_region = region for region in key_layout.start_regions: dungeon_entrance, portal_door = find_outside_connection(region) if (len(key_layout.start_regions) > 1 and dungeon_entrance and @@ -1531,7 +1545,7 @@ def create_key_counters(key_layout, world, player): if door.bigKey or door.name in special_big_key_doors: key_layout.key_logic.bk_doors.add(door) # open the door, if possible - if not door.bigKey or not child_state.big_key_special or child_state.visited_at_all(special_region): + if can_open_door(door, child_state, world, player): open_a_door(door, child_state, flat_proposal, world, player) expand_key_state(child_state, flat_proposal, world, player) code = state_id(child_state, key_layout.flat_prop) @@ -1552,6 +1566,39 @@ def find_outside_connection(region): return None, None +def can_open_door(door, state, world, player): + if state.big_key_opened: + ttl_locations = count_free_locations(state, world, player) + else: + ttl_locations = count_locations_exclude_big_chest(state, world, player) + if door.smallKey: + ttl_small_key_only = count_small_key_only_locations(state) + available_small_locations = cnt_avail_small_locations(ttl_locations, ttl_small_key_only, state, world, player) + return available_small_locations > 0 + elif door.bigKey: + available_big_locations = cnt_avail_big_locations(ttl_locations, state, world, player) + found_forced_bk = state.found_forced_bk() + return not state.big_key_opened and (available_big_locations > 0 or found_forced_bk) + else: + return True + + +def can_open_door_by_counter(door, counter: KeyCounter, layout, world, player): + if counter.big_key_opened: + ttl_locations = len(counter.free_locations) + else: + ttl_locations = len([x for x in counter.free_locations if '- Big Chest' not in x.name]) + + if door.smallKey: + # ttl_small_key_only = len(counter.key_only_locations) + return cnt_avail_small_locations_by_ctr(ttl_locations, counter, layout, world, player) > 0 + elif door.bigKey: + available_big_locations = cnt_avail_big_locations_by_ctr(ttl_locations, counter, layout, world, player) + return not counter.big_key_opened and available_big_locations > 0 and not layout.big_key_special + else: + return True + + def create_key_counter(state, key_layout, world, player): key_counter = KeyCounter(key_layout.max_chests) key_counter.child_doors.update(dict.fromkeys(unique_doors(state.small_doors+state.big_doors+state.prize_doors)))