More & better forced connection detection

This commit is contained in:
aerinon
2020-03-03 08:25:52 -07:00
parent 83a9c4bdcf
commit 5aef551f7c
4 changed files with 99 additions and 52 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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))

View File

@@ -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):