From e5b0c2923dcc51f6a912933b3c8c8ae5cf5a54fb Mon Sep 17 00:00:00 2001 From: Fabian Dill Date: Sun, 28 May 2023 22:58:59 -0500 Subject: [PATCH 1/4] Hollow Knight: something # Conflicts: # worlds/hk/__init__.py HK: fix up split mothwing cloak logic HK: fix up potential if tree issues --- worlds/hk/__init__.py | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/worlds/hk/__init__.py b/worlds/hk/__init__.py index 78287305df5f..dd675ce6ce92 100644 --- a/worlds/hk/__init__.py +++ b/worlds/hk/__init__.py @@ -538,23 +538,40 @@ def create_vanilla_location(self, location: str, item: Item): def collect(self, state, item: HKItem) -> bool: change = super(HKWorld, self).collect(state, item) if change: + prog_items = state.prog_items for effect_name, effect_value in item_effects.get(item.name, {}).items(): - state.prog_items[item.player][effect_name] += effect_value - if item.name in {"Left_Mothwing_Cloak", "Right_Mothwing_Cloak"}: - if state.prog_items[item.player].get('RIGHTDASH', 0) and \ - state.prog_items[item.player].get('LEFTDASH', 0): - (state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"]) = \ - ([max(state.prog_items[item.player]["RIGHTDASH"], state.prog_items[item.player]["LEFTDASH"])] * 2) + prog_items[effect_name, self.player] += effect_value + + # a directional overflow in dash grants an upgrade for the other side + if item.name == "Right_Mothwing_Cloak" and \ + prog_items["RIGHTDASH", self.player] > 2 > prog_items["LEFTDASH", self.player]: + prog_items["OVERFLOWLEFTDASH", self.player] += 1 + prog_items["LEFTDASH", self.player] += 1 + elif item.name == "Left_Mothwing_Cloak" and \ + prog_items["LEFTDASH", self.player] > 2 > prog_items["RIGHTDASH", self.player]: + prog_items["OVERFLOWRIGHTDASH", self.player] += 1 + prog_items["RIGHTDASH", self.player] += 1 + return change def remove(self, state, item: HKItem) -> bool: change = super(HKWorld, self).remove(state, item) if change: + prog_items = state.prog_items + + # a directional overflow in dash grants an upgrade for the other side + if item.name == "Right_Mothwing_Cloak" and prog_items["OVERFLOWLEFTDASH", self.player]: + prog_items["LEFTDASH", self.player] -= 1 + prog_items["OVERFLOWLEFTDASH", self.player] -= 1 + elif item.name == "Left_Mothwing_Cloak" and prog_items["OVERFLOWRIGHTDASH", self.player]: + prog_items["RIGHTDASH", self.player] -= 1 + prog_items["OVERFLOWRIGHTDASH", self.player] -= 1 + for effect_name, effect_value in item_effects.get(item.name, {}).items(): - if state.prog_items[item.player][effect_name] == effect_value: - del state.prog_items[item.player][effect_name] - state.prog_items[item.player][effect_name] -= effect_value + if prog_items[effect_name, item.player] == effect_value: + del prog_items[effect_name, item.player] + prog_items[effect_name, item.player] -= effect_value return change From 742f0fc9de77bd1d1f5e3b9488d7e2a4835c00e7 Mon Sep 17 00:00:00 2001 From: qwint Date: Fri, 19 Jul 2024 11:50:33 -0500 Subject: [PATCH 2/4] fixed conflicts in hk_idk --- worlds/hk/__init__.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/worlds/hk/__init__.py b/worlds/hk/__init__.py index dd675ce6ce92..5a2778500cf2 100644 --- a/worlds/hk/__init__.py +++ b/worlds/hk/__init__.py @@ -540,17 +540,17 @@ def collect(self, state, item: HKItem) -> bool: if change: prog_items = state.prog_items for effect_name, effect_value in item_effects.get(item.name, {}).items(): - prog_items[effect_name, self.player] += effect_value + prog_items[self.player][effect_name] += effect_value # a directional overflow in dash grants an upgrade for the other side if item.name == "Right_Mothwing_Cloak" and \ - prog_items["RIGHTDASH", self.player] > 2 > prog_items["LEFTDASH", self.player]: - prog_items["OVERFLOWLEFTDASH", self.player] += 1 - prog_items["LEFTDASH", self.player] += 1 + prog_items[self.player]["RIGHTDASH"] > 2 > prog_items[self.player]["LEFTDASH"]: + prog_items[self.player]["OVERFLOWLEFTDASH"] += 1 + prog_items[self.player]["LEFTDASH"] += 1 elif item.name == "Left_Mothwing_Cloak" and \ - prog_items["LEFTDASH", self.player] > 2 > prog_items["RIGHTDASH", self.player]: - prog_items["OVERFLOWRIGHTDASH", self.player] += 1 - prog_items["RIGHTDASH", self.player] += 1 + prog_items[self.player]["LEFTDASH"] > 2 > prog_items[self.player]["RIGHTDASH"]: + prog_items[self.player]["OVERFLOWRIGHTDASH"] += 1 + prog_items[self.player]["RIGHTDASH"] += 1 return change @@ -561,17 +561,15 @@ def remove(self, state, item: HKItem) -> bool: prog_items = state.prog_items # a directional overflow in dash grants an upgrade for the other side - if item.name == "Right_Mothwing_Cloak" and prog_items["OVERFLOWLEFTDASH", self.player]: - prog_items["LEFTDASH", self.player] -= 1 - prog_items["OVERFLOWLEFTDASH", self.player] -= 1 - elif item.name == "Left_Mothwing_Cloak" and prog_items["OVERFLOWRIGHTDASH", self.player]: - prog_items["RIGHTDASH", self.player] -= 1 - prog_items["OVERFLOWRIGHTDASH", self.player] -= 1 + if item.name == "Right_Mothwing_Cloak" and prog_items[self.player]["OVERFLOWLEFTDASH"]: + prog_items[self.player]["LEFTDASH"] -= 1 + prog_items[self.player]["OVERFLOWLEFTDASH"] -= 1 + elif item.name == "Left_Mothwing_Cloak" and prog_items[self.player]["OVERFLOWRIGHTDASH"]: + prog_items[self.player]["RIGHTDASH"] -= 1 + prog_items[self.player]["OVERFLOWRIGHTDASH"] -= 1 for effect_name, effect_value in item_effects.get(item.name, {}).items(): - if prog_items[effect_name, item.player] == effect_value: - del prog_items[effect_name, item.player] - prog_items[effect_name, item.player] -= effect_value + prog_items[self.player][effect_name] -= effect_value return change From ed21cc1c5bc613250f8c1c51e080b4acd90a4c0e Mon Sep 17 00:00:00 2001 From: qwint Date: Wed, 2 Oct 2024 10:20:53 -0500 Subject: [PATCH 3/4] add unit tests and tests to confirm split cloak behavior --- worlds/hk/test/__init__.py | 69 ++++++ worlds/hk/test/test_Settings.py | 235 +++++++++++++++++++ worlds/hk/test/test_collect.py | 403 ++++++++++++++++++++++++++++++++ 3 files changed, 707 insertions(+) create mode 100644 worlds/hk/test/__init__.py create mode 100644 worlds/hk/test/test_Settings.py create mode 100644 worlds/hk/test/test_collect.py diff --git a/worlds/hk/test/__init__.py b/worlds/hk/test/__init__.py new file mode 100644 index 000000000000..d8369b752f79 --- /dev/null +++ b/worlds/hk/test/__init__.py @@ -0,0 +1,69 @@ +from test.bases import WorldTestBase +import typing +from BaseClasses import CollectionState # , Item, MultiWorld +from .. import HKWorld + + +class HKTestBase(WorldTestBase): + game = "Hollow Knight" + world: HKWorld + + def assertAccessIndependency( + self, + locations: typing.List[str], + possible_items: typing.Iterable[typing.Iterable[str]], + only_check_listed: bool = False) -> None: + """Asserts that the provided locations can't be reached without + the listed items but can be reached with any + one of the provided combinations""" + all_items = [ + item_name for + item_names in + possible_items for + item_name in + item_names + ] + + state = CollectionState(self.multiworld) + + for item_names in possible_items: + items = self.get_items_by_name(item_names) + for item in items: + self.collect_all_but(item.name) + for location in locations: + self.assertTrue(state.can_reach(location, "Location", 1), + f"{location} not reachable with {item_names}") + for item in items: + state.remove(item) + + def assertAccessWithout( + self, + locations: typing.List[str], + possible_items: typing.Iterable[typing.Iterable[str]]) -> None: + """Asserts that the provided locations can't be reached without the + listed items but can be reached with any + one of the provided combinations""" + all_items = [ + item_name for + item_names in + possible_items for + item_name in + item_names + ] + + state = CollectionState(self.multiworld) + self.collect_all_but(all_items, state) + for location in locations: + self.assertTrue( + state.can_reach(location, "Location", 1), + f"{location} is not reachable without {all_items}") + + +class selectSeedHK(WorldTestBase): + game = "Hollow Knight" + # player: typing.ClassVar[int] = 1 + seed = 0 + world: HKWorld + + def world_setup(self, *args, **kwargs): + super().world_setup(self.seed) diff --git a/worlds/hk/test/test_Settings.py b/worlds/hk/test/test_Settings.py new file mode 100644 index 000000000000..25b4b8e6b719 --- /dev/null +++ b/worlds/hk/test/test_Settings.py @@ -0,0 +1,235 @@ +from . import HKTestBase, selectSeedHK + + +class TestGoal_any(HKTestBase): + options = { + "Goal": "any", + } + + +class TestGoal_hollowknight(HKTestBase): + options = { + "Goal": "hollowknight", + } + + +class TestGoal_siblings(HKTestBase): + options = { + "Goal": "siblings", + } + + +class TestGoal_radiance(HKTestBase): + options = { + "Goal": "radiance", + } + + +class TestGoal_godhome(HKTestBase): + options = { + "Goal": "godhome", + } + + +class TestGoal_godhome_flower(HKTestBase): + options = { + "Goal": "godhome_flower", + } + + +class TestRandomize_All(HKTestBase): + options = { + "Goal": "any", + + "RandomizeDreamers": True, + "RandomizeSkills": True, + "RandomizeFocus": True, + "RandomizeSwim": True, + "RandomizeCharms": True, + "RandomizeKeys": True, + "RandomizeMaskShards": True, + "RandomizeVesselFragments": True, + "RandomizeCharmNotches": True, + "RandomizePaleOre": True, + "RandomizeGeoChests": True, + "RandomizeJunkPitChests": True, + "RandomizeRancidEggs": True, + "RandomizeRelics": True, + "RandomizeWhisperingRoots": True, + "RandomizeBossEssence": True, + "RandomizeGrubs": True, + "RandomizeMimics": True, + "RandomizeMaps": True, + "RandomizeStags": True, + "RandomizeLifebloodCocoons": True, + "RandomizeGrimmkinFlames": True, + "RandomizeJournalEntries": True, + "RandomizeNail": True, + "RandomizeGeoRocks": True, + "RandomizeBossGeo": True, + "RandomizeSoulTotems": True, + "RandomizeLoreTablets": True, + "RandomizeElevatorPass": True, + } + + +class TestRandomize_None(HKTestBase): + options = { + "Goal": "any", + + "RandomizeDreamers": False, + "RandomizeSkills": False, + "RandomizeFocus": False, + "RandomizeSwim": False, + "RandomizeCharms": False, + "RandomizeKeys": False, + "RandomizeMaskShards": False, + "RandomizeVesselFragments": False, + "RandomizeCharmNotches": False, + "RandomizePaleOre": False, + "RandomizeGeoChests": False, + "RandomizeJunkPitChests": False, + "RandomizeRancidEggs": False, + "RandomizeRelics": False, + "RandomizeWhisperingRoots": False, + "RandomizeBossEssence": False, + "RandomizeGrubs": False, + "RandomizeMimics": False, + "RandomizeMaps": False, + "RandomizeStags": False, + "RandomizeLifebloodCocoons": False, + "RandomizeGrimmkinFlames": False, + "RandomizeJournalEntries": False, + "RandomizeNail": False, + "RandomizeGeoRocks": False, + "RandomizeBossGeo": False, + "RandomizeSoulTotems": False, + "RandomizeLoreTablets": False, + "RandomizeElevatorPass": False, + } + + +class TestSplit_All(HKTestBase): + options = { + "Goal": "any", + + "SplitCrystalHeart": True, + "SplitMothwingCloak": True, + "SplitMantisClaw": True, + } + + +class TestCosts_All(HKTestBase): + options = { + "Goal": "any", + + "EggShopSlots": 9, + "SlyShopSlots": 9, + "SlyKeyShopSlots": 9, + "IseldaShopSlots": 9, + "SalubraShopSlots": 9, + "SalubraCharmShopSlots": 9, + "LegEaterShopSlots": 9, + "GrubfatherRewardSlots": 9, + "SeerRewardSlots": 9, + "ExtraShopSlots": 9, + "CostSanity": True, + "CostSanityHybridChance": 1, + "CostSanityEggWeight": 1, + "CostSanityGrubWeight": 1, + "CostSanityEssenceWeight": 1, + "CostSanityCharmWeight": 1, + "CostSanityGeoWeight": 1, + } + +# RandomizeDreamers, +# RandomizeSkills, +# RandomizeFocus, +# RandomizeSwim, +# RandomizeCharms, +# RandomizeKeys, +# RandomizeMaskShards, +# RandomizeVesselFragments, +# RandomizeCharmNotches, +# RandomizePaleOre, +# RandomizeGeoChests, +# RandomizeJunkPitChests, +# RandomizeRancidEggs, +# RandomizeRelics, +# RandomizeWhisperingRoots, +# RandomizeBossEssence, +# RandomizeGrubs, +# RandomizeMimics, +# RandomizeMaps, +# RandomizeStags, +# RandomizeLifebloodCocoons, +# RandomizeGrimmkinFlames, +# RandomizeJournalEntries, +# RandomizeNail, +# RandomizeGeoRocks, +# RandomizeBossGeo, +# RandomizeSoulTotems, +# RandomizeLoreTablets, +# RandomizeElevatorPass, +# StartLocation, +# Goal, +# WhitePalace, +# SplitCrystalHeart, +# SplitMothwingCloak, +# SplitMantisClaw, +# AddUnshuffledLocations, + + +# # not useful without more asserts +# PreciseMovement, +# ProficientCombat, +# BackgroundObjectPogos, +# EnemyPogos, +# ObscureSkips, +# ShadeSkips, +# InfectionSkips, +# FireballSkips, +# SpikeTunnels, +# AcidSkips, +# DamageBoosts, +# DangerousSkips, +# DarkRooms, +# ComplexSkips, +# DifficultSkips, +# RemoveSpellUpgrades, +# MinimumGeoPrice, +# MaximumGeoPrice, +# MinimumGrubPrice, +# MaximumGrubPrice, +# MinimumEssencePrice, +# MaximumEssencePrice, +# MinimumCharmPrice, +# MaximumCharmPrice, +# RandomCharmCosts, +# PlandoCharmCosts, +# MinimumEggPrice, +# MaximumEggPrice, +# EggShopSlots, +# SlyShopSlots, +# SlyKeyShopSlots, +# IseldaShopSlots, +# SalubraShopSlots, +# SalubraCharmShopSlots, +# LegEaterShopSlots, +# GrubfatherRewardSlots, +# SeerRewardSlots, +# ExtraShopSlots, +# CostSanity, +# CostSanityHybridChance, +# CostSanityEggWeight, +# CostSanityGrubWeight, +# CostSanityEssenceWeight, +# CostSanityCharmWeight, +# CostSanityGeoWeight, + +# # should be ok not testing +# StartingGeo, +# DeathLink, +# DeathLinkShade, +# DeathLinkBreaksFragileCharms, +# ExtraPlatforms, diff --git a/worlds/hk/test/test_collect.py b/worlds/hk/test/test_collect.py new file mode 100644 index 000000000000..0583534a489a --- /dev/null +++ b/worlds/hk/test/test_collect.py @@ -0,0 +1,403 @@ +import unittest +from worlds.AutoWorld import AutoWorldRegister +from test.general import setup_solo_multiworld +from .. import HKWorld + + +class TestBase(unittest.TestCase): + def testCollect(self): + game_name, world_type = "Hollow Knight", HKWorld + multiworld = setup_solo_multiworld(world_type) + proxy_world = multiworld.worlds[1] + empty_state = multiworld.state.copy() + + for item_name in world_type.item_name_to_id: + with self.subTest("Create Item", item_name=item_name, game_name=game_name): + item = proxy_world.create_item(item_name) + + with self.subTest("Item Name", item_name=item_name, game_name=game_name): + self.assertEqual(item.name, item_name) + + if item.advancement: + with self.subTest("Item State Collect", item_name=item_name, game_name=game_name): + multiworld.state.collect(item, True) + + with self.subTest("Item State Remove", item_name=item_name, game_name=game_name): + multiworld.state.remove(item) + + self.assertEqual(multiworld.state.prog_items, empty_state.prog_items, + "Item Collect -> Remove should restore empty state.") + else: + with self.subTest("Item State Collect No Change", item_name=item_name, game_name=game_name): + # Non-Advancement should not modify state. + base_state = multiworld.state.prog_items.copy() + multiworld.state.collect(item) + self.assertEqual(base_state, multiworld.state.prog_items) + + multiworld.state.prog_items = empty_state.prog_items + + # def testCollect_split_cloak(self): + # game_name, world_type = "Hollow Knight", HKWorld + # multiworld = setup_solo_multiworld(world_type) + # proxy_world = multiworld.worlds[1] + # empty_state = multiworld.state.copy() + + # l_cloaks = ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"] + # r_cloaks = ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Right_Mothwing_Cloak"] + # for cloaks in [l_cloaks, r_cloaks]: + # items = [] + # for item_name in cloaks: + # with self.subTest("Create Item", item_name=item_name, game_name=game_name): + # item = proxy_world.create_item(item_name) + # items.append(item) + + # with self.subTest("Item Name", item_name=item_name, game_name=game_name): + # self.assertEqual(item.name, item_name) + + # if item.advancement: + # with self.subTest("Item State Collect", item_name=item_name, game_name=game_name): + # multiworld.state.collect(item, True) + # proxy_world.random.shuffle(items) + # for item in items: + # with self.subTest("Item State Remove", item_name=item_name, game_name=game_name): + # multiworld.state.remove(item) + + # self.assertEqual(multiworld.state.prog_items, empty_state.prog_items, + # f"Item Collect -> Remove should restore empty state.\n{multiworld.state.prog_items}\n\n{empty_state.prog_items}") + + # multiworld.state.prog_items = empty_state.prog_items + + def cloak_test(self, collect_cloaks, remove_cloaks, final_state): + game_name, world_type = "Hollow Knight", HKWorld + multiworld = setup_solo_multiworld(world_type) + proxy_world = multiworld.worlds[1] + empty_state = multiworld.state.copy() + + items = [] + for item_name in collect_cloaks: + with self.subTest("Create Item", item_name=item_name, game_name=game_name): + item = proxy_world.create_item(item_name) + items.append(item) + + with self.subTest("Item State Collect", item_name=item_name, game_name=game_name): + multiworld.state.collect(item, True) + + for item_name in remove_cloaks: + index, item = next((index, item) for index, item in enumerate(items) if item.name == item_name) + items.pop(index) + with self.subTest("Item State Remove", item_name=item_name, game_name=game_name): + multiworld.state.remove(item) + + for item_name in ("Left_Mothwing_Cloak", "LEFTDASH", "Right_Mothwing_Cloak", "RIGHTDASH",): + if item_name in final_state: + self.assertEqual( + multiworld.state.prog_items[1][item_name], final_state[item_name], + f"expected {final_state[item_name]} {item_name}, found {multiworld.state.prog_items[1][item_name]}" + f"\nTest collected\n{collect_cloaks}\nand removed\n{remove_cloaks}\n" + ) + else: + self.assertEqual( + multiworld.state.prog_items[1][item_name], 0, + f"expected 0 {item_name}, found {multiworld.state.prog_items[1][item_name]}" + f"\nTest collected\n{collect_cloaks}\nand removed\n{remove_cloaks}\n" + ) + + multiworld.state.prog_items = empty_state.prog_items + + def testCollect_cloak_iterations(self): + + # LLR + with self.subTest("LLR"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + [], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + ) + with self.subTest("L - L"): + self.cloak_test( + ["Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {}, + ) + with self.subTest("LL - L"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1}, + ) + with self.subTest("LLR - L"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + # with self.subTest("L - R"): + # self.cloak_test( + # ["Left_Mothwing_Cloak"], + # ["Right_Mothwing_Cloak"], + # {}, + # ) + # with self.subTest("LL - R"): + # self.cloak_test( + # ["Left_Mothwing_Cloak", "Left_Mothwing_Cloak"], + # ["Right_Mothwing_Cloak"], + # {}, + # ) + with self.subTest("LLR - R"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2}, + ) + + # LRL + with self.subTest("LRL"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + [], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + ) + with self.subTest("L - L"): + self.cloak_test( + ["Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {}, + ) + with self.subTest("LR - L"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + with self.subTest("LRL - L"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + # with self.subTest("L - R"): + # self.cloak_test( + # ["Left_Mothwing_Cloak"], + # ["Right_Mothwing_Cloak"], + # {}, + # ) + with self.subTest("LR - R"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1}, + ) + with self.subTest("LRL - R"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2}, + ) + + # RLL + with self.subTest("RLL"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak", "Left_Mothwing_Cloak"], + [], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + ) + # with self.subTest("R - L"): + # self.cloak_test( + # ["Right_Mothwing_Cloak"], + # ["Left_Mothwing_Cloak"], + # {}, + # ) + with self.subTest("RL - L"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + with self.subTest("RLL - L"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + with self.subTest("R - R"): + self.cloak_test( + ["Right_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {}, + ) + with self.subTest("RL - R"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1}, + ) + with self.subTest("RLL - R"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2}, + ) + + # RRL + with self.subTest("RRL"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + [], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + ) + # with self.subTest("R - L"): + # self.cloak_test( + # ["Right_Mothwing_Cloak"], + # ["Left_Mothwing_Cloak"], + # {}, + # ) + # with self.subTest("RR - L"): + # self.cloak_test( + # ["Right_Mothwing_Cloak", "Right_Mothwing_Cloak"], + # ["Left_Mothwing_Cloak"], + # {"Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + # ) + with self.subTest("RRL - L"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + ) + with self.subTest("R - R"): + self.cloak_test( + ["Right_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {}, + ) + with self.subTest("RR - R"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + with self.subTest("RRL - R"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + + # RLR + with self.subTest("RLR"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + [], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + ) + # with self.subTest("R - L"): + # self.cloak_test( + # ["Right_Mothwing_Cloak"], + # ["Left_Mothwing_Cloak"], + # {}, + # ) + with self.subTest("RL - L"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + with self.subTest("RLR - L"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + ) + with self.subTest("R - R"): + self.cloak_test( + ["Right_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {}, + ) + with self.subTest("RL - R"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1}, + ) + with self.subTest("RLR - R"): + self.cloak_test( + ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + + # LRR + with self.subTest("LRR"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Right_Mothwing_Cloak"], + [], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + ) + with self.subTest("L - L"): + self.cloak_test( + ["Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {}, + ) + with self.subTest("LR - L"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + with self.subTest("LRR - L"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + ) + # with self.subTest("L - R"): + # self.cloak_test( + # ["Left_Mothwing_Cloak"], + # ["Right_Mothwing_Cloak"], + # {}, + # ) + with self.subTest("LR - R"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1}, + ) + with self.subTest("LRR - R"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Right_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 1, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 1}, + ) + + # extra in pool + with self.subTest("Bonus: LLRL - L"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Left_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 2}, + ) + with self.subTest("Bonus: LLRL - R"): + self.cloak_test( + ["Left_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], + ["Right_Mothwing_Cloak"], + {"Left_Mothwing_Cloak": 3, "LEFTDASH": 3}, + ) + + # non-split + with self.subTest("Bonus: non-split"): + self.cloak_test( + ["Mothwing_Cloak", "Shade_Cloak"], + [], + {"Mothwing_Cloak": 1, "Shade_Cloak": 1, "LEFTDASH": 2, "RIGHTDASH": 2}, + ) + self.cloak_test( + ["Mothwing_Cloak", "Shade_Cloak"], + ["Mothwing_Cloak"], + {"Shade_Cloak": 1, "LEFTDASH": 1, "RIGHTDASH": 1}, + ) + self.cloak_test( + ["Mothwing_Cloak", "Shade_Cloak"], + ["Shade_Cloak"], + {"Mothwing_Cloak": 1, "LEFTDASH": 1, "RIGHTDASH": 1}, + ) From 4009fa1de88f1d209c4a02b89126a46a38f5f441 Mon Sep 17 00:00:00 2001 From: qwint Date: Wed, 2 Oct 2024 10:52:13 -0500 Subject: [PATCH 4/4] fixes to tests and col/rem --- worlds/hk/__init__.py | 22 ++++++++++++---------- worlds/hk/test/test_collect.py | 12 ++++++------ 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/worlds/hk/__init__.py b/worlds/hk/__init__.py index 5a2778500cf2..05603919ab76 100644 --- a/worlds/hk/__init__.py +++ b/worlds/hk/__init__.py @@ -543,14 +543,14 @@ def collect(self, state, item: HKItem) -> bool: prog_items[self.player][effect_name] += effect_value # a directional overflow in dash grants an upgrade for the other side - if item.name == "Right_Mothwing_Cloak" and \ - prog_items[self.player]["RIGHTDASH"] > 2 > prog_items[self.player]["LEFTDASH"]: + if prog_items[self.player]["RIGHTDASH"] > prog_items[self.player]["LEFTDASH"] > 0: prog_items[self.player]["OVERFLOWLEFTDASH"] += 1 prog_items[self.player]["LEFTDASH"] += 1 - elif item.name == "Left_Mothwing_Cloak" and \ - prog_items[self.player]["LEFTDASH"] > 2 > prog_items[self.player]["RIGHTDASH"]: + assert item.name in ("Left_Mothwing_Cloak", "Right_Mothwing_Cloak",) + elif prog_items[self.player]["LEFTDASH"] > prog_items[self.player]["RIGHTDASH"] > 0: prog_items[self.player]["OVERFLOWRIGHTDASH"] += 1 prog_items[self.player]["RIGHTDASH"] += 1 + assert item.name in ("Left_Mothwing_Cloak", "Right_Mothwing_Cloak",) return change @@ -561,12 +561,14 @@ def remove(self, state, item: HKItem) -> bool: prog_items = state.prog_items # a directional overflow in dash grants an upgrade for the other side - if item.name == "Right_Mothwing_Cloak" and prog_items[self.player]["OVERFLOWLEFTDASH"]: - prog_items[self.player]["LEFTDASH"] -= 1 - prog_items[self.player]["OVERFLOWLEFTDASH"] -= 1 - elif item.name == "Left_Mothwing_Cloak" and prog_items[self.player]["OVERFLOWRIGHTDASH"]: - prog_items[self.player]["RIGHTDASH"] -= 1 - prog_items[self.player]["OVERFLOWRIGHTDASH"] -= 1 + if item.name in ("Left_Mothwing_Cloak", "Right_Mothwing_Cloak",) and \ + prog_items[self.player]["RIGHTDASH"] == prog_items[self.player]["LEFTDASH"] > 0: + if prog_items[self.player]["OVERFLOWLEFTDASH"]: + prog_items[self.player]["LEFTDASH"] -= 1 + prog_items[self.player]["OVERFLOWLEFTDASH"] -= 1 + if prog_items[self.player]["OVERFLOWRIGHTDASH"]: + prog_items[self.player]["RIGHTDASH"] -= 1 + prog_items[self.player]["OVERFLOWRIGHTDASH"] -= 1 for effect_name, effect_value in item_effects.get(item.name, {}).items(): prog_items[self.player][effect_name] -= effect_value diff --git a/worlds/hk/test/test_collect.py b/worlds/hk/test/test_collect.py index 0583534a489a..feb013da9684 100644 --- a/worlds/hk/test/test_collect.py +++ b/worlds/hk/test/test_collect.py @@ -111,7 +111,7 @@ def testCollect_cloak_iterations(self): self.cloak_test( ["Left_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], [], - {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 2}, ) with self.subTest("L - L"): self.cloak_test( @@ -155,7 +155,7 @@ def testCollect_cloak_iterations(self): self.cloak_test( ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], [], - {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 2}, ) with self.subTest("L - L"): self.cloak_test( @@ -199,7 +199,7 @@ def testCollect_cloak_iterations(self): self.cloak_test( ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak", "Left_Mothwing_Cloak"], [], - {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 1, "RIGHTDASH": 2}, ) # with self.subTest("R - L"): # self.cloak_test( @@ -243,7 +243,7 @@ def testCollect_cloak_iterations(self): self.cloak_test( ["Right_Mothwing_Cloak", "Right_Mothwing_Cloak", "Left_Mothwing_Cloak"], [], - {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, ) # with self.subTest("R - L"): # self.cloak_test( @@ -287,7 +287,7 @@ def testCollect_cloak_iterations(self): self.cloak_test( ["Right_Mothwing_Cloak", "Left_Mothwing_Cloak", "Right_Mothwing_Cloak"], [], - {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, ) # with self.subTest("R - L"): # self.cloak_test( @@ -331,7 +331,7 @@ def testCollect_cloak_iterations(self): self.cloak_test( ["Left_Mothwing_Cloak", "Right_Mothwing_Cloak", "Right_Mothwing_Cloak"], [], - {"Left_Mothwing_Cloak": 2, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, + {"Left_Mothwing_Cloak": 1, "LEFTDASH": 2, "Right_Mothwing_Cloak": 2, "RIGHTDASH": 2}, ) with self.subTest("L - L"): self.cloak_test(