optimize sweep_for_events, some has_ functions and some minor things

This commit is contained in:
Fabian Dill 2020-08-22 19:19:29 +02:00
parent cac5795e01
commit b5048d99b9
5 changed files with 36 additions and 34 deletions

View File

@ -5,13 +5,13 @@ from enum import Enum, unique
import logging import logging
import json import json
from collections import OrderedDict, Counter, deque from collections import OrderedDict, Counter, deque
from typing import Union, Optional
import secrets
import random
from EntranceShuffle import door_addresses, indirect_connections from EntranceShuffle import door_addresses, indirect_connections
from Utils import int16_as_bytes from Utils import int16_as_bytes
from typing import Union, Optional from Items import item_name_groups
import secrets
import random
class World(object): class World(object):
@ -429,7 +429,7 @@ class CollectionState(object):
self.world = parent self.world = parent
self.reachable_regions = {player: set() for player in range(1, parent.players + 1)} self.reachable_regions = {player: set() for player in range(1, parent.players + 1)}
self.blocked_connections = {player: set() for player in range(1, parent.players + 1)} self.blocked_connections = {player: set() for player in range(1, parent.players + 1)}
self.events = [] self.events = set()
self.path = {} self.path = {}
self.locations_checked = set() self.locations_checked = set()
self.stale = {player: True for player in range(1, parent.players + 1)} self.stale = {player: True for player in range(1, parent.players + 1)}
@ -492,23 +492,19 @@ class CollectionState(object):
return spot.can_reach(self) return spot.can_reach(self)
def sweep_for_events(self, key_only: bool = False, locations=None): def sweep_for_events(self, key_only: bool = False, locations=None):
# this may need improvement
if locations is None: if locations is None:
locations = self.world.get_filled_locations() locations = self.world.get_filled_locations()
new_locations = True new_locations = True
checked_locations = 0
while new_locations: while new_locations:
reachable_events = [location for location in locations if location.event and reachable_events = {location for location in locations if location.event and
(not key_only or (not self.world.keyshuffle[ (not key_only or (not self.world.keyshuffle[
location.item.player] and location.item.smallkey) or (not self.world.bigkeyshuffle[ location.item.player] and location.item.smallkey) or (not self.world.bigkeyshuffle[
location.item.player] and location.item.bigkey)) location.item.player] and location.item.bigkey))
and location.can_reach(self)] and location.can_reach(self)}
for event in reachable_events: new_locations = reachable_events - self.events
if event not in self.events: for event in new_locations:
self.events.append(event) self.events.add(event)
self.collect(event.item, True, event) self.collect(event.item, True, event)
new_locations = len(reachable_events) > checked_locations
checked_locations = len(reachable_events)
def has(self, item, player: int, count: int = 1): def has(self, item, player: int, count: int = 1):
return self.prog_items[item, player] >= count return self.prog_items[item, player] >= count
@ -532,11 +528,10 @@ class CollectionState(object):
def has_crystals(self, count: int, player: int) -> bool: def has_crystals(self, count: int, player: int) -> bool:
found: int = 0 found: int = 0
for itemname, itemplayer in self.prog_items: for crystalnumber in range(1, 8):
if itemplayer == player and itemname.startswith('Crystal '): found += self.prog_items[f"Crystal {crystalnumber}", player]
found += 1 if found >= count:
if found >= count: return True
return True
return False return False
def can_lift_rocks(self, player: int): def can_lift_rocks(self, player: int):
@ -546,17 +541,18 @@ class CollectionState(object):
return self.has_bottles(1, player) return self.has_bottles(1, player)
def bottle_count(self, player: int) -> int: def bottle_count(self, player: int) -> int:
return len( found: int = 0
tuple(item for (item, itemplayer) in self.prog_items if itemplayer == player and item.startswith('Bottle'))) for bottlename in item_name_groups["Bottles"]:
found += self.prog_items[bottlename, player]
return found
def has_bottles(self, bottles: int, player: int): def has_bottles(self, bottles: int, player: int) -> bool:
"""Version of bottle_count that allows fast abort""" """Version of bottle_count that allows fast abort"""
found: int = 0 found: int = 0
for itemname, itemplayer in self.prog_items: for bottlename in item_name_groups["Bottles"]:
if itemplayer == player and itemname.startswith('Bottle'): found += self.prog_items[bottlename, player]
found += 1 if found >= bottles:
if found >= bottles: return True
return True
return False return False
def has_hearts(self, player: int, count: int) -> int: def has_hearts(self, player: int, count: int) -> int:
@ -995,6 +991,9 @@ class Location(object):
world = self.parent_region.world if self.parent_region and self.parent_region.world else None world = self.parent_region.world if self.parent_region and self.parent_region.world else None
return world.get_name_string_for_object(self) if world else f'{self.name} (Player {self.player})' return world.get_name_string_for_object(self) if world else f'{self.name} (Player {self.player})'
def __hash__(self):
return hash((self.name, self.player))
class Item(object): class Item(object):

