Stardew Valley: Removed Pytest Requirement from everything (#1696)

This commit is contained in:
agilbert1412 2023-04-14 23:42:02 -04:00 committed by GitHub
parent d3baca9251
commit 3fdf07677c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 158 additions and 119 deletions

View File

@ -103,7 +103,7 @@ class StardewLogic:
item_rules: Dict[str, StardewRule] = field(default_factory=dict) item_rules: Dict[str, StardewRule] = field(default_factory=dict)
tree_fruit_rules: Dict[str, StardewRule] = field(default_factory=dict) tree_fruit_rules: Dict[str, StardewRule] = field(default_factory=dict)
seed_rules: Dict[str, StardewRule] = field(default_factory=dict) seed_rules: Dict[str, StardewRule] = field(default_factory=dict)
crops_rules: Dict[str, StardewRule] = field(default_factory=dict) crop_rules: Dict[str, StardewRule] = field(default_factory=dict)
fish_rules: Dict[str, StardewRule] = field(default_factory=dict) fish_rules: Dict[str, StardewRule] = field(default_factory=dict)
museum_rules: Dict[str, StardewRule] = field(default_factory=dict) museum_rules: Dict[str, StardewRule] = field(default_factory=dict)
building_rules: Dict[str, StardewRule] = field(default_factory=dict) building_rules: Dict[str, StardewRule] = field(default_factory=dict)
@ -127,8 +127,8 @@ class StardewLogic:
}) })
self.seed_rules.update({seed.name: self.can_buy_seed(seed) for seed in all_purchasable_seeds}) self.seed_rules.update({seed.name: self.can_buy_seed(seed) for seed in all_purchasable_seeds})
self.crops_rules.update({crop.name: self.can_grow_crop(crop) for crop in all_crops}) self.crop_rules.update({crop.name: self.can_grow_crop(crop) for crop in all_crops})
self.crops_rules.update({ self.crop_rules.update({
"Coffee Bean": (self.has_season("Spring") | self.has_season("Summer")) & self.has_traveling_merchant(), "Coffee Bean": (self.has_season("Spring") | self.has_season("Summer")) & self.has_traveling_merchant(),
}) })
@ -422,7 +422,7 @@ class StardewLogic:
self.item_rules.update(self.museum_rules) self.item_rules.update(self.museum_rules)
self.item_rules.update(self.tree_fruit_rules) self.item_rules.update(self.tree_fruit_rules)
self.item_rules.update(self.seed_rules) self.item_rules.update(self.seed_rules)
self.item_rules.update(self.crops_rules) self.item_rules.update(self.crop_rules)
self.building_rules.update({ self.building_rules.update({
"Barn": self.can_spend_money(6000) & self.has(["Wood", "Stone"]), "Barn": self.can_spend_money(6000) & self.has(["Wood", "Stone"]),
@ -795,6 +795,10 @@ class StardewLogic:
if self.options[options.Friendsanity] == options.Friendsanity.option_none: if self.options[options.Friendsanity] == options.Friendsanity.option_none:
return self.can_earn_relationship(npc, hearts) return self.can_earn_relationship(npc, hearts)
if npc not in all_villagers_by_name: if npc not in all_villagers_by_name:
if npc == "Pet":
if self.options[options.Friendsanity] == options.Friendsanity.option_bachelors:
return self.can_befriend_pet(hearts)
return self.received(f"Pet: 1 <3", hearts)
if npc == "Any" or npc == "Bachelor": if npc == "Any" or npc == "Bachelor":
possible_friends = [] possible_friends = []
for name in all_villagers_by_name: for name in all_villagers_by_name:
@ -806,6 +810,11 @@ class StardewLogic:
for name in all_villagers_by_name: for name in all_villagers_by_name:
mandatory_friends.append(self.has_relationship(name, hearts)) mandatory_friends.append(self.has_relationship(name, hearts))
return And(mandatory_friends) return And(mandatory_friends)
if npc.isnumeric():
possible_friends = []
for name in all_villagers_by_name:
possible_friends.append(self.has_relationship(name, hearts))
return Count(int(npc), possible_friends)
return self.can_earn_relationship(npc, hearts) return self.can_earn_relationship(npc, hearts)
villager = all_villagers_by_name[npc] villager = all_villagers_by_name[npc]
@ -888,9 +897,9 @@ class StardewLogic:
# Catching every fish not expected # Catching every fish not expected
# Shipping every item not expected # Shipping every item not expected
self.can_get_married() & self.has_house(2), self.can_get_married() & self.has_house(2),
self.has_lived_months(3), # 5 Friends (TODO) self.has_relationship("5", 8), # 5 Friends
self.has_lived_months(4), # 10 friends (TODO) self.has_relationship("10", 8), # 10 friends
self.can_befriend_pet(5), # Max Pet self.has_relationship("Pet", 5), # Max Pet
self.can_complete_community_center(), # Community Center Completion self.can_complete_community_center(), # Community Center Completion
self.can_complete_community_center(), # CC Ceremony first point self.can_complete_community_center(), # CC Ceremony first point
self.can_complete_community_center(), # CC Ceremony second point self.can_complete_community_center(), # CC Ceremony second point
@ -940,7 +949,11 @@ class StardewLogic:
return region_rule & geodes_rule # & monster_rule & extra_rule return region_rule & geodes_rule # & monster_rule & extra_rule
def can_complete_museum(self) -> StardewRule: def can_complete_museum(self) -> StardewRule:
rules = [self.can_mine_perfectly_in_the_skull_cavern(), self.received("Traveling Merchant Metal Detector", 4)] rules = [self.can_mine_perfectly_in_the_skull_cavern()]
if self.options[options.Museumsanity] != options.Museumsanity.option_none:
rules.append(self.received("Traveling Merchant Metal Detector", 4))
for donation in all_museum_items: for donation in all_museum_items:
rules.append(self.can_find_museum_item(donation)) rules.append(self.can_find_museum_item(donation))
return And(rules) return And(rules)

View File

@ -1,4 +1,4 @@
import pytest import unittest
from test.general import setup_solo_multiworld from test.general import setup_solo_multiworld
from .. import StardewValleyWorld, StardewLocation from .. import StardewValleyWorld, StardewLocation
@ -18,40 +18,71 @@ def collect_all(mw):
collect_all(multi_world) collect_all(multi_world)
@pytest.mark.parametrize("bundle_item", all_bundle_items_except_money, class TestLogic(unittest.TestCase):
ids=[i.item.name for i in all_bundle_items_except_money]) def test_given_bundle_item_then_is_available_in_logic(self):
def test_given_bundle_item_then_is_available_in_logic(bundle_item: BundleItem): for bundle_item in all_bundle_items_except_money:
assert bundle_item.item.name in logic.item_rules with self.subTest(msg=bundle_item.item.name):
assert bundle_item.item.name in logic.item_rules
def test_given_item_rule_then_can_be_resolved(self):
for item in logic.item_rules.keys():
with self.subTest(msg=item):
rule = logic.item_rules[item]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve item rule for {item} {rule}"
@pytest.mark.parametrize("item", logic.item_rules.keys(), ids=logic.item_rules.keys()) def test_given_building_rule_then_can_be_resolved(self):
def test_given_item_rule_then_can_be_resolved(item: str): for building in logic.building_rules.keys():
rule = logic.item_rules[item] with self.subTest(msg=building):
rule = logic.building_rules[building]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve building rule for {building} {rule}"
assert MISSING_ITEM not in repr(rule) def test_given_quest_rule_then_can_be_resolved(self):
assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {item} {rule}" for quest in logic.quest_rules.keys():
with self.subTest(msg=quest):
rule = logic.quest_rules[quest]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve quest rule for {quest} {rule}"
def test_given_tree_fruit_rule_then_can_be_resolved(self):
for tree_fruit in logic.tree_fruit_rules.keys():
with self.subTest(msg=tree_fruit):
rule = logic.tree_fruit_rules[tree_fruit]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve tree fruit rule for {tree_fruit} {rule}"
@pytest.mark.parametrize("item", logic.building_rules.keys(), ids=logic.building_rules.keys()) def test_given_seed_rule_then_can_be_resolved(self):
def test_given_building_rule_then_can_be_resolved(item: str): for seed in logic.seed_rules.keys():
rule = logic.building_rules[item] with self.subTest(msg=seed):
rule = logic.seed_rules[seed]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve seed rule for {seed} {rule}"
assert MISSING_ITEM not in repr(rule) def test_given_crop_rule_then_can_be_resolved(self):
assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {item} {rule}" for crop in logic.crop_rules.keys():
with self.subTest(msg=crop):
rule = logic.crop_rules[crop]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve crop rule for {crop} {rule}"
def test_given_fish_rule_then_can_be_resolved(self):
for fish in logic.fish_rules.keys():
with self.subTest(msg=fish):
rule = logic.fish_rules[fish]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve fish rule for {fish} {rule}"
@pytest.mark.parametrize("item", logic.quest_rules.keys(), ids=logic.quest_rules.keys()) def test_given_museum_rule_then_can_be_resolved(self):
def test_given_quest_rule_then_can_be_resolved(item: str): for donation in logic.museum_rules.keys():
rule = logic.quest_rules[item] with self.subTest(msg=donation):
rule = logic.museum_rules[donation]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve museum rule for {donation} {rule}"
assert MISSING_ITEM not in repr(rule) def test_given_location_rule_then_can_be_resolved(self):
assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {item} {rule}" for location in multi_world.get_locations(1):
with self.subTest(msg=location.name):
rule = location.access_rule
@pytest.mark.parametrize("location", multi_world.get_locations(1), assert MISSING_ITEM not in repr(rule)
ids=[loc.name for loc in multi_world.get_locations(1)]) assert rule == False_() or rule(multi_world.state), f"Could not resolve location rule for {location} {rule}"
def test_given_location_rule_then_can_be_resolved(location: StardewLocation):
rule = location.access_rule
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {location} {rule}"

View File

@ -1,10 +1,9 @@
import itertools import itertools
import unittest
import pytest
from BaseClasses import ItemClassification, MultiWorld from BaseClasses import ItemClassification, MultiWorld
from Options import SpecialRange from Options import SpecialRange
from . import setup_solo_multiworld from . import setup_solo_multiworld, SVTestBase
from .. import StardewItem, options from .. import StardewItem, options
from ..options import StardewOption, stardew_valley_option_classes from ..options import StardewOption, stardew_valley_option_classes
@ -12,142 +11,138 @@ SEASONS = {"Spring", "Summer", "Fall", "Winter"}
TOOLS = {"Hoe", "Pickaxe", "Axe", "Watering Can", "Trash Can", "Fishing Rod"} TOOLS = {"Hoe", "Pickaxe", "Axe", "Watering Can", "Trash Can", "Fishing Rod"}
def basic_checks(multi_world: MultiWorld): def assert_can_win(multiworld: MultiWorld):
assert StardewItem("Victory", ItemClassification.progression, None, 1) in multi_world.get_items() for item in multiworld.get_items():
assert_can_win(multi_world) multiworld.state.collect(item)
assert len(multi_world.itempool) == len(
[location for location in multi_world.get_locations() if not location.event]) assert multiworld.find_item("Victory", 1).can_reach(multiworld.state)
def assert_can_win(multi_world: MultiWorld): def basic_checks(multiworld: MultiWorld):
for item in multi_world.get_items(): assert StardewItem("Victory", ItemClassification.progression, None, 1) in multiworld.get_items()
multi_world.state.collect(item) assert_can_win(multiworld)
assert len(multiworld.itempool) == len(
assert multi_world.find_item("Victory", 1).can_reach(multi_world.state) [location for location in multiworld.get_locations() if not location.event])
@pytest.mark.parametrize("option, value", [(option, value) class TestGenerateDynamicOptions(SVTestBase):
for option in stardew_valley_option_classes def test_given_special_range_when_generate_then_basic_checks(self):
if issubclass(option, SpecialRange) for option in stardew_valley_option_classes:
for value in option.special_range_names]) if not issubclass(option, SpecialRange):
def test_given_special_range_when_generate_then_basic_checks(option: (SpecialRange, StardewOption), value): continue
multi_world = setup_solo_multiworld({option.internal_name: option.special_range_names[value]}) with self.subTest(msg=option.internal_name):
for value in option.special_range_names:
multiworld = setup_solo_multiworld({option.internal_name: option.special_range_names[value]})
basic_checks(multiworld)
basic_checks(multi_world) def test_given_choice_when_generate_then_basic_checks(self):
for option in stardew_valley_option_classes:
if not option.options:
continue
with self.subTest(msg=option.internal_name):
for value in option.options:
multiworld = setup_solo_multiworld({option.internal_name: option.options[value]})
basic_checks(multiworld)
def test_given_option_combination_when_generate_then_basic_checks(self):
option_combinations = [{options.Goal.internal_name: options.Goal.option_master_angler,
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla}]
ids = ["Master Angler + Vanilla tools"]
for i in range(0, len(option_combinations)):
option_combination = option_combinations[i]
id = ids[i]
with self.subTest(msg=f"{id}"):
multi_world = setup_solo_multiworld(option_combination)
basic_checks(multi_world)
@pytest.mark.parametrize("option, value", [(option, value) class TestGoal(SVTestBase):
for option in stardew_valley_option_classes def test_given_goal_when_generate_then_victory_is_in_correct_location(self):
if option.options for goal, location in [("community_center", "Complete Community Center"),
for value in option.options])
def test_given_choice_when_generate_then_basic_checks(option, value):
multi_world = setup_solo_multiworld({option.internal_name: option.options[value]})
basic_checks(multi_world)
@pytest.mark.parametrize("option_combination",
[{options.Goal.internal_name: options.Goal.option_master_angler,
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla}],
ids=["Master Angler + Vanilla tools"])
def test_given_option_combination_when_generate_then_basic_checks(option_combination):
multi_world = setup_solo_multiworld(option_combination)
basic_checks(multi_world)
class TestGoal:
@pytest.mark.parametrize("goal,location", [("community_center", "Complete Community Center"),
("grandpa_evaluation", "Succeed Grandpa's Evaluation"), ("grandpa_evaluation", "Succeed Grandpa's Evaluation"),
("bottom_of_the_mines", "Reach the Bottom of The Mines"), ("bottom_of_the_mines", "Reach the Bottom of The Mines"),
("cryptic_note", "Complete Quest Cryptic Note"), ("cryptic_note", "Complete Quest Cryptic Note"),
("master_angler", "Catch Every Fish")]) ("master_angler", "Catch Every Fish")]:
def test_given_goal_when_generate_then_victory_is_in_correct_location(self, goal, location): with self.subTest(msg=f"Goal: {goal}, Location: {location}"):
multi_world = setup_solo_multiworld({options.Goal.internal_name: options.Goal.options[goal]}) world_options = {options.Goal.internal_name: options.Goal.options[goal]}
victory = multi_world.find_item("Victory", 1) multi_world = setup_solo_multiworld(world_options)
victory = multi_world.find_item("Victory", 1)
assert victory.name == location assert victory.name == location
class TestSeasonRandomization: class TestSeasonRandomization(SVTestBase):
def test_given_disabled_when_generate_then_all_seasons_are_precollected(self): def test_given_disabled_when_generate_then_all_seasons_are_precollected(self):
multi_world = setup_solo_multiworld({options.SeasonRandomization.internal_name: world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_disabled}
options.SeasonRandomization.option_disabled}) multi_world = setup_solo_multiworld(world_options)
precollected_items = {item.name for item in multi_world.precollected_items[1]} precollected_items = {item.name for item in multi_world.precollected_items[1]}
assert all([season in precollected_items for season in SEASONS]) assert all([season in precollected_items for season in SEASONS])
@pytest.mark.parametrize("value", [value for value in options.SeasonRandomization.options if "randomized" in value]) def test_given_randomized_when_generate_then_all_seasons_are_in_the_pool_or_precollected(self):
def test_given_randomized_when_generate_then_all_seasons_are_in_the_pool_or_precollected(self, value): world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized}
multi_world = setup_solo_multiworld({options.SeasonRandomization.internal_name: multi_world = setup_solo_multiworld(world_options)
options.SeasonRandomization.options[value]})
precollected_items = {item.name for item in multi_world.precollected_items[1]} precollected_items = {item.name for item in multi_world.precollected_items[1]}
items = {item.name for item in multi_world.get_items()} | precollected_items items = {item.name for item in multi_world.get_items()} | precollected_items
assert all([season in items for season in SEASONS]) assert all([season in items for season in SEASONS])
assert len(SEASONS.intersection(precollected_items)) == 1 assert len(SEASONS.intersection(precollected_items)) == 1
def test_given_progressive_when_generate_then_3_progressive_seasons_are_in_the_pool(self): def test_given_progressive_when_generate_then_3_progressive_seasons_are_in_the_pool(self):
multi_world = setup_solo_multiworld({options.SeasonRandomization.internal_name: world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive}
options.SeasonRandomization.option_progressive}) multi_world = setup_solo_multiworld(world_options)
items = [item.name for item in multi_world.get_items()] items = [item.name for item in multi_world.get_items()]
assert items.count("Progressive Season") == 3 assert items.count("Progressive Season") == 3
class TestBackpackProgression: class TestBackpackProgression(SVTestBase):
def test_given_vanilla_when_generate_then_no_backpack_in_pool(self): def test_given_vanilla_when_generate_then_no_backpack_in_pool(self):
multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name: world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla}
options.BackpackProgression.option_vanilla}) multi_world = setup_solo_multiworld(world_options)
assert "Progressive Backpack" not in {item.name for item in multi_world.get_items()} assert "Progressive Backpack" not in {item.name for item in multi_world.get_items()}
@pytest.mark.parametrize("value", def test_given_progressive_when_generate_then_progressive_backpack_is_in_pool_two_times(self):
[value for value in options.BackpackProgression.options if "progressive" in value]) world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive}
def test_given_progressive_when_generate_then_progressive_backpack_is_in_pool_two_times(self, value): multi_world = setup_solo_multiworld(world_options)
multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name:
options.BackpackProgression.options[value]})
items = [item.name for item in multi_world.get_items()] items = [item.name for item in multi_world.get_items()]
assert items.count("Progressive Backpack") == 2 assert items.count("Progressive Backpack") == 2
@pytest.mark.parametrize("value", def test_given_progressive_when_generate_then_backpack_upgrades_are_locations(self):
[value for value in options.BackpackProgression.options if "progressive" in value]) world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive}
def test_given_progressive_when_generate_then_backpack_upgrades_are_locations(self, value): multi_world = setup_solo_multiworld(world_options)
multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name:
options.BackpackProgression.options[value]})
locations = {locations.name for locations in multi_world.get_locations(1)} locations = {locations.name for locations in multi_world.get_locations(1)}
assert "Large Pack" in locations assert "Large Pack" in locations
assert "Deluxe Pack" in locations assert "Deluxe Pack" in locations
def test_given_early_progressive_when_generate_then_progressive_backpack_is_in_early_pool(self): def test_given_early_progressive_when_generate_then_progressive_backpack_is_in_early_pool(self):
multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name: world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_early_progressive}
options.BackpackProgression.option_early_progressive}) multi_world = setup_solo_multiworld(world_options)
assert "Progressive Backpack" in multi_world.early_items[1] assert "Progressive Backpack" in multi_world.early_items[1]
class TestToolProgression: class TestToolProgression(SVTestBase):
def test_given_vanilla_when_generate_then_no_tool_in_pool(self): def test_given_vanilla_when_generate_then_no_tool_in_pool(self):
multi_world = setup_solo_multiworld({options.ToolProgression.internal_name: world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_vanilla}
options.ToolProgression.option_vanilla}) multi_world = setup_solo_multiworld(world_options)
items = {item.name for item in multi_world.get_items()} items = {item.name for item in multi_world.get_items()}
for tool in TOOLS: for tool in TOOLS:
assert tool not in items assert tool not in items
def test_given_progressive_when_generate_then_progressive_tool_of_each_is_in_pool_four_times(self): def test_given_progressive_when_generate_then_progressive_tool_of_each_is_in_pool_four_times(self):
multi_world = setup_solo_multiworld({options.ToolProgression.internal_name: world_options = {options.ToolProgression.internal_name:options.ToolProgression.option_progressive}
options.ToolProgression.option_progressive}) multi_world = setup_solo_multiworld(world_options)
items = [item.name for item in multi_world.get_items()] items = [item.name for item in multi_world.get_items()]
for tool in TOOLS: for tool in TOOLS:
assert items.count("Progressive " + tool) == 4 assert items.count("Progressive " + tool) == 4
def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self): def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self):
multi_world = setup_solo_multiworld({options.ToolProgression.internal_name: world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_progressive}
options.ToolProgression.option_progressive}) multi_world = setup_solo_multiworld(world_options)
locations = {locations.name for locations in multi_world.get_locations(1)} locations = {locations.name for locations in multi_world.get_locations(1)}
for material, tool in itertools.product(["Copper", "Iron", "Gold", "Iridium"], for material, tool in itertools.product(["Copper", "Iron", "Gold", "Iridium"],