Unvalidated gen work

This commit is contained in:
aerinon
2020-03-11 14:21:14 -06:00
parent c1783082d8
commit 3fdf9e8624

View File

@@ -1023,6 +1023,7 @@ class DungeonBuilder(object):
self.flex = 0
self.key_door_proposal = None
self.allowance = None
if name in dungeon_dead_end_allowance.keys():
self.allowance = dungeon_dead_end_allowance[name]
elif 'Stonewall' in name:
@@ -1124,7 +1125,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, dunge
logger.info('-Assigning Chest Locations')
assign_location_sectors(dungeon_map, free_location_sectors, global_pole)
logger.info('-Assigning Crystal Switches and Barriers')
leftover = assign_crystal_switch_sectors(dungeon_map, crystal_switches, global_pole)
leftover = assign_crystal_switch_sectors(dungeon_map, crystal_switches, crystal_barriers, global_pole)
for sector in leftover:
if sector.polarity().is_neutral():
neutral_sectors[sector] = None
@@ -1348,7 +1349,7 @@ def minimal_locations(dungeon_name):
return 5
def assign_crystal_switch_sectors(dungeon_map, crystal_switches, global_pole, assign_one=False):
def assign_crystal_switch_sectors(dungeon_map, crystal_switches, crystal_barriers, global_pole, assign_one=False):
population = []
some_c_switches_present = False
for name, builder in dungeon_map.items():
@@ -1358,6 +1359,8 @@ def assign_crystal_switch_sectors(dungeon_map, crystal_switches, global_pole, as
some_c_switches_present = True
if len(population) == 0: # nothing needs a switch
if assign_one and not some_c_switches_present: # something should have one
if len(crystal_switches) == 0:
raise Exception('No crystal switches to assign. Ref %s' % next(iter(dungeon_map.keys())))
valid, builder_choice, switch_choice = False, None, None
switch_candidates = list(crystal_switches)
switch_choice = random.choice(switch_candidates)
@@ -1373,7 +1376,9 @@ def assign_crystal_switch_sectors(dungeon_map, crystal_switches, global_pole, as
choice = random.choice(builder_candidates)
builder_candidates.remove(choice)
builder_choice = dungeon_map[choice]
valid = global_pole.is_valid_choice(dungeon_map, builder_choice, [switch_choice])
test_set = [switch_choice]
test_set.extend(crystal_barriers)
valid = global_pole.is_valid_choice(dungeon_map, builder_choice, test_set)
assign_sector(switch_choice, builder_choice, crystal_switches, global_pole)
return crystal_switches
sector_list = list(crystal_switches)
@@ -1400,45 +1405,48 @@ def assign_crystal_barrier_sectors(dungeon_map, crystal_barriers, global_pole):
def identify_polarity_issues(dungeon_map):
unconnected_builders = {}
for name, builder in dungeon_map.items():
if len(builder.sectors) == 1:
continue
else:
def sector_filter(x, y):
return x != y
identify_polarity_issues_internal(name, builder, unconnected_builders)
return unconnected_builders
def identify_polarity_issues_internal(name, builder, unconnected_builders):
if len(builder.sectors) == 1:
return
else:
def sector_filter(x, y):
return x != y
# else:
# def sector_filter(x, y):
# return x != y and (x.outflow() > 1 or is_entrance_sector(builder, x))
connection_flags = {}
for slot in PolSlot:
connection_flags[slot] = {}
for slot2 in PolSlot:
connection_flags[slot][slot2] = False
for sector in builder.sectors:
others = [x for x in builder.sectors if sector_filter(x, sector)]
other_mag = sum_magnitude(others)
sector_mag = sector.magnitude()
check_flags(sector_mag, connection_flags)
unconnected_sector = True
connection_flags = {}
for slot in PolSlot:
connection_flags[slot] = {}
for slot2 in PolSlot:
connection_flags[slot][slot2] = False
for sector in builder.sectors:
others = [x for x in builder.sectors if sector_filter(x, sector)]
other_mag = sum_magnitude(others)
sector_mag = sector.magnitude()
check_flags(sector_mag, connection_flags)
unconnected_sector = True
for i in PolSlot:
if sector_mag[i.value] == 0 or other_mag[i.value] > 0 or self_connecting(sector, i, sector_mag):
unconnected_sector = False
break
if unconnected_sector:
for i in PolSlot:
if sector_mag[i.value] == 0 or other_mag[i.value] > 0 or self_connecting(sector, i, sector_mag):
unconnected_sector = False
break
if unconnected_sector:
for i in PolSlot:
if sector_mag[i.value] > 0 and other_mag[i.value] == 0 and not self_connecting(sector, i, sector_mag):
builder.mag_needed[i] = [x for x in PolSlot if other_mag[x.value] > 0]
if name not in unconnected_builders.keys():
unconnected_builders[name] = builder
ttl_mag = sum_magnitude(builder.sectors)
for slot in PolSlot:
for slot2 in PolSlot:
if ttl_mag[slot.value] > 0 and ttl_mag[slot2.value] > 0 and not connection_flags[slot][slot2]:
builder.mag_needed[slot] = [slot2]
builder.mag_needed[slot2] = [slot]
if sector_mag[i.value] > 0 and other_mag[i.value] == 0 and not self_connecting(sector, i, sector_mag):
builder.mag_needed[i] = [x for x in PolSlot if other_mag[x.value] > 0]
if name not in unconnected_builders.keys():
unconnected_builders[name] = builder
return unconnected_builders
ttl_mag = sum_magnitude(builder.sectors)
for slot in PolSlot:
for slot2 in PolSlot:
if ttl_mag[slot.value] > 0 and ttl_mag[slot2.value] > 0 and not connection_flags[slot][slot2]:
builder.mag_needed[slot] = [slot2]
builder.mag_needed[slot2] = [slot]
if name not in unconnected_builders.keys():
unconnected_builders[name] = builder
def self_connecting(sector, slot, magnitude):
return sector.polarity()[slot.value] == 0 and sum(magnitude) > magnitude[slot.value]
@@ -1518,7 +1526,7 @@ def filter_match_deps(candidate, match_deps):
def sum_magnitude(sector_list):
result = [0, 0, 0]
result = [0] * len(PolSlot)
for sector in sector_list:
vector = sector.magnitude()
for i in range(len(result)):
@@ -1651,7 +1659,11 @@ def polarity_step_3(dungeon_map, polarized_sectors, global_pole, logger):
sub_candidates = odd_candidates.pop(best_charge)
candidate_list = random.choice(sub_candidates)
sub_candidates.remove(candidate_list)
valid = global_pole.is_valid_choice(dungeon_map, builder, candidate_list) and valid_branch_only(builder, candidate_list)
test_set = find_forced_connections(dungeon_map, candidate_list, polarized_sectors)
if ensure_test_set_connectedness(test_set, builder, polarized_sectors, dungeon_map, global_pole):
valid = global_pole.is_valid_choice(dungeon_map, builder, test_set) and valid_branch_only(builder, candidate_list)
else:
valid = False
for candidate in candidate_list:
assign_sector(candidate, builder, polarized_sectors, global_pole)
@@ -1679,6 +1691,63 @@ def polarity_step_3(dungeon_map, polarized_sectors, global_pole, logger):
assign_sector(sector, builder, polarized_sectors, global_pole)
def find_forced_connections(dungeon_map, candidate_list, polarized_sectors):
test_set = list(candidate_list)
other_sectors = [x for x in polarized_sectors if x not in candidate_list]
dungeon_hooks = defaultdict(int)
for name, builder in dungeon_map.items():
d_mag = sum_hook_magnitude(builder.sectors)
for val in Hook:
dungeon_hooks[val] += d_mag[val.value]
queue = deque(candidate_list)
while queue:
candidate = queue.pop()
c_mag = candidate.hook_magnitude()
other_candidates = [x for x in candidate_list if x != candidate]
for val in Hook:
if c_mag[val.value] > 0:
opp = opposite_h_type(val)
o_val = opp.value
if sum_hook_magnitude(other_candidates)[o_val] == 0 and dungeon_hooks[opp] == 0 and not valid_self(c_mag, val, opp):
forced_sector = []
for sec in other_sectors:
if sec.hook_magnitude()[o_val] > 0:
forced_sector.append(sec)
if len(forced_sector) > 1:
break
if len(forced_sector) == 1:
test_set.append(forced_sector[0])
return test_set
def valid_self(c_mag, val, opp):
if val == Hook.Stairs:
return c_mag[val.value] > 2
else:
return c_mag[opp.value] > 0 and sum(c_mag) > 2
def ensure_test_set_connectedness(test_set, builder, polarized_sectors, dungeon_map, global_pole):
test_copy = list(test_set)
while not valid_connected_assignment(builder, test_copy):
dummy_builder = DungeonBuilder("Dummy Builder for " + builder.name)
dummy_builder.sectors = builder.sectors + test_copy
possibles = [x for x in polarized_sectors if x not in test_copy]
candidates = find_connected_candidates(possibles)
valid, sector = False, None
while not valid:
if len(candidates) == 0:
return False
sector = random.choice(candidates)
candidates.remove(sector)
t2 = test_copy+[sector]
valid = global_pole.is_valid_choice(dungeon_map, builder, t2) and valid_branch_only(builder, t2)
test_copy.append(sector)
dummy_builder.sectors = builder.sectors + test_copy
test_set[:] = test_copy
return True
class GlobalPolarity:
def __init__(self, candidate_sectors):
@@ -1805,6 +1874,7 @@ def find_simple_branching_candidates(builder, sector_pool):
candidates = defaultdict(list)
charges = defaultdict(list)
outflow_needed = builder.dead_ends + builder.forced_loops * 2 > builder.branches + builder.allowance
total_needed = builder.dead_ends + builder.forced_loops * 2 - builder.branches + builder.allowance
original_lack = builder.total_conn_lack
best_lack = original_lack
for sector in sector_pool:
@@ -1817,7 +1887,8 @@ def find_simple_branching_candidates(builder, sector_pool):
if lack < 0:
ttl_lack += -lack
forced_loops = calc_forced_loops(builder.sectors + [sector])
valid_branches = builder.dead_ends + forced_loops * 2 + sector.dead_ends() <= builder.branches + builder.allowance + sector.branches()
net_outflow = builder.dead_ends + forced_loops * 2 + sector.dead_ends() - builder.branches - builder.allowance - sector.branches()
valid_branches = net_outflow < total_needed
if valid_branches and (ttl_lack < original_lack or original_lack >= 0):
candidates[ttl_lack].append(sector)
charges[ttl_lack].append((builder.polarity()+sector.polarity()).charge())
@@ -1948,6 +2019,14 @@ def find_branching_candidates(builder, neutral_choices):
return candidates
def find_connected_candidates(sector_pool):
candidates = []
for sector in sector_pool:
if sector.adj_outflow() >= 2:
candidates.append(sector)
return candidates
def neutralize_the_rest(sector_pool):
neutral_choices = []
main_pool = list(sector_pool)
@@ -2034,6 +2113,10 @@ def valid_assignment(builder, sector_list):
return len(resolve_equations(builder, sector_list)) == 0
def valid_equations(builder, sector_list):
return len(resolve_equations(builder, sector_list)) == 0
def valid_connected_assignment(builder, sector_list):
full_list = sector_list + builder.sectors
for sector in full_list:
@@ -2073,13 +2156,35 @@ def valid_polarized_assignment(builder, sector_list):
def assign_the_rest(dungeon_map, neutral_sectors, global_pole):
comb_w_replace = len(dungeon_map) ** len(neutral_sectors)
combinations = None
if comb_w_replace <= 1000:
combinations = list(itertools.product(dungeon_map.keys(), repeat=len(neutral_sectors)))
random.shuffle(combinations)
tries = 0
while len(neutral_sectors) > 0:
sector_list = list(neutral_sectors)
choices = random.choices(list(dungeon_map.keys()), k=len(sector_list))
if tries > 1000 or (combinations and tries >= len(combinations)):
raise Exception('No valid assignment found for "neutral" sectors. Ref: %s' % next(iter(dungeon_map.keys())))
# sector_list = list(neutral_sectors)
if combinations:
choices = combinations[tries]
else:
choices = random.choices(list(dungeon_map.keys()), k=len(neutral_sectors))
neutral_sector_list = list(neutral_sectors)
chosen_sectors = defaultdict(list)
for i, choice in enumerate(choices):
builder = dungeon_map[choice]
if valid_assignment(builder, [sector_list[i]]):
assign_sector(sector_list[i], builder, neutral_sectors, global_pole)
chosen_sectors[choice].append(neutral_sector_list[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 name, sector_list in chosen_sectors.items():
builder = dungeon_map[name]
for sector in sector_list:
assign_sector(sector, builder, neutral_sectors, global_pole)
tries += 1
def split_dungeon_builder(builder, split_list):
@@ -2103,8 +2208,10 @@ def balance_split(candidate_sectors, dungeon_map, global_pole):
# categorize sectors
check_for_forced_dead_ends(dungeon_map, candidate_sectors, global_pole)
check_for_forced_assignments(dungeon_map, candidate_sectors, global_pole)
check_for_forced_crystal(dungeon_map, candidate_sectors, global_pole)
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, crystal_barriers,
global_pole, len(crystal_barriers) > 0)
for sector in leftover:
if sector.polarity().is_neutral():
neutral_sectors[sector] = None
@@ -2156,6 +2263,14 @@ def check_for_forced_dead_ends(dungeon_map, candidate_sectors, global_pole):
builder.c_locked = True
def check_crystal(dead_end, entrance):
if dead_end.blue_barrier and not entrance.c_switch and not dead_end.c_switch:
return False
if entrance.blue_barrier and not entrance.c_switch and not dead_end.c_switch:
return False
return True
def check_for_forced_assignments(dungeon_map, candidate_sectors, global_pole):
done = False
while not done:
@@ -2166,26 +2281,47 @@ def check_for_forced_assignments(dungeon_map, candidate_sectors, global_pole):
dungeon_hooks[name] = sum_hook_magnitude(builder.sectors)
for val in Hook:
if magnitude[val.value] == 1:
found_hooks = []
forced_sector = None
for sec in candidate_sectors:
if sec.hook_magnitude()[val.value] > 0:
forced_sector = sec
break
opp = opposite_h_type(val).value
for name, hooks in dungeon_hooks.items():
if hooks[opp] > 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)
other_sectors = [x for x in candidate_sectors if x != forced_sector]
if sum_hook_magnitude(other_sectors)[opp] == 0:
found_hooks = []
for name, hooks in dungeon_hooks.items():
if hooks[opp] > 0 and not dungeon_map[name].c_locked:
found_hooks.append(name)
if len(found_hooks) == 1:
done = False
assign_sector(forced_sector, dungeon_map[found_hooks[0]], candidate_sectors, global_pole)
def check_crystal(dead_end, entrance):
if dead_end.blue_barrier and not entrance.c_switch and not dead_end.c_switch:
return False
if entrance.blue_barrier and not entrance.c_switch and not dead_end.c_switch:
return False
def check_for_forced_crystal(dungeon_map, candidate_sectors, global_pole):
for name, builder in dungeon_map.items():
if check_for_forced_crystal_single(builder, candidate_sectors):
builder.c_switch_required = True
def check_for_forced_crystal_single(builder, candidate_sectors):
builder_doors = defaultdict(dict)
for sector in builder.sectors:
for door in sector.outstanding_doors:
builder_doors[hook_from_door(door)][door] = sector
candidate_doors = defaultdict(dict)
for sector in candidate_sectors:
for door in sector.outstanding_doors:
candidate_doors[hook_from_door(door)][door] = sector
for hook in builder_doors.keys():
for door in builder_doors[hook].keys():
opp = opposite_h_type(hook)
for d, sector in builder_doors[opp].items():
if d != door and (not sector.blue_barrier or sector.c_switch):
return False
for d, sector in candidate_doors[opp].items():
if not sector.blue_barrier or sector.c_switch:
return False
return True
@@ -2348,7 +2484,7 @@ def find_priority_equation(equations, access_id, current_access):
if best_local_profit is None or profit > best_local_profit:
best_local_profit = profit
all_candidates.append((eq, eq_list, sector))
elif best_profit is None or profit >= best_profit:
elif (best_profit is None or profit >= best_profit) and profit > 0:
if best_profit is None or profit > best_profit:
wanted_candidates = [eq]
best_profit = profit
@@ -2401,7 +2537,24 @@ def find_priority_equation(equations, access_id, current_access):
leads_to_profit = [x for x in good_local_candidates if can_enable_wanted(x[0], wanted_candidates)]
if len(leads_to_profit) == 0:
leads_to_profit = good_local_candidates
return leads_to_profit[0] # just pick one I guess
if len(leads_to_profit) == 1:
return leads_to_profit[0]
cost_point = {x[0]: find_cost_point(x, current_access) for x in leads_to_profit}
best_point = max(cost_point.values())
cost_point_candidates = [x for x in leads_to_profit if cost_point[x[0]] == best_point]
if len(cost_point_candidates) == 0:
cost_point_candidates = leads_to_profit
return cost_point_candidates[0] # just pick one I guess
def find_cost_point(eq_triplet, access):
cost_point = 0
for key, costs in eq_triplet[0].cost.items():
cost_count = len(costs)
if cost_count > 0:
cost_point += access[key] - cost_count
return cost_point
def find_greedy_equation(equations, access_id, current_access):