Significant performance upgrades in convoluted seeds.

This commit is contained in:
LLCoolDave 2017-05-26 09:55:49 +02:00
parent 2f10495248
commit 5207ccd8fd
1 changed files with 53 additions and 35 deletions

View File

@ -105,7 +105,12 @@ class World(object):
temp_state = self.state.copy()
temp_state._clear_cache()
temp_state.collect(item)
return len(self.get_placeable_locations()) < len(self.get_placeable_locations(temp_state))
for location in self.get_unfilled_locations():
if temp_state.can_reach(location) and not self.state.can_reach(location):
return True
return False
def can_beat_game(self):
prog_locations = [location for location in self.get_locations() if location.item is not None and location.item.advancement]
@ -137,44 +142,32 @@ class CollectionState(object):
self.prog_items = []
self.world = parent
self.has_everything = has_everything
self.changed = False
self.region_cache = {}
self.location_cache = {}
self.entrance_cache = {}
# use to avoid cycle dependencies during resolution
self.recursion_cache = []
self.recursion_count = 0
def _clear_cache(self):
# we only need to invalidate results which were False, places we could reach before we can still reach after adding more items
self.region_cache = {k: v for k, v in self.region_cache.items() if v}
self.location_cache = {k: v for k, v in self.location_cache.items() if v}
self.entrance_cache = {k: v for k, v in self.entrance_cache.items() if v}
self.recursion_cache = []
self.changed = False
def copy(self):
ret = CollectionState(self.world, self.has_everything)
ret.prog_items = copy.copy(self.prog_items)
ret.changed = self.changed
ret.region_cache = copy.copy(self.region_cache)
ret.location_cache = copy.copy(self.location_cache)
ret.entrance_cache = copy.copy(self.entrance_cache)
ret.recursion_cache = copy.copy(self.recursion_cache)
return ret
def can_reach(self, spot, resolution_hint=None):
if self.changed:
self._clear_cache()
if spot in self.recursion_cache:
return False
try:
spot_type = spot.spot_type
if spot_type == 'Region':
correct_cache = self.region_cache
elif spot_type == 'Location':
if spot_type == 'Location':
correct_cache = self.location_cache
elif spot_type == 'Region':
correct_cache = self.region_cache
elif spot_type == 'Entrance':
correct_cache = self.entrance_cache
else:
@ -192,15 +185,20 @@ class CollectionState(object):
spot = self.world.get_region(spot)
correct_cache = self.region_cache
if spot.recursion_count > 0:
return False
if spot not in correct_cache:
# for the purpose of evaluating results, recursion is resolved by always denying recursive access (as that ia what we are trying to figure out right now in the first place
self.recursion_cache.append(spot)
spot.recursion_count += 1
self.recursion_count += 1
can_reach = spot.can_reach(self)
self.recursion_cache.pop()
spot.recursion_count -= 1
self.recursion_count -= 1
# we only store qualified false results (i.e. ones not inside a hypothetical)
if not can_reach:
if not self.recursion_cache:
if self.recursion_count == 0:
correct_cache[spot] = can_reach
else:
correct_cache[spot] = can_reach
@ -221,10 +219,25 @@ class CollectionState(object):
self.world._item_cache[item] = cached
else:
# this should probably not happen, wonky item distribution?
return len([location for location in candidates if self.can_reach(location)]) >= 1
return self._can_reach_n(self.world.find_items(item), 1)
return self.can_reach(cached)
return len([location for location in self.world.find_items(item) if self.can_reach(location)]) >= count
return self._can_reach_n(self.world.find_items(item), count)
def _can_reach_n(self, candidates, count):
maxfail = len(candidates) - count
fail = 0
success = 0
for candidate in candidates:
if self.can_reach(candidate):
success += 1
else:
fail += 1
if fail > maxfail:
return False
if success >= count:
return True
return False
def has(self, item):
if self.has_everything:
@ -266,36 +279,39 @@ class CollectionState(object):
return self.has(self.world.required_medallions[1])
def collect(self, item):
changed = False
if item.name.startswith('Progressive '):
if 'Sword' in item.name:
if self.has('Golden Sword'):
return
pass
elif self.has('Tempered Sword'):
self.prog_items.append('Golden Sword')
self.changed = True
changed = True
elif self.has('Master Sword'):
self.prog_items.append('Tempered Sword')
self.changed = True
changed = True
elif self.has('Fighter Sword'):
self.prog_items.append('Master Sword')
self.changed = True
changed = True
else:
self.prog_items.append('Fighter Sword')
self.changed = True
changed = True
elif 'Glove' in item.name:
if self.has('Titans Mitts'):
return
pass
elif self.has('Power Glove'):
self.prog_items.append('Titans Mitts')
self.changed = True
changed = True
else:
self.prog_items.append('Power Glove')
self.changed = True
return
changed = True
if item.advancement:
elif item.advancement:
self.prog_items.append(item.name)
self.changed = True
changed = True
if changed:
self._clear_cache()
def remove(self, item):
if item.advancement:
@ -330,8 +346,7 @@ class CollectionState(object):
self.region_cache = {}
self.location_cache = {}
self.entrance_cache = {}
self.recursion_cache = []
self.changed = False
self.recursion_count = 0
def __getattr__(self, item):
if item.startswith('can_reach_'):
@ -351,6 +366,7 @@ class Region(object):
self.locations = []
self.spot_type = 'Region'
self.hint_text = 'Hyrule'
self.recursion_count = 0
def can_reach(self, state):
for entrance in self.entrances:
@ -373,6 +389,7 @@ class Entrance(object):
self.connected_region = None
self.target = None
self.spot_type = 'Entrance'
self.recursion_count = 0
def access_rule(self, state):
return True
@ -405,6 +422,7 @@ class Location(object):
self.address = address
self.spot_type = 'Location'
self.hint_text = hint_text if hint_text is not None else 'Hyrule'
self.recursion_count = 0
def access_rule(self, state):
return True