More & better forced connection detection
This commit is contained in:
@@ -1024,6 +1024,30 @@ class Direction(Enum):
|
|||||||
Up = 4
|
Up = 4
|
||||||
Down = 5
|
Down = 5
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class Hook(Enum):
|
||||||
|
North = 0
|
||||||
|
West = 1
|
||||||
|
South = 2
|
||||||
|
East = 3
|
||||||
|
Stairs = 4
|
||||||
|
|
||||||
|
|
||||||
|
hook_dir_map = {
|
||||||
|
Direction.North: Hook.North,
|
||||||
|
Direction.South: Hook.South,
|
||||||
|
Direction.West: Hook.West,
|
||||||
|
Direction.East: Hook.East,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def hook_from_door(door):
|
||||||
|
if door.type == DoorType.SpiralStairs:
|
||||||
|
return Hook.Stairs
|
||||||
|
if door.type == DoorType.Normal:
|
||||||
|
return hook_dir_map[door.direction]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class Polarity:
|
class Polarity:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -1282,6 +1306,13 @@ class Sector(object):
|
|||||||
magnitude[idx] = magnitude[idx] + 1
|
magnitude[idx] = magnitude[idx] + 1
|
||||||
return magnitude
|
return magnitude
|
||||||
|
|
||||||
|
def hook_magnitude(self):
|
||||||
|
magnitude = [0] * len(Hook)
|
||||||
|
for door in self.outstanding_doors:
|
||||||
|
idx = hook_from_door(door).value
|
||||||
|
magnitude[idx] = magnitude[idx] + 1
|
||||||
|
return magnitude
|
||||||
|
|
||||||
def outflow(self):
|
def outflow(self):
|
||||||
outflow = 0
|
outflow = 0
|
||||||
for door in self.outstanding_doors:
|
for door in self.outstanding_doors:
|
||||||
|
|||||||
@@ -2,26 +2,17 @@ import random
|
|||||||
import collections
|
import collections
|
||||||
import itertools
|
import itertools
|
||||||
from collections import defaultdict, deque
|
from collections import defaultdict, deque
|
||||||
from enum import Enum, unique
|
|
||||||
import logging
|
import logging
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
import operator as op
|
import operator as op
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, Sector, PolSlot, flooded_keys
|
from BaseClasses import DoorType, Direction, CrystalBarrier, RegionType, Polarity, PolSlot, flooded_keys
|
||||||
|
from BaseClasses import Hook, hook_from_door
|
||||||
from Regions import key_only_locations, dungeon_events, flooded_keys_reverse
|
from Regions import key_only_locations, dungeon_events, flooded_keys_reverse
|
||||||
from Dungeons import dungeon_regions
|
from Dungeons import dungeon_regions
|
||||||
|
|
||||||
|
|
||||||
@unique
|
|
||||||
class Hook(Enum):
|
|
||||||
North = 0
|
|
||||||
West = 1
|
|
||||||
South = 2
|
|
||||||
East = 3
|
|
||||||
Stairs = 4
|
|
||||||
|
|
||||||
|
|
||||||
class GraphPiece:
|
class GraphPiece:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -515,43 +506,32 @@ def filter_for_potential_bk_locations(locations):
|
|||||||
and x.name not in key_only_locations.keys() and x.name not in ['Agahnim 1', 'Agahnim 2']]
|
and x.name not in key_only_locations.keys() and x.name not in ['Agahnim 1', 'Agahnim 2']]
|
||||||
|
|
||||||
|
|
||||||
def opposite_h_type(h_type):
|
type_map = {
|
||||||
type_map = {
|
Hook.Stairs: Hook.Stairs,
|
||||||
Hook.Stairs: Hook.Stairs,
|
Hook.North: Hook.South,
|
||||||
Hook.North: Hook.South,
|
Hook.South: Hook.North,
|
||||||
Hook.South: Hook.North,
|
Hook.West: Hook.East,
|
||||||
Hook.West: Hook.East,
|
Hook.East: Hook.West,
|
||||||
Hook.East: Hook.West,
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
def opposite_h_type(h_type):
|
||||||
return type_map[h_type]
|
return type_map[h_type]
|
||||||
|
|
||||||
|
|
||||||
def hook_from_door(door):
|
hang_dir_map = {
|
||||||
if door.type == DoorType.SpiralStairs:
|
Direction.North: Hook.South,
|
||||||
return Hook.Stairs
|
Direction.South: Hook.North,
|
||||||
if door.type == DoorType.Normal:
|
Direction.West: Hook.East,
|
||||||
dir = {
|
Direction.East: Hook.West,
|
||||||
Direction.North: Hook.North,
|
}
|
||||||
Direction.South: Hook.South,
|
|
||||||
Direction.West: Hook.West,
|
|
||||||
Direction.East: Hook.East,
|
|
||||||
}
|
|
||||||
return dir[door.direction]
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def hanger_from_door(door):
|
def hanger_from_door(door):
|
||||||
if door.type == DoorType.SpiralStairs:
|
if door.type == DoorType.SpiralStairs:
|
||||||
return Hook.Stairs
|
return Hook.Stairs
|
||||||
if door.type == DoorType.Normal:
|
if door.type == DoorType.Normal:
|
||||||
dir = {
|
return hang_dir_map[door.direction]
|
||||||
Direction.North: Hook.South,
|
|
||||||
Direction.South: Hook.North,
|
|
||||||
Direction.West: Hook.East,
|
|
||||||
Direction.East: Hook.West,
|
|
||||||
}
|
|
||||||
return dir[door.direction]
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -1443,6 +1423,15 @@ def sum_magnitude(sector_list):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def sum_hook_magnitude(sector_list):
|
||||||
|
result = [0] * len(Hook)
|
||||||
|
for sector in sector_list:
|
||||||
|
vector = sector.hook_magnitude()
|
||||||
|
for i in range(len(result)):
|
||||||
|
result[i] = result[i] + vector[i]
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def sum_polarity(sector_list):
|
def sum_polarity(sector_list):
|
||||||
pol = Polarity()
|
pol = Polarity()
|
||||||
for sector in sector_list:
|
for sector in sector_list:
|
||||||
@@ -1944,6 +1933,7 @@ def balance_split(candidate_sectors, dungeon_map, global_pole):
|
|||||||
logger = logging.getLogger('')
|
logger = logging.getLogger('')
|
||||||
# categorize sectors
|
# categorize sectors
|
||||||
check_for_forced_dead_ends(dungeon_map, candidate_sectors, global_pole)
|
check_for_forced_dead_ends(dungeon_map, candidate_sectors, global_pole)
|
||||||
|
check_for_forced_assignments(dungeon_map, candidate_sectors, global_pole)
|
||||||
crystal_switches, crystal_barriers, neutral_sectors, polarized_sectors = categorize_sectors(candidate_sectors)
|
crystal_switches, crystal_barriers, neutral_sectors, polarized_sectors = categorize_sectors(candidate_sectors)
|
||||||
leftover = assign_crystal_switch_sectors(dungeon_map, crystal_switches, global_pole, len(crystal_barriers) > 0)
|
leftover = assign_crystal_switch_sectors(dungeon_map, crystal_switches, global_pole, len(crystal_barriers) > 0)
|
||||||
for sector in leftover:
|
for sector in leftover:
|
||||||
@@ -1966,20 +1956,21 @@ def check_for_forced_dead_ends(dungeon_map, candidate_sectors, global_pole):
|
|||||||
other_sectors = [x for x in candidate_sectors if x not in dead_end_sectors]
|
other_sectors = [x for x in candidate_sectors if x not in dead_end_sectors]
|
||||||
for name, builder in dungeon_map.items():
|
for name, builder in dungeon_map.items():
|
||||||
other_sectors += builder.sectors
|
other_sectors += builder.sectors
|
||||||
other_magnitude = sum_magnitude(other_sectors)
|
other_magnitude = sum_hook_magnitude(other_sectors)
|
||||||
dead_cnt = [0] * len(PolSlot)
|
dead_cnt = [0] * len(Hook)
|
||||||
for sector in dead_end_sectors:
|
for sector in dead_end_sectors:
|
||||||
pol = sector.polarity()
|
hook_mag = sector.hook_magnitude()
|
||||||
for slot in PolSlot:
|
for hook in Hook:
|
||||||
if pol.vector[slot.value] != 0:
|
if hook_mag[hook.value] != 0:
|
||||||
dead_cnt[slot.value] += 1
|
dead_cnt[hook.value] += 1
|
||||||
for slot in PolSlot:
|
for hook in Hook:
|
||||||
if dead_cnt[slot.value] > other_magnitude[slot.value]:
|
opp = opposite_h_type(hook)
|
||||||
|
if dead_cnt[hook.value] > other_magnitude[opp.value]:
|
||||||
raise Exception('Impossible to satisfy all these dead ends')
|
raise Exception('Impossible to satisfy all these dead ends')
|
||||||
elif dead_cnt[slot.value] == other_magnitude[slot.value]:
|
elif dead_cnt[hook.value] == other_magnitude[opp.value]:
|
||||||
candidates = [x for x in dead_end_sectors if x.magnitude()[slot.value] > 0]
|
candidates = [x for x in dead_end_sectors if x.hook_magnitude()[hook.value] > 0]
|
||||||
for sector in other_sectors:
|
for sector in other_sectors:
|
||||||
if sector.magnitude()[slot.value] > 0 and sector.is_entrance_sector() and sector.branching_factor() == 2:
|
if sector.hook_magnitude()[opp.value] > 0 and sector.is_entrance_sector() and sector.branching_factor() == 2:
|
||||||
builder = None
|
builder = None
|
||||||
for b in dungeon_map.values():
|
for b in dungeon_map.values():
|
||||||
if sector in b.sectors:
|
if sector in b.sectors:
|
||||||
@@ -1996,6 +1987,31 @@ def check_for_forced_dead_ends(dungeon_map, candidate_sectors, global_pole):
|
|||||||
builder.c_locked = True
|
builder.c_locked = True
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_forced_assignments(dungeon_map, candidate_sectors, global_pole):
|
||||||
|
done = False
|
||||||
|
while not done:
|
||||||
|
done = True
|
||||||
|
magnitude = sum_hook_magnitude(candidate_sectors)
|
||||||
|
dungeon_hooks = {}
|
||||||
|
for name, builder in dungeon_map.items():
|
||||||
|
dungeon_hooks[name] = sum_hook_magnitude(builder.sectors)
|
||||||
|
for val in Hook:
|
||||||
|
if magnitude[val.value] == 1:
|
||||||
|
found_hooks = []
|
||||||
|
opp = opposite_h_type(val)
|
||||||
|
for name, hooks in dungeon_hooks.items():
|
||||||
|
if hooks[opp.value] > 0 and not dungeon_map[name].c_locked:
|
||||||
|
found_hooks.append(name)
|
||||||
|
if len(found_hooks) == 1:
|
||||||
|
done = False
|
||||||
|
forced_sector = None
|
||||||
|
for sec in candidate_sectors:
|
||||||
|
if sec.hook_magnitude()[val.value] > 0:
|
||||||
|
forced_sector = sec
|
||||||
|
break
|
||||||
|
assign_sector(forced_sector, dungeon_map[found_hooks[0]], candidate_sectors, global_pole)
|
||||||
|
|
||||||
|
|
||||||
def check_crystal(dead_end, entrance):
|
def check_crystal(dead_end, entrance):
|
||||||
if dead_end.blue_barrier and not entrance.c_switch and not dead_end.c_switch:
|
if dead_end.blue_barrier and not entrance.c_switch and not dead_end.c_switch:
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -265,10 +265,10 @@ def generate_itempool(world, player):
|
|||||||
amt = world.pool_adjustment[player]
|
amt = world.pool_adjustment[player]
|
||||||
if amt < 0:
|
if amt < 0:
|
||||||
for i in range(0, amt):
|
for i in range(0, amt):
|
||||||
pool.remove(get_custom_array_key('Rupees (20)'))
|
pool.remove('Rupees (20)')
|
||||||
elif amt > 0:
|
elif amt > 0:
|
||||||
for i in range(0, amt):
|
for i in range(0, amt):
|
||||||
pool.append(get_custom_array_key('Rupees (20)'))
|
pool.append('Rupees (20)')
|
||||||
|
|
||||||
for item in precollected_items:
|
for item in precollected_items:
|
||||||
world.push_precollected(ItemFactory(item, player))
|
world.push_precollected(ItemFactory(item, player))
|
||||||
|
|||||||
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.f-dev'
|
__version__ = '0.0.g-dev'
|
||||||
|
|
||||||
|
|
||||||
def main(args, seed=None):
|
def main(args, seed=None):
|
||||||
|
|||||||
Reference in New Issue
Block a user