View File

@ -209,7 +209,7 @@ def fill_restrictive(world, base_state: CollectionState, locations, itempool, si
if location.item and not location.event: if location.item and not location.event:
placements.append(location) placements.append(location)
raise FillError(f'No more spots to place {item_to_place}, locations {locations} are invalid. ' raise FillError(f'No more spots to place {item_to_place}, locations {locations} are invalid. '
f'\nAlready placed {len(placements)}: {", ".join(placements)}') f'Already placed {len(placements)}: {", ".join(placements)}')
world.push_item(spot_to_fill, item_to_place, False) world.push_item(spot_to_fill, item_to_place, False)
locations.remove(spot_to_fill) locations.remove(spot_to_fill)

View File

@ -1,9 +1,9 @@
import logging import logging
from BaseClasses import Item
def ItemFactory(items, player): def ItemFactory(items, player):
from BaseClasses import Item
ret = [] ret = []
singleton = False singleton = False
if isinstance(items, str): if isinstance(items, str):

2
Rom.py
View File

@ -348,6 +348,8 @@ def _populate_sprite_table():
if sprite.valid: if sprite.valid:
_sprite_table[sprite.name.lower()] = sprite _sprite_table[sprite.name.lower()] = sprite
_sprite_table[os.path.basename(file).split(".")[0].lower()] = sprite # alias for filename base _sprite_table[os.path.basename(file).split(".")[0].lower()] = sprite # alias for filename base
else:
logging.debug(f"Spritefile {file} could not be loaded as a valid sprite.")
with concurrent.futures.ThreadPoolExecutor() as pool: with concurrent.futures.ThreadPoolExecutor() as pool:
for dir in [local_path('data/sprites/alttpr'), local_path('data/sprites/custom')]: for dir in [local_path('data/sprites/alttpr'), local_path('data/sprites/custom')]:

View File

@ -17,17 +17,18 @@
# To test if your yaml is valid or not, you can use this website: # To test if your yaml is valid or not, you can use this website:
# http://www.yamllint.com/ # http://www.yamllint.com/
description: Your Description Here # Used to describe your yaml. Useful if you have multiple files description: easy template # Used to describe your yaml. Useful if you have multiple files
name: YourName # Your name in-game. Spaces and underscores will be replaced with dashes name: YourName # Your name in-game. Spaces will be replaced with underscores and there is a 16 character limit
glitches_required: # Determine the logic required to complete the seed glitches_required: # Determine the logic required to complete the seed
none: 1 # No glitches required none: 1 # No glitches required
minor_glitches: 0 # Puts fake flipper, waterwalk, super bunny shenanigans, and etc into logic minor_glitches: 0 # Puts fake flipper, waterwalk, super bunny shenanigans, and etc into logic
overworld_glitches: 0 # Assumes the player has knowledge of both overworld major glitches (boots clips, mirror clips) and minor glitches (fake flipper, super bunny shenanigans, water walk and etc.) overworld_glitches: 0 # Assumes the player has knowledge of both overworld major glitches (boots clips, mirror clips) and minor glitches (fake flipper, super bunny shenanigans, water walk and etc.)
no_logic: 0 # Items are places completely at random and with no regard for logic. Your fire rod could be on Trinexx no_logic: 0 # Items are places completely at random and with no regard for logic. Your fire rod could be on Trinexx
meta_ignore: # Nullify options specified in the meta.yaml file. Adding an option here guarantees it will not occur in your seed, even if the .yaml file specifies it meta_ignore: # Nullify options specified in the meta.yaml file. Adding an option here guarantees it will not occur in your seed, even if the .yaml file specifies it
world_state: mode:
- inverted # Never play inverted seeds - inverted # Never play inverted seeds
- retro # Never play retro seeds retro:
- on # Never play retro seeds
weapons: weapons:
- swordless # Never play a swordless seed - swordless # Never play a swordless seed
map_shuffle: # Shuffle dungeon maps into the world and other dungeons, including other players' worlds map_shuffle: # Shuffle dungeon maps into the world and other dungeons, including other players' worlds