@@ -160,7 +160,7 @@ def vanilla_key_logic(world, player):
|
|||||||
world.key_logic[player][builder.name] = key_layout.key_logic
|
world.key_logic[player][builder.name] = key_layout.key_logic
|
||||||
log_key_logic(builder.name, key_layout.key_logic)
|
log_key_logic(builder.name, key_layout.key_logic)
|
||||||
last_key = None
|
last_key = None
|
||||||
if world.shuffle[player] == 'vanilla':
|
if world.shuffle[player] == 'vanilla' and world.accessibility[player] == 'items':
|
||||||
validate_vanilla_key_logic(world, player)
|
validate_vanilla_key_logic(world, player)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import random
|
import random
|
||||||
import collections
|
import collections
|
||||||
|
import itertools
|
||||||
from collections import defaultdict, deque
|
from collections import defaultdict, deque
|
||||||
from enum import Enum, unique
|
from enum import Enum, unique
|
||||||
import logging
|
import logging
|
||||||
@@ -1467,20 +1468,32 @@ def assign_polarized_sectors(dungeon_map, polarized_sectors, global_pole, logger
|
|||||||
problem_builders = identify_branching_issues_2(problem_builders)
|
problem_builders = identify_branching_issues_2(problem_builders)
|
||||||
|
|
||||||
# step 5: assign randomly until gone - must maintain connectedness, neutral polarity, branching, lack, etc.
|
# step 5: assign randomly until gone - must maintain connectedness, neutral polarity, branching, lack, etc.
|
||||||
|
comb_w_replace = len(dungeon_map) ** len(neutral_choices)
|
||||||
|
combinations = None
|
||||||
|
if comb_w_replace <= 1000:
|
||||||
|
combinations = list(itertools.product(dungeon_map.keys(), repeat=len(neutral_choices)))
|
||||||
|
random.shuffle(combinations)
|
||||||
tries = 0
|
tries = 0
|
||||||
while len(polarized_sectors) > 0:
|
while len(polarized_sectors) > 0:
|
||||||
if tries > 100:
|
if tries > 1000 or (combinations and tries >= len(combinations)):
|
||||||
raise Exception('No valid assignment found. Ref: %s' % next(iter(dungeon_map.keys())))
|
raise Exception('No valid assignment found. Ref: %s' % next(iter(dungeon_map.keys())))
|
||||||
choices = random.choices(list(dungeon_map.keys()), k=len(neutral_choices))
|
if combinations:
|
||||||
valid = []
|
choices = combinations[tries]
|
||||||
|
else:
|
||||||
|
choices = random.choices(list(dungeon_map.keys()), k=len(neutral_choices))
|
||||||
|
chosen_sectors = defaultdict(list)
|
||||||
for i, choice in enumerate(choices):
|
for i, choice in enumerate(choices):
|
||||||
builder = dungeon_map[choice]
|
chosen_sectors[choice].extend(neutral_choices[i])
|
||||||
if valid_assignment(builder, neutral_choices[i]):
|
all_valid = True
|
||||||
|
for name, sector_list in chosen_sectors.items():
|
||||||
|
if not valid_assignment(dungeon_map[name], sector_list):
|
||||||
|
all_valid = False
|
||||||
|
break
|
||||||
|
if all_valid:
|
||||||
|
for i, choice in enumerate(choices):
|
||||||
|
builder = dungeon_map[choice]
|
||||||
for sector in neutral_choices[i]:
|
for sector in neutral_choices[i]:
|
||||||
assign_sector(sector, builder, polarized_sectors, global_pole)
|
assign_sector(sector, builder, polarized_sectors, global_pole)
|
||||||
valid.append(neutral_choices[i])
|
|
||||||
for c in valid:
|
|
||||||
neutral_choices.remove(c)
|
|
||||||
tries += 1
|
tries += 1
|
||||||
|
|
||||||
|
|
||||||
@@ -1931,6 +1944,7 @@ def resolve_equations(builder, sector_list):
|
|||||||
# negative benefit transforms (dead end)
|
# negative benefit transforms (dead end)
|
||||||
def find_priority_equation(equations, current_access):
|
def find_priority_equation(equations, current_access):
|
||||||
flex = calc_flex(equations, current_access)
|
flex = calc_flex(equations, current_access)
|
||||||
|
required = calc_required(equations, current_access)
|
||||||
best_profit = None
|
best_profit = None
|
||||||
triplet_candidates = []
|
triplet_candidates = []
|
||||||
local_profit_map = {}
|
local_profit_map = {}
|
||||||
@@ -1951,14 +1965,17 @@ def find_priority_equation(equations, current_access):
|
|||||||
else:
|
else:
|
||||||
triplet_candidates.append((eq, eq_list, sector))
|
triplet_candidates.append((eq, eq_list, sector))
|
||||||
local_profit_map[sector] = best_local_profit
|
local_profit_map[sector] = best_local_profit
|
||||||
if len(triplet_candidates) == 0:
|
filtered_candidates = filter_requirements(triplet_candidates, equations, required, current_access)
|
||||||
|
if len(filtered_candidates) == 0:
|
||||||
|
filtered_candidates = triplet_candidates
|
||||||
|
if len(filtered_candidates) == 0:
|
||||||
return None, None, None # can't pay for anything
|
return None, None, None # can't pay for anything
|
||||||
if len(triplet_candidates) == 1:
|
if len(filtered_candidates) == 1:
|
||||||
return triplet_candidates[0]
|
return filtered_candidates[0]
|
||||||
|
|
||||||
required_candidates = [x for x in triplet_candidates if x[0].required]
|
required_candidates = [x for x in filtered_candidates if x[0].required]
|
||||||
if len(required_candidates) == 0:
|
if len(required_candidates) == 0:
|
||||||
required_candidates = triplet_candidates
|
required_candidates = filtered_candidates
|
||||||
if len(required_candidates) == 1:
|
if len(required_candidates) == 1:
|
||||||
return required_candidates[0]
|
return required_candidates[0]
|
||||||
|
|
||||||
@@ -1974,6 +1991,46 @@ def find_priority_equation(equations, current_access):
|
|||||||
return good_local_candidates[0] # just pick one I guess
|
return good_local_candidates[0] # just pick one I guess
|
||||||
|
|
||||||
|
|
||||||
|
def calc_required(equations, current_access):
|
||||||
|
ttl = 0
|
||||||
|
for num in current_access.values():
|
||||||
|
ttl += num
|
||||||
|
local_profit_map = {}
|
||||||
|
for sector, eq_list in equations.items():
|
||||||
|
best_local_profit = None
|
||||||
|
for eq in eq_list:
|
||||||
|
profit = eq.profit()
|
||||||
|
if best_local_profit is None or profit > best_local_profit:
|
||||||
|
best_local_profit = profit
|
||||||
|
local_profit_map[sector] = best_local_profit
|
||||||
|
ttl += best_local_profit
|
||||||
|
if ttl == 0:
|
||||||
|
new_lists = {}
|
||||||
|
for sector, eq_list in equations.items():
|
||||||
|
if len(eq_list) > 1:
|
||||||
|
rem_list = []
|
||||||
|
for eq in eq_list:
|
||||||
|
if eq.profit() < local_profit_map[sector]:
|
||||||
|
rem_list.append(eq)
|
||||||
|
if len(rem_list) > 0:
|
||||||
|
new_lists[sector] = [x for x in eq_list if x not in rem_list]
|
||||||
|
for sector, eq_list in new_lists.items():
|
||||||
|
if len(eq_list) <= 1:
|
||||||
|
for eq in eq_list:
|
||||||
|
eq.required = True
|
||||||
|
equations[sector] = eq_list
|
||||||
|
required_costs = defaultdict(int)
|
||||||
|
required_benefits = defaultdict(int)
|
||||||
|
for sector, eq_list in equations.items():
|
||||||
|
for eq in eq_list:
|
||||||
|
if eq.required:
|
||||||
|
for key, door_list in eq.cost.items():
|
||||||
|
required_costs[key] += len(door_list)
|
||||||
|
for key, door_list in eq.benefit.items():
|
||||||
|
required_benefits[key] += len(door_list)
|
||||||
|
return required_costs, required_benefits
|
||||||
|
|
||||||
|
|
||||||
def calc_flex(equations, current_access):
|
def calc_flex(equations, current_access):
|
||||||
flex_spending = defaultdict(int)
|
flex_spending = defaultdict(int)
|
||||||
required_costs = defaultdict(int)
|
required_costs = defaultdict(int)
|
||||||
@@ -1987,6 +2044,45 @@ def calc_flex(equations, current_access):
|
|||||||
return flex_spending
|
return flex_spending
|
||||||
|
|
||||||
|
|
||||||
|
def filter_requirements(triplet_candidates, equations, required, current_access):
|
||||||
|
r_costs, r_exits = required
|
||||||
|
valid_candidates = []
|
||||||
|
for cand, cand_list, cand_sector in triplet_candidates:
|
||||||
|
valid = True
|
||||||
|
if not cand.required:
|
||||||
|
potential_benefit = defaultdict(int)
|
||||||
|
potential_costs = defaultdict(int)
|
||||||
|
for h_type, benefit in current_access.items():
|
||||||
|
cur_cost = len(cand.cost[h_type])
|
||||||
|
if benefit - cur_cost > 0:
|
||||||
|
potential_benefit[h_type] += benefit - cur_cost
|
||||||
|
for h_type, benefit_list in cand.benefit.items():
|
||||||
|
potential_benefit[h_type] += len(benefit_list)
|
||||||
|
for sector, eq_list in equations.items():
|
||||||
|
if sector == cand_sector:
|
||||||
|
affected_doors = [d for x in cand.benefit.values() for d in x] + [d for x in cand.cost.values() for d in x]
|
||||||
|
adj_list = [x for x in eq_list if x.door not in affected_doors]
|
||||||
|
else:
|
||||||
|
adj_list = eq_list
|
||||||
|
for eq in adj_list:
|
||||||
|
for h_type, benefit_list in eq.benefit.items():
|
||||||
|
potential_benefit[h_type] += len(benefit_list)
|
||||||
|
for h_type, cost_list in eq.cost.items():
|
||||||
|
potential_costs[h_type] += len(cost_list)
|
||||||
|
for h_type, requirement in r_costs.items():
|
||||||
|
if requirement > 0 and potential_benefit[h_type] < requirement:
|
||||||
|
valid = False
|
||||||
|
break
|
||||||
|
if valid:
|
||||||
|
for h_type, requirement in r_exits.items():
|
||||||
|
if requirement > 0 and potential_costs[h_type] < requirement:
|
||||||
|
valid = False
|
||||||
|
break
|
||||||
|
if valid:
|
||||||
|
valid_candidates.append((cand, cand_list, cand_sector))
|
||||||
|
return valid_candidates
|
||||||
|
|
||||||
|
|
||||||
def resolve_equation(equation, eq_list, sector, current_access, reached_doors, equations):
|
def resolve_equation(equation, eq_list, sector, current_access, reached_doors, equations):
|
||||||
for key, door_list in equation.cost.items():
|
for key, door_list in equation.cost.items():
|
||||||
if current_access[key] - len(door_list) < 0:
|
if current_access[key] - len(door_list) < 0:
|
||||||
|
|||||||
2
Fill.py
2
Fill.py
@@ -233,7 +233,7 @@ def valid_key_placement(item, location, itempool, world):
|
|||||||
if dungeon.name not in item.name and (dungeon.name != 'Hyrule Castle' or 'Escape' not in item.name):
|
if dungeon.name not in item.name and (dungeon.name != 'Hyrule Castle' or 'Escape' not in item.name):
|
||||||
return True
|
return True
|
||||||
key_logic = world.key_logic[item.player][dungeon.name]
|
key_logic = world.key_logic[item.player][dungeon.name]
|
||||||
unplaced_keys = len([x for x in itempool if x.name == key_logic.small_key_name])
|
unplaced_keys = len([x for x in itempool if x.name == key_logic.small_key_name and x.player == item.player])
|
||||||
return key_logic.check_placement(unplaced_keys)
|
return key_logic.check_placement(unplaced_keys)
|
||||||
else:
|
else:
|
||||||
inside_dungeon_item = ((item.smallkey and not world.keyshuffle[item.player])
|
inside_dungeon_item = ((item.smallkey and not world.keyshuffle[item.player])
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ def analyze_dungeon(key_layout, world, player):
|
|||||||
key_layout.key_counters = create_key_counters(key_layout, world, player)
|
key_layout.key_counters = create_key_counters(key_layout, world, player)
|
||||||
key_logic = key_layout.key_logic
|
key_logic = key_layout.key_logic
|
||||||
|
|
||||||
find_bk_locked_sections(key_layout, world)
|
find_bk_locked_sections(key_layout, world, player)
|
||||||
key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations))
|
key_logic.bk_chests.update(find_big_chest_locations(key_layout.all_chest_locations))
|
||||||
if world.retro[player] and world.mode[player] != 'standard':
|
if world.retro[player] and world.mode[player] != 'standard':
|
||||||
return
|
return
|
||||||
@@ -304,14 +304,14 @@ def queue_sorter_2(queue_item):
|
|||||||
return 1 if door.bigKey else 0
|
return 1 if door.bigKey else 0
|
||||||
|
|
||||||
|
|
||||||
def find_bk_locked_sections(key_layout, world):
|
def find_bk_locked_sections(key_layout, world, player):
|
||||||
if key_layout.big_key_special:
|
if key_layout.big_key_special:
|
||||||
return
|
return
|
||||||
key_counters = key_layout.key_counters
|
key_counters = key_layout.key_counters
|
||||||
key_logic = key_layout.key_logic
|
key_logic = key_layout.key_logic
|
||||||
|
|
||||||
bk_key_not_required = set()
|
bk_key_not_required = set()
|
||||||
big_chest_allowed_big_key = world.accessibility != 'locations'
|
big_chest_allowed_big_key = world.accessibility[player] != 'locations'
|
||||||
for counter in key_counters.values():
|
for counter in key_counters.values():
|
||||||
key_layout.all_chest_locations.update(counter.free_locations)
|
key_layout.all_chest_locations.update(counter.free_locations)
|
||||||
key_layout.all_locations.update(counter.free_locations)
|
key_layout.all_locations.update(counter.free_locations)
|
||||||
@@ -1450,8 +1450,8 @@ def validate_key_placement(key_layout, world, player):
|
|||||||
if not can_progress:
|
if not can_progress:
|
||||||
missing_locations = set(max_counter.free_locations.keys()).difference(found_locations)
|
missing_locations = set(max_counter.free_locations.keys()).difference(found_locations)
|
||||||
missing_items = [l for l in missing_locations if l.item is None or (l.item.name != smallkey_name and l.item != dungeon.big_key) or "- Boss" in l.name]
|
missing_items = [l for l in missing_locations if l.item is None or (l.item.name != smallkey_name and l.item != dungeon.big_key) or "- Boss" in l.name]
|
||||||
#missing_key_only = set(max_counter.key_only_locations.keys()).difference(counter.key_only_locations.keys()) # do freestanding keys matter for locations?
|
# missing_key_only = set(max_counter.key_only_locations.keys()).difference(counter.key_only_locations.keys()) # do freestanding keys matter for locations?
|
||||||
if len(missing_items) > 0: #world.accessibility[player]=='locations' and (len(missing_locations)>0 or len(missing_key_only) > 0):
|
if len(missing_items) > 0: # world.accessibility[player]=='locations' and (len(missing_locations)>0 or len(missing_key_only) > 0):
|
||||||
logging.getLogger('').error("Keylock - can't open locations: ")
|
logging.getLogger('').error("Keylock - can't open locations: ")
|
||||||
for i in missing_locations:
|
for i in missing_locations:
|
||||||
logging.getLogger('').error(i)
|
logging.getLogger('').error(i)
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -24,7 +24,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
|
|||||||
from ItemList import generate_itempool, difficulties, fill_prizes
|
from ItemList import generate_itempool, difficulties, fill_prizes
|
||||||
from Utils import output_path, parse_player_names
|
from Utils import output_path, parse_player_names
|
||||||
|
|
||||||
__version__ = '0.0.17pre'
|
__version__ = '0.0.17.2p'
|
||||||
|
|
||||||
|
|
||||||
def main(args, seed=None):
|
def main(args, seed=None):
|
||||||
|
|||||||
Reference in New Issue
Block a user