Generation improvements

This commit is contained in:
aerinon
2020-12-01 15:00:53 -07:00
parent 64b539706c
commit 32e7544775
9 changed files with 53 additions and 40 deletions
+1
View File
@@ -1499,6 +1499,7 @@ class Portal(object):
self.boss_exit_idx = boss_exit_idx
self.default = True
self.destination = False
self.dependent = None
self.deadEnd = False
self.light_world = False
-2
View File
@@ -2,8 +2,6 @@ import argparse
import copy
import json
import os
import logging
import random
import textwrap
import shlex
import sys
+18 -10
View File
@@ -462,7 +462,12 @@ def analyze_portals(world, player):
if len(possible_portals) == 1:
world.get_portal(possible_portals[0], player).destination = True
elif len(possible_portals) > 1:
world.get_portal(random.choice(possible_portals), player).destination = True
dest_portal = random.choice(possible_portals)
access_portal = world.get_portal(dest_portal, player)
access_portal.destination = True
for other_portal in possible_portals:
if other_portal != dest_portal:
world.get_portal(dest_portal, player).dependent = access_portal
def connect_portal(portal, world, player):
@@ -560,7 +565,7 @@ def create_dungeon_entrances(world, player):
for key, portal_list in dungeon_portals.items():
if key in dungeon_drops.keys():
entrance_map[key].extend(dungeon_drops[key])
if key in split_portals.keys() and world.intensity[player] >= 3:
if key in split_portals.keys():
dead_ends = []
destinations = []
the_rest = []
@@ -586,10 +591,12 @@ def create_dungeon_entrances(world, player):
p_entrance = portal.door.entrance
r_name = p_entrance.parent_region.name
split_map[key][choice].append(r_name)
originating[key][choice][p_entrance.connected_region.name] = None
entrance_region = find_entrance_region(portal)
originating[key][choice][entrance_region.name] = None
dest_choices = [x for x in choices if len(split_map[key][x]) > 0]
for portal in destinations:
restricted = portal.door.entrance.connected_region.name in world.inaccessible_regions[player]
entrance_region = find_entrance_region(portal)
restricted = entrance_region.name in world.inaccessible_regions[player]
if restricted:
filtered_choices = [x for x in choices if any(y not in world.inaccessible_regions[player] for y in originating[key][x].keys())]
else:
@@ -604,15 +611,16 @@ def create_dungeon_entrances(world, player):
portal = world.get_portal(portal_name, player)
r_name = portal.door.entrance.parent_region.name
entrance_map[key].append(r_name)
if key in split_portals.keys():
for split_key in split_portals[key]:
if split_key not in split_map[key]:
split_map[key][split_key] = []
if world.intensity[player] < 3:
split_map[key][split_portal_defaults[key][r_name]].append(r_name)
return entrance_map, split_map
def find_entrance_region(portal):
for entrance in portal.door.entrance.connected_region.entrances:
if entrance.parent_region.type != RegionType.Dungeon:
return entrance.parent_region
return None
# def unpair_all_doors(world, player):
# for paired_door in world.paired_doors[player]:
# paired_door.pair = False
+8 -7
View File
@@ -2750,14 +2750,14 @@ def split_dungeon_builder(builder, split_list, builder_info):
builder.split_dungeon_map[name].valid_proposal = proposal
return builder.split_dungeon_map # we made this earlier in gen, just use it
attempts, comb_w_replace, merge_attempt = 0, None, False
attempts, comb_w_replace, merge_attempt, merge_limit = 0, None, 0, len(split_list) - 1
while attempts < 5: # does not solve coin flips 3% of the time
try:
candidate_sectors = dict.fromkeys(builder.sectors)
global_pole = GlobalPolarity(candidate_sectors)
dungeon_map, sub_builder, merge_keys = {}, None, []
if merge_attempt:
if merge_attempt > 0:
candidates = []
for name, split_entrances in split_list.items():
if len(split_entrances) > 1:
@@ -2770,12 +2770,13 @@ def split_dungeon_builder(builder, split_list, builder_info):
p = next(x for x in world.dungeon_portals[player] if x.door.entrance.parent_region.name == r_name)
if not p.deadEnd:
candidates.append(name)
merge_keys = random.sample(candidates, 2) if len(candidates) >= 2 else []
merge_keys = random.sample(candidates, merge_attempt+1) if len(candidates) >= merge_attempt+1 else []
for name, split_entrances in split_list.items():
key = builder.name + ' ' + name
if merge_keys and name in merge_keys:
other_key = builder.name + ' ' + [x for x in merge_keys if x != name][0]
if other_key in dungeon_map:
other_keys = [builder.name + ' ' + x for x in merge_keys if x != name]
other_key = next((x for x in other_keys if x in dungeon_map), None)
if other_key:
key = other_key
sub_builder = dungeon_map[other_key]
sub_builder.all_entrances.extend(split_entrances)
@@ -2791,8 +2792,8 @@ def split_dungeon_builder(builder, split_list, builder_info):
attempts += 5 # all the combinations were tried already, no use repeating
else:
attempts += 1
if attempts >= 5 and not merge_attempt:
merge_attempt, attempts = True, 0
if attempts >= 5 and merge_attempt < merge_limit:
merge_attempt, attempts = merge_attempt + 1, 0
raise GenerationException('Unable to resolve in 5 attempts')
+2 -2
View File
@@ -1,12 +1,12 @@
import collections
from BaseClasses import RegionType
from Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region
from Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region, create_menu_region
def create_inverted_regions(world, player):
world.regions += [
create_dw_region(player, 'Menu', None, ['Links House S&Q', 'Dark Sanctuary S&Q', 'Old Man S&Q', 'Castle Ledge S&Q']),
create_menu_region(player, 'Menu', None, ['Links House S&Q', 'Dark Sanctuary S&Q', 'Old Man S&Q', 'Castle Ledge S&Q']),
create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest', 'Bombos Tablet'],
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Kings Grave Outer Rocks', 'Dam',
'Inverted Big Bomb Shop', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
+10 -5
View File
@@ -25,7 +25,7 @@ from Fill import distribute_items_cutoff, distribute_items_staleness, distribute
from ItemList import generate_itempool, difficulties, fill_prizes, fill_specific_items
from Utils import output_path, parse_player_names
__version__ = '0.2.0.16u'
__version__ = '0.2.0.17u'
class EnemizerError(RuntimeError):
pass
@@ -354,6 +354,7 @@ def main(args, seed=None, fish=None):
return world
def copy_world(world):
# ToDo: Not good yet
ret = World(world.players, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords,
@@ -443,15 +444,19 @@ def copy_world(world):
# fill locations
for location in world.get_locations():
new_location = ret.get_location(location.name, location.player)
if location.item is not None:
item = Item(location.item.name, location.item.advancement, location.item.priority, location.item.type, player = location.item.player)
ret.get_location(location.name, location.player).item = item
item.location = ret.get_location(location.name, location.player)
new_location.item = item
item.location = new_location
item.world = ret
if location.event:
ret.get_location(location.name, location.player).event = True
new_location.event = True
if location.locked:
ret.get_location(location.name, location.player).locked = True
new_location.locked = True
# these need to be modified properly by set_rules
new_location.access_rule = lambda state: True
new_location.item_rule = lambda state: True
# copy remaining itempool. No item in itempool should have an assigned location
for item in world.itempool:
+2
View File
@@ -92,6 +92,8 @@ testing to verify logic is all good.
# Bug Fixes
* 2.0.17u
* Generation improvements
* 2.0.16u
* Prevent HUD from showing key counter when in the overworld. (Aga 2 doesn't always clear the dungeon indicator)
* Fixed key logic regarding certain isolated "important" locations
+3 -3
View File
@@ -564,7 +564,7 @@ def inverted_rules(world, player):
set_rule(world.get_entrance('Bomb Hut Outer Bushes', player), lambda state: state.has_Pearl(player))
set_rule(world.get_entrance('North Fairy Cave Drop', player), lambda state: state.has_Pearl(player))
set_rule(world.get_entrance('Lost Woods Hideout Drop', player), lambda state: state.has_Pearl(player))
set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player) and (state.can_reach('Potion Shop Area', 'Region', player))) # new inverted region, need pearl for bushes or access to potion shop door/waterfall fairy
set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player) and (state.can_reach('Potion Shop Area', 'Region', player))) # new inverted region, need pearl for bushes or access to potion shop door/waterfall fairy
set_rule(world.get_entrance('Desert Palace Entrance (North) Rocks', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player))
set_rule(world.get_entrance('Desert Ledge Return Rocks', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player)) # should we decide to place something that is not a dungeon end up there at some point
set_rule(world.get_entrance('Checkerboard Cave', player), lambda state: state.can_lift_rocks(player) and state.has_Pearl(player))
@@ -1348,7 +1348,7 @@ def set_bunny_rules(world, player):
# Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing.
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave',
'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)']
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
bunny_accessible_locations = ['Link\'s House', 'Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid',
'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge', 'Dark Blacksmith Ruins']
@@ -1432,7 +1432,7 @@ def set_inverted_bunny_rules(world, player):
# Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing.
bunny_impassable_caves = ['Bumper Cave', 'Two Brothers House', 'Hookshot Cave',
'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)', 'The Sky']
bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
bunny_accessible_locations = ['Link\'s House', 'Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree',
'Checkerboard Cave', 'Potion Shop', 'Spectacle Rock Cave', 'Pyramid',
'Hype Cave - Generous Guy', 'Peg Cave', 'Bumper Cave Ledge', 'Dark Blacksmith Ruins',
'Bombos Tablet', 'Ether Tablet', 'Purple Chest']
+9 -11
View File
@@ -1,7 +1,5 @@
import subprocess
import sys
import traceback
import io
import multiprocessing
import concurrent.futures
import argparse
@@ -42,6 +40,7 @@ def main(args=None):
task.success = False
task.name = testname
task.mode = mode[0]
task.cmd = basecommand + " " + command + mode[1]
task_mapping.append(task)
test("Vanilla ", "--shuffle vanilla")
@@ -61,14 +60,12 @@ def main(args=None):
try:
result = task.result()
if result.returncode:
raise Exception(result.stderr)
except:
error = io.StringIO()
traceback.print_exc(file=error)
errors.append([task.name + task.mode, error.getvalue()])
else:
alive += 1
task.success = True
errors.append([task.name + task.mode, task.cmd, result.stderr])
else:
alive += 1
task.success = True
except Exception as e:
raise e
progressbar.set_description(f"Success rate: {(alive/dead_or_alive)*100:.2f}% - {task.name}{task.mode}")
@@ -129,7 +126,8 @@ if __name__ == "__main__":
with open(f"{dr[0]}{(f'-{tense}' if dr[0] in ['basic', 'crossed'] else '')}-errors.txt", 'w') as stream:
for error in errors:
stream.write(error[0] + "\n")
stream.write(error[1] + "\n\n")
stream.write(error[1] + "\n")
stream.write(error[2] + "\n\n")
with open("success.txt", "w") as stream:
stream.write(str.join("\n", successes))