Skip to content

Commit

Permalink
Merge branch 'main' into sweep_per_player
Browse files Browse the repository at this point in the history
# Conflicts:
#	BaseClasses.py
  • Loading branch information
Mysteryem committed Aug 22, 2024
2 parents 0f37f2d + 64b654d commit 5bf588b
Show file tree
Hide file tree
Showing 86 changed files with 61,002 additions and 11,588 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
worlds/blasphemous/region_data.py linguist-generated=true
39 changes: 22 additions & 17 deletions BaseClasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ def get_all_state(self, use_cache: bool) -> CollectionState:
subworld = self.worlds[player]
for item in subworld.get_pre_fill_items():
subworld.collect(ret, item)
ret.sweep_for_events()
ret.sweep_for_advancements()

if use_cache:
self._all_state = ret
Expand Down Expand Up @@ -545,7 +545,7 @@ def can_beat_game(self, starting_state: Optional[CollectionState] = None) -> boo
if self.has_beaten_game(state):
return True

for _ in state.sweep_for_events(yield_each_sweep=True, checked_locations=state.locations_checked):
for _ in state.sweep_for_advancements(yield_each_sweep=True, checked_locations=state.locations_checked):
if self.has_beaten_game(state):
return True

Expand Down Expand Up @@ -644,7 +644,7 @@ class CollectionState():
multiworld: MultiWorld
reachable_regions: Dict[int, Set[Region]]
blocked_connections: Dict[int, Set[Entrance]]
events: Set[Location]
advancements: Set[Location]
path: Dict[Union[Region, Entrance], PathValue]
locations_checked: Set[Location]
stale: Dict[int, bool]
Expand All @@ -656,7 +656,7 @@ def __init__(self, parent: MultiWorld):
self.multiworld = parent
self.reachable_regions = {player: set() for player in parent.get_all_ids()}
self.blocked_connections = {player: set() for player in parent.get_all_ids()}
self.events = set()
self.advancements = set()
self.path = {}
self.locations_checked = set()
self.stale = {player: True for player in parent.get_all_ids()}
Expand Down Expand Up @@ -705,7 +705,7 @@ def copy(self) -> CollectionState:
self.reachable_regions.items()}
ret.blocked_connections = {player: entrance_set.copy() for player, entrance_set in
self.blocked_connections.items()}
ret.events = self.events.copy()
ret.advancements = self.advancements.copy()
ret.path = self.path.copy()
ret.locations_checked = self.locations_checked.copy()
for function in self.additional_copy_functions:
Expand Down Expand Up @@ -737,11 +737,16 @@ def can_reach_entrance(self, spot: str, player: int) -> bool:
def can_reach_region(self, spot: str, player: int) -> bool:
return self.multiworld.get_region(spot, player).can_reach(self)

def _sweep_for_events_impl(self, events_per_player: List[Tuple[int, List[Location]]], yield_each_sweep: bool,
) -> Iterator[None]:
def sweep_for_events(self, locations: Optional[Iterable[Location]] = None) -> None:
Utils.deprecate("sweep_for_events has been renamed to sweep_for_advancements. The functionality is the same. "
"Please switch over to sweep_for_advancements.")
return self.sweep_for_advancements(locations)

