More key logic fixes
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import logging
|
||||
from collections import defaultdict, deque
|
||||
|
||||
from Regions import dungeon_events
|
||||
from BaseClasses import DoorType
|
||||
from Regions import dungeon_events, key_only_locations
|
||||
from Dungeons import dungeon_keys, dungeon_bigs
|
||||
from DungeonGenerator import ExplorationState
|
||||
|
||||
@@ -98,7 +99,7 @@ def build_key_layout(builder, start_regions, proposal, world, player):
|
||||
def calc_max_chests(builder, key_layout, world, player):
|
||||
if world.doorShuffle[player] != 'crossed':
|
||||
return len(world.get_dungeon(key_layout.sector.name, player).small_keys)
|
||||
return builder.key_doors_num - key_layout.max_drops
|
||||
return max(0, builder.key_doors_num - key_layout.max_drops)
|
||||
|
||||
|
||||
def analyze_dungeon(key_layout, world, player):
|
||||
@@ -124,7 +125,7 @@ def analyze_dungeon(key_layout, world, player):
|
||||
avail_bigs = exist_relevant_big_doors(key_counter, key_layout)
|
||||
non_big_locs = count_locations_big_optional(key_counter.free_locations)
|
||||
if not key_counter.big_key_opened:
|
||||
if chest_keys == non_big_locs and available <= possible_smalls and not avail_bigs:
|
||||
if chest_keys == non_big_locs and chest_keys > 0 and available <= possible_smalls and not avail_bigs:
|
||||
key_logic.bk_restricted.update(filter_big_chest(key_counter.free_locations))
|
||||
if not key_counter.big_key_opened and big_chest_in_locations(key_counter.free_locations):
|
||||
key_logic.sm_restricted.update(find_big_chest_locations(key_counter.free_locations))
|
||||
@@ -308,7 +309,8 @@ def key_wasted(new_door, old_door, old_counter, new_counter, key_layout, world,
|
||||
new_avail = new_chest_keys + new_key_diff
|
||||
if new_key_diff < old_key_diff or new_avail < old_avail:
|
||||
return True
|
||||
if new_avail == old_avail:
|
||||
if new_avail >= old_avail:
|
||||
wasted_keys = 0
|
||||
old_children = old_counter.child_doors.keys()
|
||||
new_children = [x for x in new_counter.child_doors.keys() if x != old_door and x.dest != old_door and (not x.bigKey or x not in old_children)]
|
||||
current_counter = new_counter
|
||||
@@ -319,6 +321,8 @@ def key_wasted(new_door, old_door, old_counter, new_counter, key_layout, world,
|
||||
bk_open = bk_opened or new_door.bigKey
|
||||
new_counter = find_counter(proposed_doors, bk_open, key_layout)
|
||||
if key_wasted(new_child, old_door, current_counter, new_counter, key_layout, world, player):
|
||||
wasted_keys += 1
|
||||
if new_avail - wasted_keys < old_avail:
|
||||
return True # waste is possible
|
||||
return False
|
||||
|
||||
@@ -476,11 +480,13 @@ def bk_restricted_rules(rule, door, odd_counter, empty_flag, key_counter, key_la
|
||||
ignored_doors = {**ignored_doors, **dict.fromkeys(dest_ignored)}
|
||||
post_counter = open_some_counter(door_open, key_layout, ignored_doors.keys())
|
||||
unique_loc = dict_difference(post_counter.free_locations, best_counter.free_locations)
|
||||
if bk_rule.is_valid and len(unique_loc) > 0:
|
||||
# todo: figure out the intention behind this change - better way to detect the big key is blocking needed key onlys?
|
||||
if len(unique_loc) > 0: # and bk_rule.is_valid
|
||||
rule.alternate_small_key = bk_rule.small_key_num
|
||||
rule.alternate_big_key_loc.update(unique_loc)
|
||||
elif not bk_rule.is_valid:
|
||||
key_layout.key_logic.bk_restricted.update(unique_loc)
|
||||
# elif not bk_rule.is_valid:
|
||||
# key_layout.key_logic.bk_restricted.update(unique_loc)
|
||||
|
||||
|
||||
def open_a_door(door, child_state, flat_proposal):
|
||||
if door.bigKey:
|
||||
@@ -494,7 +500,7 @@ def open_a_door(door, child_state, flat_proposal):
|
||||
child_state.small_doors[:] = [x for x in child_state.small_doors if x.door != door]
|
||||
child_state.avail_doors.extend(doors_to_open)
|
||||
dest_door = door.dest
|
||||
if dest_door in flat_proposal:
|
||||
if dest_door in flat_proposal and door.type != DoorType.SpiralStairs:
|
||||
child_state.opened_doors.append(dest_door)
|
||||
if child_state.in_door_list_ic(dest_door, child_state.small_doors):
|
||||
now_available = [x for x in child_state.small_doors if x.door == dest_door]
|
||||
@@ -559,10 +565,26 @@ def filter_big_chest(locations):
|
||||
return [x for x in locations if '- Big Chest' not in x.name]
|
||||
|
||||
|
||||
def count_free_locations(state):
|
||||
cnt = 0
|
||||
for loc in state.found_locations:
|
||||
if '- Prize' not in loc.name and loc.name not in dungeon_events and loc.name not in key_only_locations and loc.name not in ['Agahnim 1', 'Agahnim 2', 'Hyrule Castle - Big Key Drop']:
|
||||
cnt += 1
|
||||
return cnt
|
||||
|
||||
|
||||
def count_locations_exclude_big_chest(state):
|
||||
cnt = 0
|
||||
for loc in state.found_locations:
|
||||
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc.name not in dungeon_events and loc.name not in ['Agahnim 1', 'Agahnim 2', 'Hyrule Castle - Big Key Drop']:
|
||||
if '- Big Chest' not in loc.name and '- Prize' not in loc.name and loc.name not in dungeon_events and loc.name not in key_only_locations and loc.name not in ['Agahnim 1', 'Agahnim 2', 'Hyrule Castle - Big Key Drop']:
|
||||
cnt += 1
|
||||
return cnt
|
||||
|
||||
|
||||
def count_key_only_locations(state):
|
||||
cnt = 0
|
||||
for loc in state.found_locations:
|
||||
if loc.name in key_only_locations:
|
||||
cnt += 1
|
||||
return cnt
|
||||
|
||||
@@ -784,6 +806,8 @@ def reduce_rules(small_rules, collected, collected_alt):
|
||||
|
||||
# Soft lock stuff
|
||||
def validate_key_layout(key_layout, world, player):
|
||||
if world.retro[player]: # retro is all good - don't care how the doors are laid out
|
||||
return True
|
||||
flat_proposal = key_layout.flat_prop
|
||||
state = ExplorationState(dungeon=key_layout.sector.name)
|
||||
state.key_locations = key_layout.max_chests
|
||||
@@ -800,8 +824,10 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
|
||||
num_bigs = 1 if len(state.big_doors) > 0 else 0 # all or nothing
|
||||
if not smalls_avail and num_bigs == 0:
|
||||
return True # I think that's the end
|
||||
ttl_locations = state.ttl_locations if state.big_key_opened else count_locations_exclude_big_chest(state)
|
||||
available_small_locations = cnt_avail_small_locations(key_layout, ttl_locations, state, world, player)
|
||||
# todo: fix state to separate out these types
|
||||
ttl_locations = count_free_locations(state) if state.big_key_opened else count_locations_exclude_big_chest(state)
|
||||
ttl_key_only = count_key_only_locations(state)
|
||||
available_small_locations = cnt_avail_small_locations(ttl_locations + ttl_key_only, state, world, player)
|
||||
available_big_locations = cnt_avail_big_locations(ttl_locations, state, world, player)
|
||||
if (not smalls_avail or available_small_locations == 0) and (state.big_key_opened or num_bigs == 0 or available_big_locations == 0):
|
||||
return False
|
||||
@@ -810,8 +836,9 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
|
||||
for exp_door in state.small_doors:
|
||||
state_copy = state.copy()
|
||||
open_a_door(exp_door.door, state_copy, flat_proposal)
|
||||
state_copy.used_locations += 1
|
||||
state_copy.used_smalls += 1
|
||||
if state_copy.used_smalls > ttl_key_only:
|
||||
state_copy.used_locations += 1
|
||||
code = state_id(state_copy, flat_proposal)
|
||||
if code not in checked_states.keys():
|
||||
valid = validate_key_layout_sub_loop(key_layout, state_copy, checked_states, flat_proposal, world, player)
|
||||
@@ -835,7 +862,7 @@ def validate_key_layout_sub_loop(key_layout, state, checked_states, flat_proposa
|
||||
return True
|
||||
|
||||
|
||||
def cnt_avail_small_locations(key_layout, ttl_locations, state, world, player):
|
||||
def cnt_avail_small_locations(ttl_locations, state, world, player):
|
||||
if not world.keyshuffle[player] and not world.retro[player]:
|
||||
return min(ttl_locations - state.used_locations, state.key_locations - state.used_smalls)
|
||||
return state.key_locations - state.used_smalls
|
||||
@@ -843,7 +870,7 @@ def cnt_avail_small_locations(key_layout, ttl_locations, state, world, player):
|
||||
|
||||
def cnt_avail_big_locations(ttl_locations, state, world, player):
|
||||
if not world.bigkeyshuffle[player]:
|
||||
return ttl_locations - state.used_locations if not state.big_key_special else 0
|
||||
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
|
||||
|
||||
|
||||
|
||||
2
Main.py
2
Main.py
@@ -23,7 +23,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
|
||||
from ItemList import generate_itempool, difficulties, fill_prizes
|
||||
from Utils import output_path, parse_player_names
|
||||
|
||||
__version__ = '0.0.2-pre'
|
||||
__version__ = '0.0.3-pre'
|
||||
|
||||
def main(args, seed=None):
|
||||
if args.outputpath:
|
||||
|
||||
Reference in New Issue
Block a user