def _sweep_for_advancements_impl(self, events_per_player: List[Tuple[int, List[Location]]], yield_each_sweep: bool,
) -> Iterator[None]:
"""
The implementation for sweep_for_events is separated here because it returns a generator due to the use of a
yield statement.
The implementation for sweep_for_advancements is separated here because it returns a generator due to the use
of a yield statement.
"""
all_players = {player for player, _ in events_per_player}
players_to_check = all_players
Expand Down Expand Up @@ -781,7 +786,7 @@ def _sweep_for_events_impl(self, events_per_player: List[Tuple[int, List[Locatio

# Collect the items from the reachable locations.
for event in reachable_locations:
self.events.add(event)
self.advancements.add(event)
item = event.item
assert isinstance(item, Item), "tried to collect Event with no Item"
if self.collect(item, True, event):
Expand All @@ -804,19 +809,19 @@ def _sweep_for_events_impl(self, events_per_player: List[Tuple[int, List[Locatio
if yield_each_sweep:
yield

def sweep_for_events(self, locations: Optional[Iterable[Location]] = None, yield_each_sweep: bool = False,
checked_locations: Optional[Set[Location]] = None) -> Optional[Iterator[None]]:
def sweep_for_advancements(self, locations: Optional[Iterable[Location]] = None, yield_each_sweep: bool = False,
checked_locations: Optional[Set[Location]] = None) -> Optional[Iterator[None]]:
"""
Sweep through the locations that contain uncollected advancement items, collecting the items into the state
until there are no more reachable locations that contain uncollected advancement items.
:param locations: The locations to sweep through, defaulting to all locations in the multiworld.
:param yield_each_sweep: When True, return a generator that yields at the end of each sweep iteration.
:param checked_locations: Optional override of locations to filter out from the locations argument, defaults to
self.events when None.
self.advancements when None.
"""
if checked_locations is None:
checked_locations = self.events
checked_locations = self.advancements

# Since the sweep loop usually performs many iterations, the locations are filtered in advance.
# A list of tuples is used, instead of a dictionary, because it is faster to iterate.
Expand All @@ -841,11 +846,11 @@ def sweep_for_events(self, locations: Optional[Iterable[Location]] = None, yield

if yield_each_sweep:
# Return a generator that will yield at the end of each sweep iteration.
return self._sweep_for_events_impl(events_per_player, True)
return self._sweep_for_advancements_impl(events_per_player, True)
else:
# Create the generator, but tell it not to yield anything, so it will run to completion in zero iterations
# once started, then start and exhaust the generator by attempting to iterate it.
for _ in self._sweep_for_events_impl(events_per_player, False):
for _ in self._sweep_for_advancements_impl(events_per_player, False):
assert False, "Generator yielded when it should have run to completion without yielding"
return None

Expand Down Expand Up @@ -951,7 +956,7 @@ def collect(self, item: Item, prevent_sweep: bool = False, location: Optional[Lo
self.stale[item.player] = True

if changed and not prevent_sweep:
self.sweep_for_events()
self.sweep_for_advancements()

return changed

Expand Down
14 changes: 7 additions & 7 deletions Fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def sweep_from_pool(base_state: CollectionState, itempool: typing.Sequence[Item]
new_state = base_state.copy()
for item in itempool:
new_state.collect(item, True)
new_state.sweep_for_events(locations=locations)
new_state.sweep_for_advancements(locations=locations)
return new_state


Expand Down Expand Up @@ -329,8 +329,8 @@ def accessibility_corrections(multiworld: MultiWorld, state: CollectionState, lo
pool.append(location.item)
state.remove(location.item)
location.item = None
if location in state.events:
state.events.remove(location)
if location in state.advancements:
state.advancements.remove(location)
locations.append(location)
if pool and locations:
locations.sort(key=lambda loc: loc.progress_type != LocationProgressType.PRIORITY)
Expand Down Expand Up @@ -363,7 +363,7 @@ def distribute_early_items(multiworld: MultiWorld,
early_priority_locations: typing.List[Location] = []
loc_indexes_to_remove: typing.Set[int] = set()
base_state = multiworld.state.copy()
base_state.sweep_for_events(locations=(loc for loc in multiworld.get_filled_locations() if loc.address is None))
base_state.sweep_for_advancements(locations=(loc for loc in multiworld.get_filled_locations() if loc.address is None))
for i, loc in enumerate(fill_locations):
if loc.can_reach(base_state):
if loc.progress_type == LocationProgressType.PRIORITY:
Expand Down Expand Up @@ -558,7 +558,7 @@ def flood_items(multiworld: MultiWorld) -> None:
progress_done = False

# sweep once to pick up preplaced items
multiworld.state.sweep_for_events()
multiworld.state.sweep_for_advancements()

# fill multiworld from top of itempool while we can
while not progress_done:
Expand Down Expand Up @@ -746,7 +746,7 @@ def item_percentage(player: int, num: int) -> float:
), items_to_test):
reducing_state.collect(location.item, True, location)

reducing_state.sweep_for_events(locations=locations_to_test)
reducing_state.sweep_for_advancements(locations=locations_to_test)

if multiworld.has_beaten_game(balancing_state):
if not multiworld.has_beaten_game(reducing_state):
Expand Down Expand Up @@ -829,7 +829,7 @@ def failed(warning: str, force: typing.Union[bool, str]) -> None:
warn(warning, force)

swept_state = multiworld.state.copy()
swept_state.sweep_for_events()
swept_state.sweep_for_advancements()
reachable = frozenset(multiworld.get_reachable_locations(swept_state))
early_locations: typing.Dict[int, typing.List[str]] = collections.defaultdict(list)
non_early_locations: typing.Dict[int, typing.List[str]] = collections.defaultdict(list)
Expand Down
34 changes: 17 additions & 17 deletions LttPAdjuster.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from argparse import Namespace
from concurrent.futures import as_completed, ThreadPoolExecutor
from glob import glob
from tkinter import Tk, Frame, Label, StringVar, Entry, filedialog, messagebox, Button, Radiobutton, LEFT, X, TOP, LabelFrame, \
from tkinter import Tk, Frame, Label, StringVar, Entry, filedialog, messagebox, Button, Radiobutton, LEFT, X, BOTH, TOP, LabelFrame, \
IntVar, Checkbutton, E, W, OptionMenu, Toplevel, BOTTOM, RIGHT, font as font, PhotoImage
from tkinter.constants import DISABLED, NORMAL
from urllib.parse import urlparse
Expand All @@ -29,7 +29,8 @@


GAME_ALTTP = "A Link to the Past"

WINDOW_MIN_HEIGHT = 525
WINDOW_MIN_WIDTH = 425

class AdjusterWorld(object):
def __init__(self, sprite_pool):
Expand Down Expand Up @@ -242,16 +243,17 @@ def adjustGUI():
from argparse import Namespace
from Utils import __version__ as MWVersion
adjustWindow = Tk()
adjustWindow.minsize(WINDOW_MIN_WIDTH, WINDOW_MIN_HEIGHT)
adjustWindow.wm_title("Archipelago %s LttP Adjuster" % MWVersion)
set_icon(adjustWindow)

rom_options_frame, rom_vars, set_sprite = get_rom_options_frame(adjustWindow)

bottomFrame2 = Frame(adjustWindow)
bottomFrame2 = Frame(adjustWindow, padx=8, pady=2)

romFrame, romVar = get_rom_frame(adjustWindow)

romDialogFrame = Frame(adjustWindow)
romDialogFrame = Frame(adjustWindow, padx=8, pady=2)
baseRomLabel2 = Label(romDialogFrame, text='Rom to adjust')
romVar2 = StringVar()
romEntry2 = Entry(romDialogFrame, textvariable=romVar2)
Expand All @@ -261,9 +263,9 @@ def RomSelect2():
romVar2.set(rom)

romSelectButton2 = Button(romDialogFrame, text='Select Rom', command=RomSelect2)
romDialogFrame.pack(side=TOP, expand=True, fill=X)
baseRomLabel2.pack(side=LEFT)
romEntry2.pack(side=LEFT, expand=True, fill=X)
romDialogFrame.pack(side=TOP, expand=False, fill=X)
baseRomLabel2.pack(side=LEFT, expand=False, fill=X, padx=(0, 8))
romEntry2.pack(side=LEFT, expand=True, fill=BOTH, pady=1)
romSelectButton2.pack(side=LEFT)

def adjustRom():
Expand Down Expand Up @@ -331,12 +333,11 @@ def saveGUISettings():
messagebox.showinfo(title="Success", message="Settings saved to persistent storage")

adjustButton = Button(bottomFrame2, text='Adjust Rom', command=adjustRom)
rom_options_frame.pack(side=TOP)
rom_options_frame.pack(side=TOP, padx=8, pady=8, fill=BOTH, expand=True)
adjustButton.pack(side=LEFT, padx=(5,5))

saveButton = Button(bottomFrame2, text='Save Settings', command=saveGUISettings)
saveButton.pack(side=LEFT, padx=(5,5))

bottomFrame2.pack(side=TOP, pady=(5,5))

tkinter_center_window(adjustWindow)
Expand Down Expand Up @@ -576,7 +577,7 @@ def hide(self):
def get_rom_frame(parent=None):
adjuster_settings = get_adjuster_settings(GAME_ALTTP)

romFrame = Frame(parent)
romFrame = Frame(parent, padx=8, pady=8)
baseRomLabel = Label(romFrame, text='LttP Base Rom: ')
romVar = StringVar(value=adjuster_settings.baserom)
romEntry = Entry(romFrame, textvariable=romVar)
Expand All @@ -596,20 +597,19 @@ def RomSelect():
romSelectButton = Button(romFrame, text='Select Rom', command=RomSelect)

baseRomLabel.pack(side=LEFT)
romEntry.pack(side=LEFT, expand=True, fill=X)
romEntry.pack(side=LEFT, expand=True, fill=BOTH, pady=1)
romSelectButton.pack(side=LEFT)
romFrame.pack(side=TOP, expand=True, fill=X)
romFrame.pack(side=TOP, fill=X)

return romFrame, romVar

def get_rom_options_frame(parent=None):
adjuster_settings = get_adjuster_settings(GAME_ALTTP)

romOptionsFrame = LabelFrame(parent, text="Rom options")
romOptionsFrame.columnconfigure(0, weight=1)
romOptionsFrame.columnconfigure(1, weight=1)
romOptionsFrame = LabelFrame(parent, text="Rom options", padx=8, pady=8)

for i in range(5):
romOptionsFrame.rowconfigure(i, weight=1)
romOptionsFrame.rowconfigure(i, weight=0, pad=4)
vars = Namespace()

vars.MusicVar = IntVar()
Expand Down Expand Up @@ -660,7 +660,7 @@ def SpriteSelect():
spriteSelectButton = Button(spriteDialogFrame, text='...', command=SpriteSelect)

baseSpriteLabel.pack(side=LEFT)
spriteEntry.pack(side=LEFT)
spriteEntry.pack(side=LEFT, expand=True, fill=X)
spriteSelectButton.pack(side=LEFT)

oofDialogFrame = Frame(romOptionsFrame)
Expand Down
1 change: 1 addition & 0 deletions NetUtils.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class NetworkItem(typing.NamedTuple):
item: int
location: int
player: int
""" Sending player, except in LocationInfo (from LocationScouts), where it is the receiving player. """
flags: int = 0


Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ Currently, the following games are supported:
* A Hat in Time
* Old School Runescape
* Kingdom Hearts 1
* Mega Man 2
* Yacht Dice

For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
Expand Down
6 changes: 6 additions & 0 deletions docs/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@
# Minecraft
/worlds/minecraft/ @KonoTyran @espeon65536

# Mega Man 2
/worlds/mm2/ @Silvris

# MegaMan Battle Network 3
/worlds/mmbn3/ @digiholic

Expand Down Expand Up @@ -199,6 +202,9 @@
# The Witness
/worlds/witness/ @NewSoupVi @blastron

# Yacht Dice
/worlds/yachtdice/ @spinerak

# Yoshi's Island
/worlds/yoshisisland/ @PinkSwitch

Expand Down
5 changes: 5 additions & 0 deletions inno_setup.iss
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,11 @@ Root: HKCR; Subkey: "{#MyAppName}cv64patch"; ValueData: "Arc
Root: HKCR; Subkey: "{#MyAppName}cv64patch\DefaultIcon"; ValueData: "{app}\ArchipelagoBizHawkClient.exe,0"; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}cv64patch\shell\open\command"; ValueData: """{app}\ArchipelagoBizHawkClient.exe"" ""%1"""; ValueType: string; ValueName: "";

Root: HKCR; Subkey: ".apmm2"; ValueData: "{#MyAppName}mm2patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mm2patch"; ValueData: "Archipelago Mega Man 2 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mm2patch\DefaultIcon"; ValueData: "{app}\ArchipelagoBizHawkClient.exe,0"; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}mm2patch\shell\open\command"; ValueData: """{app}\ArchipelagoBizHawkClient.exe"" ""%1"""; ValueType: string; ValueName: "";

Root: HKCR; Subkey: ".apladx"; ValueData: "{#MyAppName}ladxpatch"; Flags: uninsdeletevalue; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}ladxpatch"; ValueData: "Archipelago Links Awakening DX Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: "";
Root: HKCR; Subkey: "{#MyAppName}ladxpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoLinksAwakeningClient.exe,0"; ValueType: string; ValueName: "";
Expand Down
6 changes: 3 additions & 3 deletions test/bases.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def get_state(self, items):
for item in items:
item.classification = ItemClassification.progression
state.collect(item, prevent_sweep=True)
state.sweep_for_events()
state.sweep_for_advancements()
state.update_reachable_regions(1)
self._state_cache[self.multiworld, tuple(items)] = state
return state
Expand Down Expand Up @@ -221,8 +221,8 @@ def remove(self, items: typing.Union[Item, typing.Iterable[Item]]) -> None:
if isinstance(items, Item):
items = (items,)
for item in items:
if item.location and item.advancement and item.location in self.multiworld.state.events:
self.multiworld.state.events.remove(item.location)
if item.location and item.advancement and item.location in self.multiworld.state.advancements:
self.multiworld.state.advancements.remove(item.location)
self.multiworld.state.remove(item)

def can_reach_location(self, location: str) -> bool:
Expand Down
6 changes: 3 additions & 3 deletions test/general/test_fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def test_minimal_mixed_fill(self):
location_pool = player1.locations[1:] + player2.locations
item_pool = player1.prog_items[:-1] + player2.prog_items
fill_restrictive(multiworld, multiworld.state, location_pool, item_pool)
multiworld.state.sweep_for_events() # collect everything
multiworld.state.sweep_for_advancements() # collect everything

# all of player2's locations and items should be accessible (not all of player1's)
for item in player2.prog_items:
Expand Down Expand Up @@ -443,8 +443,8 @@ def test_double_sweep(self):
item = player1.prog_items[0]
item.code = None
location.place_locked_item(item)
multiworld.state.sweep_for_events()
multiworld.state.sweep_for_events()
multiworld.state.sweep_for_advancements()
multiworld.state.sweep_for_advancements()
self.assertTrue(multiworld.state.prog_items[item.player][item.name], "Sweep did not collect - Test flawed")
self.assertEqual(multiworld.state.prog_items[item.player][item.name], 1, "Sweep collected multiple times")

Expand Down
12 changes: 12 additions & 0 deletions test/general/test_reachability.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,18 @@ class TestBase(unittest.TestCase):
"Desert Northern Cliffs", # on top of mountain, only reachable via OWG
"Dark Death Mountain Bunny Descent Area" # OWG Mountain descent
},
# These Blasphemous regions are not reachable with default options
"Blasphemous": {
"D01Z04S13[SE]", # difficulty must be hard
"D01Z05S25[E]", # difficulty must be hard
"D02Z02S05[W]", # difficulty must be hard and purified_hand must be true
"D04Z01S06[E]", # purified_hand must be true
"D04Z02S02[NE]", # difficulty must be hard and purified_hand must be true
"D05Z01S11[SW]", # difficulty must be hard
"D06Z01S08[N]", # difficulty must be hard and purified_hand must be true
"D20Z02S11[NW]", # difficulty must be hard
"D20Z02S11[E]", # difficulty must be hard
},
"Ocarina of Time": {
"Prelude of Light Warp", # Prelude is not progression by default
"Serenade of Water Warp", # Serenade is not progression by default
Expand Down
Loading

0 comments on commit 5bf588b

Please sign in to comment.