989 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			989 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			Python
		
	
	
	
from __future__ import annotations
 | 
						|
 | 
						|
from dataclasses import dataclass, field
 | 
						|
from typing import Dict, Union, Optional, Iterable, Sized, Tuple, List
 | 
						|
 | 
						|
from . import options
 | 
						|
from .data import all_fish, FishItem, all_purchasable_seeds, SeedItem, all_crops, CropItem
 | 
						|
from .data.bundle_data import BundleItem
 | 
						|
from .data.museum_data import all_museum_items, MuseumItem
 | 
						|
from .data.region_data import SVRegion
 | 
						|
from .data.villagers_data import all_villagers_by_name
 | 
						|
from .items import all_items, Group
 | 
						|
from .options import StardewOptions
 | 
						|
from .stardew_rule import False_, Reach, Or, True_, Received, Count, And, Has, TotalReceived, StardewRule
 | 
						|
 | 
						|
MONEY_PER_MONTH = 15000
 | 
						|
MISSING_ITEM = "THIS ITEM IS MISSING"
 | 
						|
 | 
						|
tool_materials = {
 | 
						|
    "Copper": 1,
 | 
						|
    "Iron": 2,
 | 
						|
    "Gold": 3,
 | 
						|
    "Iridium": 4
 | 
						|
}
 | 
						|
 | 
						|
tool_prices = {
 | 
						|
    "Copper": 2000,
 | 
						|
    "Iron": 5000,
 | 
						|
    "Gold": 10000,
 | 
						|
    "Iridium": 25000
 | 
						|
}
 | 
						|
 | 
						|
skill_level_per_month_end = {
 | 
						|
    0: {
 | 
						|
        "Farming": 2,
 | 
						|
        "Fishing": 2,
 | 
						|
        "Foraging": 2,
 | 
						|
        "Mining": 2,
 | 
						|
        "Combat": 2,
 | 
						|
    },
 | 
						|
    1: {
 | 
						|
        "Farming": 4,
 | 
						|
        "Fishing": 4,
 | 
						|
        "Foraging": 4,
 | 
						|
        "Mining": 4,
 | 
						|
        "Combat": 3,
 | 
						|
    },
 | 
						|
    2: {
 | 
						|
        "Farming": 7,
 | 
						|
        "Fishing": 5,
 | 
						|
        "Foraging": 5,
 | 
						|
        "Mining": 5,
 | 
						|
        "Combat": 4,
 | 
						|
    },
 | 
						|
    3: {
 | 
						|
        "Farming": 7,
 | 
						|
        "Fishing": 7,
 | 
						|
        "Foraging": 6,
 | 
						|
        "Mining": 7,
 | 
						|
        "Combat": 5,
 | 
						|
    },
 | 
						|
    4: {
 | 
						|
        "Farming": 10,
 | 
						|
        "Fishing": 10,
 | 
						|
        "Foraging": 10,
 | 
						|
        "Mining": 10,
 | 
						|
        "Combat": 10,
 | 
						|
    },
 | 
						|
}
 | 
						|
month_end_per_skill_level: Dict[Tuple[str, int], int] = {}
 | 
						|
month_end_per_total_level: Dict[int, int] = {}
 | 
						|
 | 
						|
 | 
						|
def initialize_season_per_skill_level():
 | 
						|
    current_level = {
 | 
						|
        "Farming": 0,
 | 
						|
        "Fishing": 0,
 | 
						|
        "Foraging": 0,
 | 
						|
        "Mining": 0,
 | 
						|
        "Combat": 0,
 | 
						|
    }
 | 
						|
    for month_end, skills in skill_level_per_month_end.items():
 | 
						|
        for skill, expected_level in skills.items():
 | 
						|
            for level_up in range(current_level[skill] + 1, expected_level + 1):
 | 
						|
                skill_level = (skill, level_up)
 | 
						|
                if skill_level not in month_end_per_skill_level:
 | 
						|
                    month_end_per_skill_level[skill_level] = month_end
 | 
						|
        level_up = 0
 | 
						|
        for level_up in range(level_up + 1, sum(skills.values()) + 1):
 | 
						|
            if level_up not in month_end_per_total_level:
 | 
						|
                month_end_per_total_level[level_up] = month_end
 | 
						|
 | 
						|
 | 
						|
initialize_season_per_skill_level()
 | 
						|
week_days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
 | 
						|
 | 
						|
 | 
						|
@dataclass(frozen=True, repr=False)
 | 
						|
class StardewLogic:
 | 
						|
    player: int
 | 
						|
    options: StardewOptions
 | 
						|
 | 
						|
    item_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)
 | 
						|
    crop_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)
 | 
						|
    building_rules: Dict[str, StardewRule] = field(default_factory=dict)
 | 
						|
    quest_rules: Dict[str, StardewRule] = field(default_factory=dict)
 | 
						|
 | 
						|
    def __post_init__(self):
 | 
						|
        self.fish_rules.update({fish.name: self.can_catch_fish(fish) for fish in all_fish})
 | 
						|
        self.museum_rules.update({donation.name: self.can_find_museum_item(donation) for donation in all_museum_items})
 | 
						|
 | 
						|
        self.tree_fruit_rules.update({
 | 
						|
            "Apple": self.has_lived_months(1) & (self.has_season("Fall") | self.can_reach_region(SVRegion.greenhouse)),
 | 
						|
            "Apricot": self.has_lived_months(1) & (self.has_season("Spring") | self.can_reach_region(SVRegion.greenhouse)),
 | 
						|
            "Cherry": self.has_lived_months(1) & (self.has_season("Spring") | self.can_reach_region(SVRegion.greenhouse)),
 | 
						|
            "Orange": self.has_lived_months(1) & (self.has_season("Summer") | self.can_reach_region(SVRegion.greenhouse)),
 | 
						|
            "Peach": self.has_lived_months(1) & (self.has_season("Summer") | self.can_reach_region(SVRegion.greenhouse)),
 | 
						|
            "Pomegranate": self.has_lived_months(1) & (self.has_season("Fall") | self.can_reach_region(SVRegion.greenhouse)),
 | 
						|
            "Banana Sapling": self.can_reach_region(SVRegion.ginger_island),
 | 
						|
            "Mango Sapling": self.can_reach_region(SVRegion.ginger_island),
 | 
						|
            "Banana": self.has("Banana Sapling") & (self.has_season("Summer") | self.can_reach_region(SVRegion.greenhouse)),
 | 
						|
            "Mango": self.has("Mango Sapling") & (self.has_season("Summer") | self.can_reach_region(SVRegion.greenhouse)),
 | 
						|
        })
 | 
						|
 | 
						|
        self.seed_rules.update({seed.name: self.can_buy_seed(seed) for seed in all_purchasable_seeds})
 | 
						|
        self.crop_rules.update({crop.name: self.can_grow_crop(crop) for crop in all_crops})
 | 
						|
        self.crop_rules.update({
 | 
						|
            "Coffee Bean": (self.has_season("Spring") | self.has_season("Summer")) & self.has_traveling_merchant(),
 | 
						|
        })
 | 
						|
 | 
						|
        self.item_rules.update({
 | 
						|
            "Aged Roe": self.has("Preserves Jar") & self.has("Roe"),
 | 
						|
            "Algae Soup": self.can_cook() & self.has("Green Algae") & self.has_relationship("Clint", 3),
 | 
						|
            "Any Egg": self.has("Chicken Egg") | self.has("Duck Egg"),
 | 
						|
            "Artichoke Dip": self.can_cook() & self.has_season("Fall") & self.has("Artichoke") & self.has("Cow Milk"),
 | 
						|
            "Artifact Trove": self.has("Omni Geode") & self.can_reach_region(SVRegion.desert),
 | 
						|
            "Bait": self.has_skill_level("Fishing", 2),
 | 
						|
            "Bat Wing": self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern(),
 | 
						|
            "Battery Pack": self.has("Lightning Rod"),
 | 
						|
            "Bean Hotpot": self.can_cook() & self.has_relationship("Clint", 7) & self.has("Green Bean"),
 | 
						|
            "Bee House": self.has_skill_level("Farming", 3) & self.has("Iron Bar") & self.has("Maple Syrup"),
 | 
						|
            "Beer": (self.has("Keg") & self.has("Wheat")) | self.can_spend_money(400),
 | 
						|
            "Blackberry": self.has_season("Fall"),
 | 
						|
            "Blackberry Cobbler": self.can_cook() & self.has_season("Fall") & self.has_year_two() &
 | 
						|
                                  self.has("Blackberry") & self.has("Sugar") & self.has("Wheat Flour"),
 | 
						|
            "Blueberry Tart": self.has("Blueberry") & self.has("Any Egg") & self.has_relationship("Pierre", 3),
 | 
						|
            "Bouquet": self.has_relationship("Bachelor", 8),
 | 
						|
            "Bread": self.can_spend_money(120) | (self.can_spend_money(100) & self.can_cook()),
 | 
						|
            "Broken CD": self.can_crab_pot(),
 | 
						|
            "Broken Glasses": self.can_crab_pot(),
 | 
						|
            "Bug Meat": self.can_mine_in_the_mines_floor_1_40(),
 | 
						|
            "Cactus Fruit": self.can_reach_region(SVRegion.desert),
 | 
						|
            "Cave Carrot": self.can_mine_to_floor(10),
 | 
						|
            "Caviar": self.has("Preserves Jar") & self.has("Sturgeon Roe"),
 | 
						|
            "Chanterelle": self.has_season("Fall") & self.can_reach_region(SVRegion.secret_woods),
 | 
						|
            "Cheese Press": self.has_skill_level("Farming", 6) & self.has("Hardwood") & self.has("Copper Bar"),
 | 
						|
            "Cheese": (self.has("Cow Milk") & self.has("Cheese Press")) |
 | 
						|
                      (self.can_reach_region(SVRegion.desert) & self.has("Emerald")),
 | 
						|
            "Cheese Cauliflower": self.has(["Cheese", "Cauliflower"]) & self.has_relationship("Pam", 3) &
 | 
						|
                                  self.can_cook(),
 | 
						|
            "Chicken": self.has_building("Coop"),
 | 
						|
            "Chicken Egg": self.has(["Egg", "Egg (Brown)", "Large Egg", "Large Egg (Brown)"], 1),
 | 
						|
            "Chocolate Cake": self.can_cook() & self.has_season("Winter") & self.has("Wheat Flour") & self.has(
 | 
						|
                "Sugar") & self.has("Any Egg"),
 | 
						|
            "Chowder": self.can_cook() & self.has_relationship("Willy", 3) & self.has(["Clam", "Cow Milk"]),
 | 
						|
            "Clam": True_(),
 | 
						|
            "Clay": True_(),
 | 
						|
            "Glazed Yams": self.can_cook() & self.has_season("Fall") & self.has("Yam") & self.has("Sugar"),
 | 
						|
            "Cloth": (self.has("Wool") & self.has("Loom")) |
 | 
						|
                     (self.can_reach_region(SVRegion.desert) & self.has("Aquamarine")),
 | 
						|
            "Coal": True_(),
 | 
						|
            "Cockle": True_(),
 | 
						|
            "Coconut": self.can_reach_region(SVRegion.desert),
 | 
						|
            "Coffee": (self.has("Keg") & self.has("Coffee Bean")) | self.has("Coffee Maker") |
 | 
						|
                      self.can_spend_money(300) | self.has("Hot Java Ring"),
 | 
						|
            "Coffee Maker": False_(),
 | 
						|
            "Common Mushroom": self.has_season("Fall") |
 | 
						|
                               (self.has_season("Spring") & self.can_reach_region(SVRegion.secret_woods)),
 | 
						|
            "Complete Breakfast": self.can_cook() & self.has_season("Spring") & self.has_lived_months(4) &
 | 
						|
                                  self.has("Fried Egg") & self.has("Cow Milk") & self.has("Hashbrowns") | self.has(
 | 
						|
                "Pancakes"),
 | 
						|
            "Copper Bar": self.can_smelt("Copper Ore"),
 | 
						|
            "Copper Ore": self.can_mine_in_the_mines_floor_1_40() | self.can_mine_in_the_skull_cavern(),
 | 
						|
            "Coral": self.can_reach_region(SVRegion.tide_pools) | self.has_season("Summer"),
 | 
						|
            "Cow": self.has_building("Barn"),
 | 
						|
            "Cow Milk": self.has("Milk") | self.has("Large Milk"),
 | 
						|
            "Crab": self.can_crab_pot(),
 | 
						|
            "Crab Cakes": self.can_mine_in_the_skull_cavern() |
 | 
						|
                          (self.can_cook() & self.has_season("Fall") & self.has_year_two() & self.has("Crab") &
 | 
						|
                           self.has("Wheat Flour") & self.has("Chicken Egg") & self.has("Oil")),
 | 
						|
            "Crab Pot": self.has_skill_level("Fishing", 3),
 | 
						|
            "Cranberry Candy": self.can_cook() & self.has_season("Winter") & self.has("Cranberries") &
 | 
						|
                               self.has("Apple") & self.has("Sugar"),
 | 
						|
            "Crayfish": self.can_crab_pot(),
 | 
						|
            "Crispy Bass": self.can_cook() & self.has_relationship("Kent", 3) & self.has("Largemouth Bass") &
 | 
						|
                           self.has("Wheat Flour") & self.has("Oil"),
 | 
						|
            "Crocus": self.has_season("Winter"),
 | 
						|
            "Crystal Fruit": self.has_season("Winter"),
 | 
						|
            "Daffodil": self.has_season("Spring"),
 | 
						|
            "Dandelion": self.has_season("Spring"),
 | 
						|
            "Dish O' The Sea": self.can_cook() & self.has_skill_level("Fishing", 3) &
 | 
						|
                               self.has(["Sardine", "Hashbrowns"]),
 | 
						|
            "Dorado": self.can_fish(78) & self.has_season("Summer"),
 | 
						|
            "Dried Starfish": self.can_fish() & self.can_reach_region(SVRegion.beach),
 | 
						|
            "Driftwood": self.can_crab_pot(),
 | 
						|
            "Duck Egg": self.has("Duck"),
 | 
						|
            "Duck Feather": self.has("Duck"),
 | 
						|
            "Duck": self.has_building("Big Coop"),
 | 
						|
            "Egg": self.has("Chicken"),
 | 
						|
            "Egg (Brown)": self.has("Chicken"),
 | 
						|
            "Eggplant Parmesan": self.can_cook() & self.has_relationship("Lewis", 7) & self.has("Eggplant") & self.has(
 | 
						|
                "Tomato"),
 | 
						|
            "Escargot": self.can_cook() & self.has_relationship("Willy", 5) & self.has("Snail") & self.has("Garlic"),
 | 
						|
            "Farmer's Lunch": self.can_cook() & self.has_skill_level("Farming", 3) & self.has("Omelet") & self.has(
 | 
						|
                "Parsnip"),
 | 
						|
            "Fiber": True_(),
 | 
						|
            "Fiddlehead Fern": self.can_reach_region(SVRegion.secret_woods) & self.has_season("Summer"),
 | 
						|
            "Fiddlehead Risotto": self.can_cook() & self.has_season("Fall") & self.has("Oil") &
 | 
						|
                                  self.has("Fiddlehead Fern") & self.has("Garlic"),
 | 
						|
            "Fishing Chest": self.can_fish_chests(),
 | 
						|
            "Fish Taco": self.can_cook() & self.has_relationship("Linus", 7) & self.has("Tuna") & self.has("Tortilla") &
 | 
						|
                         self.has("Red Cabbage") & self.has("Mayonnaise"),
 | 
						|
            "Fried Calamari": self.can_cook() & self.has_relationship("Jodi", 3) & self.has("Squid") &
 | 
						|
                              self.has("Wheat Flour") & self.has("Oil"),
 | 
						|
            "Fried Eel": self.can_cook() & self.has_relationship("George", 3) & self.has("Eel") & self.has("Oil"),
 | 
						|
            "Fried Egg": self.can_cook() & self.has("Any Egg"),
 | 
						|
            "Fried Mushroom": self.can_cook() & self.has_relationship("Demetrius", 3) & self.has(
 | 
						|
                "Morel") & self.has("Common Mushroom"),
 | 
						|
            "Frozen Geode": self.can_mine_in_the_mines_floor_41_80(),
 | 
						|
            "Fruit Salad": self.can_cook() & self.has_season("Fall") & self.has_year_two() & self.has("Blueberry") &
 | 
						|
                           self.has("Melon") & self.has("Apricot"),
 | 
						|
            "Furnace": self.has("Stone") & self.has("Copper Ore"),
 | 
						|
            "Geode": self.can_mine_in_the_mines_floor_1_40(),
 | 
						|
            "Ginger": self.can_reach_region(SVRegion.ginger_island),
 | 
						|
            "Ginger Ale": self.can_cook() & self.can_reach_region(SVRegion.ginger_island) & self.has("Ginger") & self.has(
 | 
						|
                "Sugar"),
 | 
						|
            "Goat Cheese": self.has("Goat Milk") & self.has("Cheese Press"),
 | 
						|
            "Goat Milk": self.has("Goat"),
 | 
						|
            "Goat": self.has_building("Big Barn"),
 | 
						|
            "Gold Bar": self.can_smelt("Gold Ore"),
 | 
						|
            "Gold Ore": self.can_mine_in_the_mines_floor_81_120() | self.can_mine_in_the_skull_cavern(),
 | 
						|
            "Golden Pumpkin": self.has_season("Fall") | self.has("Artifact Trove"),
 | 
						|
            "Green Algae": self.can_fish(),
 | 
						|
            "Green Tea": self.has("Keg") & self.has("Tea Leaves"),
 | 
						|
            "Hardwood": self.has_tool("Axe", "Copper"),
 | 
						|
            "Hashbrowns": self.can_cook() & self.can_spend_money(50) & self.has("Potato"),
 | 
						|
            "Hazelnut": self.has_season("Fall"),
 | 
						|
            "Holly": self.has_season("Winter"),
 | 
						|
            "Honey": self.can_reach_region(SVRegion.desert) |
 | 
						|
                     (self.has("Bee House") &
 | 
						|
                      (self.has_season("Spring") | self.has_season("Summer") | self.has_season("Fall"))),
 | 
						|
            "Hot Java Ring": self.can_reach_region(SVRegion.ginger_island),
 | 
						|
            "Ice Cream": (self.has_season("Summer") & self.can_reach_region(SVRegion.town)) | self.can_reach_region(
 | 
						|
                "The Desert"),
 | 
						|
            # | (self.can_cook() & self.has_relationship("Jodi", 7) & self.has("Cow Milk") & self.has("Sugar")),
 | 
						|
            "Iridium Bar": self.can_smelt("Iridium Ore"),
 | 
						|
            "Iridium Ore": self.can_mine_in_the_skull_cavern(),
 | 
						|
            "Iron Bar": self.can_smelt("Iron Ore"),
 | 
						|
            "Iron Ore": self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern(),
 | 
						|
            "Jelly": self.has("Preserves Jar"),
 | 
						|
            "JotPK Small Buff": self.has_jotpk_power_level(2),
 | 
						|
            "JotPK Medium Buff": self.has_jotpk_power_level(4),
 | 
						|
            "JotPK Big Buff": self.has_jotpk_power_level(7),
 | 
						|
            "JotPK Max Buff": self.has_jotpk_power_level(9),
 | 
						|
            "Juice": self.has("Keg"),
 | 
						|
            "Junimo Kart Small Buff": self.has_junimo_kart_power_level(2),
 | 
						|
            "Junimo Kart Medium Buff": self.has_junimo_kart_power_level(4),
 | 
						|
            "Junimo Kart Big Buff": self.has_junimo_kart_power_level(6),
 | 
						|
            "Junimo Kart Max Buff": self.has_junimo_kart_power_level(8),
 | 
						|
            "Keg": self.has_skill_level("Farming", 8) & self.has("Iron Bar") & self.has("Copper Bar") & self.has(
 | 
						|
                "Oak Resin"),
 | 
						|
            "Large Egg": self.has("Chicken"),
 | 
						|
            "Large Egg (Brown)": self.has("Chicken"),
 | 
						|
            "Large Goat Milk": self.has("Goat"),
 | 
						|
            "Large Milk": self.has("Cow"),
 | 
						|
            "Leek": self.has_season("Spring"),
 | 
						|
            "Lightning Rod": self.has_skill_level("Foraging", 6),
 | 
						|
            "Lobster": self.can_crab_pot(),
 | 
						|
            "Loom": self.has_skill_level("Farming", 7) & self.has("Pine Tar"),
 | 
						|
            "Magic Rock Candy": self.can_reach_region(SVRegion.desert) & self.has("Prismatic Shard"),
 | 
						|
            "Magma Geode": self.can_mine_in_the_mines_floor_81_120() |
 | 
						|
                           (self.has("Lava Eel") & self.has_building("Fish Pond")),
 | 
						|
            "Maki Roll": self.can_cook() & self.can_fish(),
 | 
						|
            "Maple Bar": self.can_cook() & self.has_season("Summer") & self.has_year_two() & self.has("Maple Syrup") &
 | 
						|
                         self.has("Sugar") & self.has("Wheat Flour"),
 | 
						|
            "Maple Syrup": self.has("Tapper"),
 | 
						|
            "Mayonnaise": self.has("Mayonnaise Machine") & self.has("Chicken Egg"),
 | 
						|
            "Mayonnaise Machine": self.has_skill_level("Farming", 2) & self.has("Wood") & self.has("Stone") &
 | 
						|
                                  self.has("Earth Crystal") & self.has("Copper Bar"),
 | 
						|
            "Mead": self.has("Keg") & self.has("Honey"),
 | 
						|
            "Milk": self.has("Cow"),
 | 
						|
            "Miner's Treat": self.can_cook() & self.has_skill_level("Mining", 3) & self.has("Cow Milk") & self.has(
 | 
						|
                "Cave Carrot"),
 | 
						|
            "Morel": self.can_reach_region(SVRegion.secret_woods),
 | 
						|
            "Mussel": True_(),
 | 
						|
            "Nautilus Shell": self.has_season("Winter"),
 | 
						|
            "Oak Resin": self.has("Tapper"),
 | 
						|
            "Oil": True_(),
 | 
						|
            "Oil Maker": self.has_skill_level("Farming", 8) & self.has("Hardwood") & self.has("Gold Bar"),
 | 
						|
            "Omelet": self.can_cook() & self.can_spend_money(100) & self.has("Any Egg") & self.has("Cow Milk"),
 | 
						|
            "Omni Geode": self.can_mine_in_the_mines_floor_41_80() |
 | 
						|
                          self.can_reach_region(SVRegion.desert) |
 | 
						|
                          self.can_do_panning() |
 | 
						|
                          self.received("Rusty Key") |
 | 
						|
                          (self.has("Octopus") & self.has_building("Fish Pond")) |
 | 
						|
                          self.can_reach_region(SVRegion.ginger_island),
 | 
						|
            "Ostrich": self.has_building("Barn") & self.has("Ostrich Egg"),
 | 
						|
            "Ostrich Egg": self.can_reach_region(SVRegion.ginger_island),
 | 
						|
            "Oyster": True_(),
 | 
						|
            "Pale Ale": self.has("Keg") & self.has("Hops"),
 | 
						|
            "Pale Broth": self.can_cook() & self.has_relationship("Marnie", 3) & self.has("White Algae"),
 | 
						|
            "Pancakes": self.can_cook() & self.can_spend_money(100) & self.has("Any Egg"),
 | 
						|
            "Parsnip Soup": self.can_cook() & self.has_relationship("Caroline", 3) & self.has(
 | 
						|
                "Parsnip") & self.has("Cow Milk"),
 | 
						|
            "Pearl": (self.has("Blobfish") & self.has_building("Fish Pond")) |
 | 
						|
                     (self.has_lived_months(4) & self.has("Artifact Trove")),
 | 
						|
            "Pepper Poppers": self.can_cook() & self.has("Cheese") & self.has(
 | 
						|
                "Hot Pepper") & self.has_relationship("Shane", 3),
 | 
						|
            "Periwinkle": self.can_crab_pot(),
 | 
						|
            "Pickles": self.has("Preserves Jar"),
 | 
						|
            "Pig": self.has_building("Deluxe Barn"),
 | 
						|
            "Piña Colada": False_(),  # self.can_reach_region(SVRegion.ginger_island),
 | 
						|
            "Pine Tar": self.has("Tapper"),
 | 
						|
            "Pink Cake": self.can_cook() & self.has_season("Summer") & self.has("Melon") & self.has(
 | 
						|
                "Wheat Flour") & self.has("Sugar") & self.has("Any Egg"),
 | 
						|
            "Pizza": self.can_spend_money(600),
 | 
						|
            "Plum Pudding": self.can_cook() & self.has_season("Winter") & self.has("Wild Plum") &
 | 
						|
                            self.has("Wheat Flour") & self.has("Sugar"),
 | 
						|
            "Poppyseed Muffin": self.can_cook() & self.has_season("Winter") & self.has_year_two() &
 | 
						|
                                self.has("Poppy") & self.has("Wheat Flour") & self.has("Sugar"),
 | 
						|
            "Preserves Jar": self.has_skill_level("Farming", 4),
 | 
						|
            "Pumpkin Pie": self.can_cook() & self.has_season("Winter") & self.has("Wheat Flour") &
 | 
						|
                           self.has("Cow Milk") & self.has("Sugar"),
 | 
						|
            "Purple Mushroom": self.can_mine_in_the_mines_floor_81_120() | self.can_mine_in_the_skull_cavern(),
 | 
						|
            "Rabbit": self.has_building("Deluxe Coop"),
 | 
						|
            "Rabbit's Foot": self.has("Rabbit"),
 | 
						|
            "Radioactive Bar": self.can_smelt("Radioactive Ore"),
 | 
						|
            "Radioactive Ore": self.can_mine_perfectly_in_the_skull_cavern() & self.can_reach_region(SVRegion.ginger_island),
 | 
						|
            "Rainbow Shell": self.has_season("Summer"),
 | 
						|
            "Rain Totem": self.has_skill_level("Foraging", 9),
 | 
						|
            "Recycling Machine": self.has_skill_level("Fishing", 4) & self.has("Wood") &
 | 
						|
                                 self.has("Stone") & self.has("Iron Bar"),
 | 
						|
            "Red Mushroom": self.can_reach_region(SVRegion.secret_woods) & (
 | 
						|
                    self.has_season("Summer") | self.has_season("Fall")),
 | 
						|
            "Refined Quartz": self.can_smelt("Quartz") | self.can_smelt("Fire Quartz") |
 | 
						|
                              (self.has("Recycling Machine") & (self.has("Broken CD") | self.has("Broken Glasses"))),
 | 
						|
            "Rhubarb Pie": self.can_cook() & self.has_relationship("Marnie", 7) & self.has("Rhubarb") &
 | 
						|
                           self.has("Wheat Flour") & self.has("Sugar"),
 | 
						|
            "Rice": True_(),
 | 
						|
            "Rice Pudding": self.can_cook() & self.has_relationship("Evelyn", 7) & self.has("Cow Milk") &
 | 
						|
                            self.has("Sugar") & self.has("Rice"),
 | 
						|
            "Roe": self.can_fish() & self.has_building("Fish Pond"),
 | 
						|
            "Roots Platter": self.can_cook() & self.has_skill_level("Combat", 3) &
 | 
						|
                             self.has("Cave Carrot") & self.has("Winter Root"),
 | 
						|
            "Roasted Hazelnuts": self.can_cook() & self.has_season("Summer") & self.has("Hazelnut"),
 | 
						|
            "Salad": self.can_spend_money(220),
 | 
						|
            # | (self.can_cook() & self.has_relationship("Emily", 3) & self.has("Leek") & self.has("Dandelion") & self.has("Vinegar")),
 | 
						|
            "Salmonberry": self.has_season("Spring"),
 | 
						|
            "Salmon Dinner": self.can_cook() & self.has_relationship("Gus", 3) & self.has("Salmon") & self.has(
 | 
						|
                "Amaranth") & self.has("Kale"),
 | 
						|
            "Sashimi": self.can_fish() & self.can_cook() & self.has_relationship("Linus", 3),
 | 
						|
            "Sea Urchin": self.can_reach_region(SVRegion.tide_pools) | self.has_season("Summer"),
 | 
						|
            "Seaweed": self.can_fish() | self.can_reach_region(SVRegion.tide_pools),
 | 
						|
            "Secret Note": self.received("Magnifying Glass"),
 | 
						|
            "Sheep": self.has_building("Deluxe Barn"),
 | 
						|
            "Shrimp": self.can_crab_pot(),
 | 
						|
            "Slime": self.can_mine_in_the_mines_floor_1_40(),
 | 
						|
            "Snail": self.can_crab_pot(),
 | 
						|
            "Snow Yam": self.has_season("Winter"),
 | 
						|
            "Soggy Newspaper": self.can_crab_pot(),
 | 
						|
            "Solar Essence": self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern(),
 | 
						|
            "Spaghetti": self.can_spend_money(240),
 | 
						|
            "Spice Berry": self.has_season("Summer"),
 | 
						|
            "Spring Onion": self.has_season("Spring"),
 | 
						|
            "Squid Ink": self.can_mine_in_the_mines_floor_81_120() | (
 | 
						|
                    self.has_building("Fish Pond") & self.has("Squid")),
 | 
						|
            "Staircase": self.has_skill_level("Mining", 2),
 | 
						|
            "Stir Fry": self.can_cook() & self.has_season("Spring") & self.has("Cave Carrot") &
 | 
						|
                        self.has("Common Mushroom") & self.has("Kale") & self.has("Oil"),
 | 
						|
            "Stone": self.has_tool("Pickaxe"),
 | 
						|
            "Stuffing": self.has_season("Winter") |
 | 
						|
                        (self.can_cook() & self.has_relationship("Pam", 7) & self.has("Bread") &
 | 
						|
                         self.has("Cranberries") & self.has("Hazelnut")),
 | 
						|
            "Sturgeon Roe": self.has("Sturgeon") & self.has_building("Fish Pond"),
 | 
						|
            "Sugar": True_(),
 | 
						|
            "Survival Burger": self.can_cook() & self.has_skill_level("Foraging", 2) &
 | 
						|
                               self.has(["Bread", "Cave Carrot", "Eggplant"]),
 | 
						|
            "Sweet Pea": self.has_season("Summer"),
 | 
						|
            "Tapper": self.has_skill_level("Foraging", 3),
 | 
						|
            "Tea Bush": self.has_relationship("Caroline", 2),
 | 
						|
            "Tea Leaves": self.has_lived_months(1) & self.has("Tea Bush"),
 | 
						|
            "Tortilla": self.can_cook() & self.can_spend_money(100) & self.has("Corn"),
 | 
						|
            "Trash": self.can_crab_pot(),
 | 
						|
            "Triple Shot Espresso": (self.has("Hot Java Ring") |
 | 
						|
                                     (self.can_cook() & self.can_spend_money(5000) & self.has("Coffee"))),
 | 
						|
            "Tropical Curry": False_(),
 | 
						|
            # self.can_cook() & self.can_reach_region(SVRegion.ginger_island) & self.has("Coconut") & self.has("Pineapple") & self.has("Hot Pepper"),
 | 
						|
            "Truffle Oil": self.has("Truffle") & self.has("Oil Maker"),
 | 
						|
            "Truffle": self.has("Pig") & self.has_spring_summer_or_fall(),
 | 
						|
            "Vegetable Medley": self.can_cook() & self.has_relationship("Caroline", 7) & self.has("Tomato") & self.has(
 | 
						|
                "Beet"),
 | 
						|
            "Vinegar": True_(),
 | 
						|
            "Void Egg": self.can_meet("Krobus") | (self.has_building("Fish Pond") & self.has("Void Salmon")),
 | 
						|
            "Void Essence": self.can_mine_in_the_mines_floor_81_120() | self.can_mine_in_the_skull_cavern(),
 | 
						|
            "Void Mayonnaise": self.has("Mayonnaise Machine") & self.has("Void Egg"),
 | 
						|
            "Wheat Flour": True_(),
 | 
						|
            "White Algae": self.can_fish() & self.can_mine_in_the_mines_floor_1_40(),
 | 
						|
            "Wild Horseradish": self.has_season("Spring"),
 | 
						|
            "Wild Plum": self.has_season("Fall"),
 | 
						|
            "Wilted Bouquet": self.has("Furnace") & self.has("Bouquet") & self.has("Coal"),
 | 
						|
            "Wine": self.has("Keg"),
 | 
						|
            "Winter Root": self.has_season("Winter"),
 | 
						|
            "Wood": self.has_tool("Axe"),
 | 
						|
            "Wool": self.has("Rabbit") | self.has("Sheep"),
 | 
						|
            "Hay": self.has_building("Silo"),
 | 
						|
        })
 | 
						|
        self.item_rules.update(self.fish_rules)
 | 
						|
        self.item_rules.update(self.museum_rules)
 | 
						|
        self.item_rules.update(self.tree_fruit_rules)
 | 
						|
        self.item_rules.update(self.seed_rules)
 | 
						|
        self.item_rules.update(self.crop_rules)
 | 
						|
 | 
						|
        self.building_rules.update({
 | 
						|
            "Barn": self.can_spend_money(6000) & self.has(["Wood", "Stone"]),
 | 
						|
            "Big Barn": self.can_spend_money(12000) & self.has(["Wood", "Stone"]) & self.has_building("Barn"),
 | 
						|
            "Deluxe Barn": self.can_spend_money(25000) & self.has(["Wood", "Stone"]) & self.has_building("Big Barn"),
 | 
						|
            "Coop": self.can_spend_money(4000) & self.has(["Wood", "Stone"]),
 | 
						|
            "Big Coop": self.can_spend_money(10000) & self.has(["Wood", "Stone"]) & self.has_building("Coop"),
 | 
						|
            "Deluxe Coop": self.can_spend_money(20000) & self.has(["Wood", "Stone"]) & self.has_building("Big Coop"),
 | 
						|
            "Fish Pond": self.can_spend_money(5000) & self.has(["Stone", "Seaweed", "Green Algae"]),
 | 
						|
            "Mill": self.can_spend_money(2500) & self.has(["Stone", "Wood", "Cloth"]),
 | 
						|
            "Shed": self.can_spend_money(15000) & self.has("Wood"),
 | 
						|
            "Big Shed": self.can_spend_money(20000) & self.has(["Wood", "Stone"]) & self.has_building("Shed"),
 | 
						|
            "Silo": self.can_spend_money(100) & self.has(["Stone", "Clay", "Copper Bar"]),
 | 
						|
            "Slime Hutch": self.can_spend_money(10000) & self.has(["Stone", "Refined Quartz", "Iridium Bar"]),
 | 
						|
            "Stable": self.can_spend_money(10000) & self.has(["Hardwood", "Iron Bar"]),
 | 
						|
            "Well": self.can_spend_money(1000) & self.has("Stone"),
 | 
						|
            "Shipping Bin": self.can_spend_money(250) & self.has("Wood"),
 | 
						|
            "Kitchen": self.can_spend_money(10000) & self.has("Wood") & self.has_house(0),
 | 
						|
            "Kids Room": self.can_spend_money(50000) & self.has("Hardwood") & self.has_house(1),
 | 
						|
            "Cellar": self.can_spend_money(100000) & self.has_house(2),
 | 
						|
        })
 | 
						|
 | 
						|
        self.quest_rules.update({
 | 
						|
            "Introductions": True_(),
 | 
						|
            "How To Win Friends": self.can_complete_quest("Introductions"),
 | 
						|
            "Getting Started": self.has("Parsnip") & self.has_tool("Hoe") & self.has_tool("Watering Can"),
 | 
						|
            "To The Beach": True_(),
 | 
						|
            "Raising Animals": self.can_complete_quest("Getting Started") & self.has_building("Coop"),
 | 
						|
            "Advancement": self.can_complete_quest("Getting Started") & self.has_skill_level("Farming", 1),
 | 
						|
            "Archaeology": self.has_tool("Hoe") | self.can_mine_in_the_mines_floor_1_40() | self.can_fish(),
 | 
						|
            "Meet The Wizard": True_() & self.can_reach_region(SVRegion.community_center),
 | 
						|
            "Forging Ahead": self.has("Copper Ore") & self.has("Furnace"),
 | 
						|
            "Smelting": self.has("Copper Bar"),
 | 
						|
            "Initiation": self.can_mine_in_the_mines_floor_1_40(),
 | 
						|
            "Robin's Lost Axe": self.has_season("Spring"),
 | 
						|
            "Jodi's Request": self.has_season("Spring") & self.has("Cauliflower"),
 | 
						|
            "Mayor's \"Shorts\"": self.has_season("Summer") & self.has_relationship("Marnie", 4),
 | 
						|
            "Blackberry Basket": self.has_season("Fall"),
 | 
						|
            "Marnie's Request": self.has_relationship("Marnie", 3) & self.has("Cave Carrot"),
 | 
						|
            "Pam Is Thirsty": self.has_season("Summer") & self.has("Pale Ale"),
 | 
						|
            "A Dark Reagent": self.has_season("Winter") & self.has("Void Essence"),
 | 
						|
            "Cow's Delight": self.has_season("Fall") & self.has("Amaranth"),
 | 
						|
            "The Skull Key": self.received("Skull Key") & self.can_reach_region(SVRegion.desert),
 | 
						|
            "Crop Research": self.has_season("Summer") & self.has("Melon"),
 | 
						|
            "Knee Therapy": self.has_season("Summer") & self.has("Hot Pepper"),
 | 
						|
            "Robin's Request": self.has_season("Winter") & self.has("Hardwood"),
 | 
						|
            "Qi's Challenge": self.can_mine_in_the_skull_cavern(),
 | 
						|
            "The Mysterious Qi": self.has("Battery Pack") & self.can_reach_region(SVRegion.desert) & self.has(
 | 
						|
                "Rainbow Shell") & self.has("Beet") & self.has("Solar Essence"),
 | 
						|
            "Carving Pumpkins": self.has_season("Fall") & self.has("Pumpkin"),
 | 
						|
            "A Winter Mystery": self.has_season("Winter"),
 | 
						|
            "Strange Note": self.received("Magnifying Glass") & self.can_reach_region(SVRegion.secret_woods) & self.has(
 | 
						|
                "Maple Syrup"),
 | 
						|
            "Cryptic Note": self.received("Magnifying Glass") & self.can_reach_region(SVRegion.perfect_skull_cavern),
 | 
						|
            "Fresh Fruit": self.has("Apricot"),
 | 
						|
            "Aquatic Research": self.has("Pufferfish"),
 | 
						|
            "A Soldier's Star": self.has_relationship("Kent") & self.has("Starfruit"),
 | 
						|
            "Mayor's Need": self.has("Truffle Oil"),
 | 
						|
            "Wanted: Lobster": self.has("Lobster"),
 | 
						|
            "Pam Needs Juice": self.has("Battery Pack"),
 | 
						|
            "Fish Casserole": self.has_relationship("Jodi", 4) & self.has("Largemouth Bass"),
 | 
						|
            "Catch A Squid": self.has("Squid"),
 | 
						|
            "Fish Stew": self.has("Albacore"),
 | 
						|
            "Pierre's Notice": self.has("Sashimi"),
 | 
						|
            "Clint's Attempt": self.has("Amethyst"),
 | 
						|
            "A Favor For Clint": self.has("Iron Bar"),
 | 
						|
            "Staff Of Power": self.has("Iridium Bar"),
 | 
						|
            "Granny's Gift": self.has("Leek"),
 | 
						|
            "Exotic Spirits": self.has("Coconut"),
 | 
						|
            "Catch a Lingcod": self.has("Lingcod"),
 | 
						|
        })
 | 
						|
 | 
						|
    def has(self, items: Union[str, (Iterable[str], Sized)], count: Optional[int] = None) -> StardewRule:
 | 
						|
        if isinstance(items, str):
 | 
						|
            return Has(items, self.item_rules)
 | 
						|
 | 
						|
        if len(items) == 0:
 | 
						|
            return True_()
 | 
						|
 | 
						|
        if count is None or count == len(items):
 | 
						|
            return And(self.has(item) for item in items)
 | 
						|
 | 
						|
        if count == 1:
 | 
						|
            return Or(self.has(item) for item in items)
 | 
						|
 | 
						|
        return Count(count, (self.has(item) for item in items))
 | 
						|
 | 
						|
    def received(self, items: Union[str, Iterable[str]], count: Optional[int] = 1) -> StardewRule:
 | 
						|
        if count <= 0 or not items:
 | 
						|
            return True_()
 | 
						|
 | 
						|
        if isinstance(items, str):
 | 
						|
            return Received(items, self.player, count)
 | 
						|
 | 
						|
        if count is None:
 | 
						|
            return And(self.received(item) for item in items)
 | 
						|
 | 
						|
        if count == 1:
 | 
						|
            return Or(self.received(item) for item in items)
 | 
						|
 | 
						|
        return TotalReceived(count, items, self.player)
 | 
						|
 | 
						|
    def can_reach_region(self, spot: str) -> StardewRule:
 | 
						|
        return Reach(spot, "Region", self.player)
 | 
						|
 | 
						|
    def can_reach_any_region(self, spots: Iterable[str]) -> StardewRule:
 | 
						|
        return Or(self.can_reach_region(spot) for spot in spots)
 | 
						|
 | 
						|
    def can_reach_all_regions(self, spots: Iterable[str]) -> StardewRule:
 | 
						|
        return And(self.can_reach_region(spot) for spot in spots)
 | 
						|
 | 
						|
    def can_reach_location(self, spot: str) -> StardewRule:
 | 
						|
        return Reach(spot, "Location", self.player)
 | 
						|
 | 
						|
    def can_reach_entrance(self, spot: str) -> StardewRule:
 | 
						|
        return Reach(spot, "Entrance", self.player)
 | 
						|
 | 
						|
    def can_have_earned_total_money(self, amount: int) -> StardewRule:
 | 
						|
        return self.has_lived_months(min(8, amount // MONEY_PER_MONTH))
 | 
						|
 | 
						|
    def can_spend_money(self, amount: int) -> StardewRule:
 | 
						|
        return self.has_lived_months(min(8, amount // (MONEY_PER_MONTH // 5)))
 | 
						|
 | 
						|
    def has_tool(self, tool: str, material: str = "Basic") -> StardewRule:
 | 
						|
        if material == "Basic":
 | 
						|
            return True_()
 | 
						|
 | 
						|
        if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
 | 
						|
            return self.received(f"Progressive {tool}", count=tool_materials[material])
 | 
						|
 | 
						|
        return self.has(f"{material} Bar") & self.can_spend_money(tool_prices[material])
 | 
						|
 | 
						|
    def has_skill_level(self, skill: str, level: int) -> StardewRule:
 | 
						|
        if level == 0:
 | 
						|
            return True_()
 | 
						|
 | 
						|
        if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
 | 
						|
            return self.received(f"{skill} Level", count=level)
 | 
						|
 | 
						|
        if skill == "Fishing" and self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
 | 
						|
            return self.can_get_fishing_xp()
 | 
						|
 | 
						|
        return self.has_lived_months(month_end_per_skill_level[(skill, level)])
 | 
						|
 | 
						|
    def has_total_skill_level(self, level: int) -> StardewRule:
 | 
						|
        if level == 0:
 | 
						|
            return True_()
 | 
						|
 | 
						|
        if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
 | 
						|
            skills_items = ["Farming Level", "Mining Level", "Foraging Level",
 | 
						|
                            "Fishing Level", "Combat Level"]
 | 
						|
            return self.received(skills_items, count=level)
 | 
						|
 | 
						|
        if level > 40 and self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
 | 
						|
            return self.has_lived_months(month_end_per_total_level[level]) & self.can_get_fishing_xp()
 | 
						|
 | 
						|
        return self.has_lived_months(month_end_per_total_level[level])
 | 
						|
 | 
						|
    def has_building(self, building: str) -> StardewRule:
 | 
						|
        if not self.options[options.BuildingProgression] == options.BuildingProgression.option_vanilla:
 | 
						|
            count = 1
 | 
						|
            if building in ["Coop", "Barn", "Shed"]:
 | 
						|
                building = f"Progressive {building}"
 | 
						|
            elif building.startswith("Big"):
 | 
						|
                count = 2
 | 
						|
                building = " ".join(["Progressive", *building.split(" ")[1:]])
 | 
						|
            elif building.startswith("Deluxe"):
 | 
						|
                count = 3
 | 
						|
                building = " ".join(["Progressive", *building.split(" ")[1:]])
 | 
						|
            return self.received(f"{building}", count)
 | 
						|
 | 
						|
        return Has(building, self.building_rules)
 | 
						|
 | 
						|
    def has_house(self, upgrade_level: int) -> StardewRule:
 | 
						|
        if upgrade_level < 1:
 | 
						|
            return True_()
 | 
						|
 | 
						|
        if upgrade_level > 3:
 | 
						|
            return False_()
 | 
						|
 | 
						|
        if not self.options[options.BuildingProgression] == options.BuildingProgression.option_vanilla:
 | 
						|
            return self.received(f"Progressive House", upgrade_level)
 | 
						|
 | 
						|
        if upgrade_level == 1:
 | 
						|
            return Has("Kitchen", self.building_rules)
 | 
						|
 | 
						|
        if upgrade_level == 2:
 | 
						|
            return Has("Kids Room", self.building_rules)
 | 
						|
 | 
						|
        # if upgrade_level == 3:
 | 
						|
        return Has("Cellar", self.building_rules)
 | 
						|
 | 
						|
    def can_complete_quest(self, quest: str) -> StardewRule:
 | 
						|
        return Has(quest, self.quest_rules)
 | 
						|
 | 
						|
    def can_get_fishing_xp(self) -> StardewRule:
 | 
						|
        if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
 | 
						|
            return self.can_fish() | self.can_crab_pot()
 | 
						|
 | 
						|
        return self.can_fish()
 | 
						|
 | 
						|
    def has_max_fishing_rod(self) -> StardewRule:
 | 
						|
        if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
 | 
						|
            return self.received("Progressive Fishing Rod", 4)
 | 
						|
        return self.can_get_fishing_xp()
 | 
						|
 | 
						|
    def can_fish(self, difficulty: int = 0) -> StardewRule:
 | 
						|
        skill_required = max(0, int((difficulty / 10) - 1))
 | 
						|
        if difficulty <= 40:
 | 
						|
            skill_required = 0
 | 
						|
        skill_rule = self.has_skill_level("Fishing", skill_required)
 | 
						|
        number_fishing_rod_required = 1 if difficulty < 50 else 2
 | 
						|
        if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
 | 
						|
            return self.received("Progressive Fishing Rod", number_fishing_rod_required) & skill_rule
 | 
						|
 | 
						|
        return skill_rule
 | 
						|
 | 
						|
    def has_max_fishing(self) -> StardewRule:
 | 
						|
        skill_rule = self.has_skill_level("Fishing", 10)
 | 
						|
        return self.has_max_fishing_rod() & skill_rule
 | 
						|
 | 
						|
    def can_fish_chests(self) -> StardewRule:
 | 
						|
        skill_rule = self.has_skill_level("Fishing", 4)
 | 
						|
        return self.has_max_fishing_rod() & skill_rule
 | 
						|
 | 
						|
    def can_buy_seed(self, seed: SeedItem):
 | 
						|
        if self.options[options.SeedShuffle] == options.SeedShuffle.option_disabled or seed.name == "Rare Seed":
 | 
						|
            item_rule = True_()
 | 
						|
        else:
 | 
						|
            item_rule = self.received(seed.name)
 | 
						|
        season_rule = self.has_any_season(seed.seasons)
 | 
						|
        region_rule = self.can_reach_any_region(seed.regions)
 | 
						|
        return season_rule & region_rule & item_rule
 | 
						|
 | 
						|
    def can_grow_crop(self, crop: CropItem):
 | 
						|
        season_rule = self.has_any_season(crop.farm_growth_seasons)
 | 
						|
        seed_rule = self.has(crop.seed.name)
 | 
						|
        farm_rule = self.can_reach_region(SVRegion.farm) & season_rule
 | 
						|
        region_rule = farm_rule | self.can_reach_region(SVRegion.greenhouse)
 | 
						|
        return seed_rule & region_rule
 | 
						|
 | 
						|
    def can_catch_fish(self, fish: FishItem) -> StardewRule:
 | 
						|
        region_rule = self.can_reach_any_region(fish.locations)
 | 
						|
        season_rule = self.has_any_season(fish.seasons)
 | 
						|
        difficulty_rule = self.can_fish(fish.difficulty)
 | 
						|
        if fish.difficulty == -1:
 | 
						|
            difficulty_rule = self.can_crab_pot()
 | 
						|
        return region_rule & season_rule & difficulty_rule
 | 
						|
 | 
						|
    def can_catch_every_fish(self) -> StardewRule:
 | 
						|
        rules = [self.has_skill_level("Fishing", 10), self.has_max_fishing_rod()]
 | 
						|
        for fish in all_fish:
 | 
						|
            rules.append(self.can_catch_fish(fish))
 | 
						|
        return And(rules)
 | 
						|
 | 
						|
    def has_max_fishing_rod(self) -> StardewRule:
 | 
						|
        if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
 | 
						|
            return self.received("Progressive Fishing Rod", 4)
 | 
						|
        return self.can_get_fishing_xp()
 | 
						|
 | 
						|
    def can_cook(self) -> StardewRule:
 | 
						|
        return self.has_house(1) or self.has_skill_level("Foraging", 9)
 | 
						|
 | 
						|
    def can_smelt(self, item: str) -> StardewRule:
 | 
						|
        return self.has("Furnace") & self.has(item)
 | 
						|
 | 
						|
    def can_crab_pot(self) -> StardewRule:
 | 
						|
        if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
 | 
						|
            return self.has("Crab Pot")
 | 
						|
 | 
						|
        return True_()
 | 
						|
 | 
						|
    def can_do_panning(self) -> StardewRule:
 | 
						|
        return self.received("Glittering Boulder Removed")
 | 
						|
 | 
						|
    # Regions
 | 
						|
    def can_mine_in_the_mines_floor_1_40(self) -> StardewRule:
 | 
						|
        return self.can_reach_region(SVRegion.mines_floor_5)
 | 
						|
 | 
						|
    def can_mine_in_the_mines_floor_41_80(self) -> StardewRule:
 | 
						|
        return self.can_reach_region(SVRegion.mines_floor_45)
 | 
						|
 | 
						|
    def can_mine_in_the_mines_floor_81_120(self) -> StardewRule:
 | 
						|
        return self.can_reach_region(SVRegion.mines_floor_85)
 | 
						|
 | 
						|
    def can_mine_in_the_skull_cavern(self) -> StardewRule:
 | 
						|
        return (self.can_progress_in_the_mines_from_floor(120) &
 | 
						|
                self.can_reach_region(SVRegion.skull_cavern))
 | 
						|
 | 
						|
    def can_mine_perfectly_in_the_skull_cavern(self) -> StardewRule:
 | 
						|
        return (self.can_progress_in_the_mines_from_floor(160) &
 | 
						|
                self.can_reach_region(SVRegion.skull_cavern))
 | 
						|
 | 
						|
    def get_weapon_rule_for_floor_tier(self, tier: int):
 | 
						|
        if tier >= 4:
 | 
						|
            return self.has_galaxy_weapon()
 | 
						|
        if tier >= 3:
 | 
						|
            return self.has_great_weapon()
 | 
						|
        if tier >= 2:
 | 
						|
            return self.has_good_weapon()
 | 
						|
        if tier >= 1:
 | 
						|
            return self.has_decent_weapon()
 | 
						|
        return self.has_any_weapon()
 | 
						|
 | 
						|
    def can_progress_in_the_mines_from_floor(self, floor: int) -> StardewRule:
 | 
						|
        tier = int(floor / 40)
 | 
						|
        rules = []
 | 
						|
        weapon_rule = self.get_weapon_rule_for_floor_tier(tier)
 | 
						|
        rules.append(weapon_rule)
 | 
						|
        if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
 | 
						|
            rules.append(self.received("Progressive Pickaxe", tier))
 | 
						|
        if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
 | 
						|
            combat_tier = min(10, max(0, tier * 2))
 | 
						|
            rules.append(self.has_skill_level("Combat", combat_tier))
 | 
						|
        return And(rules)
 | 
						|
 | 
						|
    def can_progress_easily_in_the_mines_from_floor(self, floor: int) -> StardewRule:
 | 
						|
        tier = int(floor / 40) + 1
 | 
						|
        rules = []
 | 
						|
        weapon_rule = self.get_weapon_rule_for_floor_tier(tier)
 | 
						|
        rules.append(weapon_rule)
 | 
						|
        if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
 | 
						|
            rules.append(self.received("Progressive Pickaxe", count=tier))
 | 
						|
        if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
 | 
						|
            combat_tier = min(10, max(0, tier * 2))
 | 
						|
            rules.append(self.has_skill_level("Combat", combat_tier))
 | 
						|
        return And(rules)
 | 
						|
 | 
						|
    def has_mine_elevator_to_floor(self, floor: int) -> StardewRule:
 | 
						|
        if (self.options[options.TheMinesElevatorsProgression] ==
 | 
						|
                options.TheMinesElevatorsProgression.option_progressive or
 | 
						|
                self.options[options.TheMinesElevatorsProgression] ==
 | 
						|
                options.TheMinesElevatorsProgression.option_progressive_from_previous_floor):
 | 
						|
            return self.received("Progressive Mine Elevator", count=int(floor / 5))
 | 
						|
        return True_()
 | 
						|
 | 
						|
    def can_mine_to_floor(self, floor: int) -> StardewRule:
 | 
						|
        previous_elevator = max(floor - 5, 0)
 | 
						|
        previous_previous_elevator = max(floor - 10, 0)
 | 
						|
        return ((self.has_mine_elevator_to_floor(previous_elevator) &
 | 
						|
                 self.can_progress_in_the_mines_from_floor(previous_elevator)) |
 | 
						|
                (self.has_mine_elevator_to_floor(previous_previous_elevator) &
 | 
						|
                 self.can_progress_easily_in_the_mines_from_floor(previous_previous_elevator)))
 | 
						|
 | 
						|
    def has_jotpk_power_level(self, power_level: int) -> StardewRule:
 | 
						|
        if self.options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_full_shuffling:
 | 
						|
            return True_()
 | 
						|
        jotpk_buffs = ["JotPK: Progressive Boots", "JotPK: Progressive Gun",
 | 
						|
                       "JotPK: Progressive Ammo", "JotPK: Extra Life", "JotPK: Increased Drop Rate"]
 | 
						|
        return self.received(jotpk_buffs, power_level)
 | 
						|
 | 
						|
    def has_junimo_kart_power_level(self, power_level: int) -> StardewRule:
 | 
						|
        if self.options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_full_shuffling:
 | 
						|
            return True_()
 | 
						|
        return self.received("Junimo Kart: Extra Life", power_level)
 | 
						|
 | 
						|
    def has_traveling_merchant(self, tier: int = 1):
 | 
						|
        traveling_merchant_days = [f"Traveling Merchant: {day}" for day in week_days]
 | 
						|
        return self.received(traveling_merchant_days, tier)
 | 
						|
 | 
						|
    def can_get_married(self) -> StardewRule:
 | 
						|
        return self.can_reach_region(SVRegion.tide_pools) & self.has_relationship("Bachelor", 10) & self.has_house(1)
 | 
						|
 | 
						|
    def can_have_two_children(self) -> StardewRule:
 | 
						|
        return self.can_get_married() & self.has_house(2) & self.has_relationship("Bachelor", 12)
 | 
						|
 | 
						|
    def has_relationship(self, npc: str, hearts: int = 1) -> StardewRule:
 | 
						|
        if hearts <= 0:
 | 
						|
            return True_()
 | 
						|
        if self.options[options.Friendsanity] == options.Friendsanity.option_none:
 | 
						|
            return self.can_earn_relationship(npc, hearts)
 | 
						|
        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":
 | 
						|
                possible_friends = []
 | 
						|
                for name in all_villagers_by_name:
 | 
						|
                    if npc == "Any" or all_villagers_by_name[name].bachelor:
 | 
						|
                        possible_friends.append(self.has_relationship(name, hearts))
 | 
						|
                return Or(possible_friends)
 | 
						|
            if npc == "All":
 | 
						|
                mandatory_friends = []
 | 
						|
                for name in all_villagers_by_name:
 | 
						|
                    mandatory_friends.append(self.has_relationship(name, hearts))
 | 
						|
                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)
 | 
						|
 | 
						|
        villager = all_villagers_by_name[npc]
 | 
						|
        if self.options[options.Friendsanity] == options.Friendsanity.option_bachelors and not villager.bachelor:
 | 
						|
            return self.can_earn_relationship(npc, hearts)
 | 
						|
        if self.options[options.Friendsanity] == options.Friendsanity.option_starting_npcs and not villager.available:
 | 
						|
            return self.can_earn_relationship(npc, hearts)
 | 
						|
        if self.options[
 | 
						|
            options.Friendsanity] != options.Friendsanity.option_all_with_marriage and villager.bachelor and hearts > 8:
 | 
						|
            return self.received(f"{villager.name}: 1 <3", 8) & self.can_earn_relationship(npc, hearts)
 | 
						|
        return self.received(f"{villager.name}: 1 <3", hearts)
 | 
						|
 | 
						|
    def can_meet(self, npc: str) -> StardewRule:
 | 
						|
        if npc not in all_villagers_by_name:
 | 
						|
            return True_()
 | 
						|
        villager = all_villagers_by_name[npc]
 | 
						|
        rules = [self.can_reach_any_region(villager.locations)]
 | 
						|
        if npc == "Kent":
 | 
						|
            rules.append(self.has_lived_months(4))
 | 
						|
        if npc == "Dwarf":
 | 
						|
            rules.append(self.received("Dwarvish Translation Guide"))
 | 
						|
            rules.append(self.has_tool("Pickaxe", "Iron"))
 | 
						|
 | 
						|
        return And(rules)
 | 
						|
 | 
						|
    def can_earn_relationship(self, npc: str, hearts: int = 0) -> StardewRule:
 | 
						|
        if npc == "Pet":
 | 
						|
            return self.can_befriend_pet(hearts)
 | 
						|
        if npc in all_villagers_by_name:
 | 
						|
            villager = all_villagers_by_name[npc]
 | 
						|
            option1 = self.has_season(villager.birthday) & self.has(villager.gifts) & self.has_lived_months(1)
 | 
						|
            option2 = self.has_season(villager.birthday) & self.has(villager.gifts, 1) & self.has_lived_months(
 | 
						|
                hearts // 3)
 | 
						|
            option3 = (self.has_season(villager.birthday) | self.has(villager.gifts, 1)) & self.has_lived_months(
 | 
						|
                hearts // 2)
 | 
						|
            option4 = self.has_lived_months(hearts)
 | 
						|
            return self.can_meet(npc) & (option1 | option2 | option3 | option4)
 | 
						|
        else:
 | 
						|
            return self.has_lived_months(min(hearts // 2, 8))
 | 
						|
 | 
						|
    def can_befriend_pet(self, hearts: int):
 | 
						|
        if hearts == 0:
 | 
						|
            return True_()
 | 
						|
        points = hearts * 200
 | 
						|
        points_per_month = 12 * 14
 | 
						|
        points_per_water_month = 18 * 14
 | 
						|
        return self.can_reach_region(SVRegion.farm) & \
 | 
						|
            ((self.has_tool("Watering Can") & self.has_lived_months(points // points_per_water_month)) |
 | 
						|
             self.has_lived_months(points // points_per_month))
 | 
						|
 | 
						|
    def can_complete_bundle(self, bundle_requirements: List[BundleItem], number_required: int) -> StardewRule:
 | 
						|
        item_rules = []
 | 
						|
        for bundle_item in bundle_requirements:
 | 
						|
            if bundle_item.item.item_id == -1:
 | 
						|
                return self.can_spend_money(bundle_item.amount)
 | 
						|
            else:
 | 
						|
                item_rules.append(bundle_item.item.name)
 | 
						|
        return self.has(item_rules, number_required)
 | 
						|
 | 
						|
    def can_complete_community_center(self) -> StardewRule:
 | 
						|
        return (self.can_reach_location("Complete Crafts Room") &
 | 
						|
                self.can_reach_location("Complete Pantry") &
 | 
						|
                self.can_reach_location("Complete Fish Tank") &
 | 
						|
                self.can_reach_location("Complete Bulletin Board") &
 | 
						|
                self.can_reach_location("Complete Vault") &
 | 
						|
                self.can_reach_location("Complete Boiler Room"))
 | 
						|
 | 
						|
    def can_finish_grandpa_evaluation(self) -> StardewRule:
 | 
						|
        # https://stardewvalleywiki.com/Grandpa
 | 
						|
        rules_worth_a_point = [self.can_have_earned_total_money(50000),  # 50 000g
 | 
						|
                               self.can_have_earned_total_money(100000),  # 100 000g
 | 
						|
                               self.can_have_earned_total_money(200000),  # 200 000g
 | 
						|
                               self.can_have_earned_total_money(300000),  # 300 000g
 | 
						|
                               self.can_have_earned_total_money(500000),  # 500 000g
 | 
						|
                               self.can_have_earned_total_money(1000000),  # 1 000 000g first point
 | 
						|
                               self.can_have_earned_total_money(1000000),  # 1 000 000g second point
 | 
						|
                               self.has_total_skill_level(30),  # Total Skills: 30
 | 
						|
                               self.has_total_skill_level(50),  # Total Skills: 50
 | 
						|
                               # Completing the museum not expected
 | 
						|
                               # Catching every fish not expected
 | 
						|
                               # Shipping every item not expected
 | 
						|
                               self.can_get_married() & self.has_house(2),
 | 
						|
                               self.has_relationship("5", 8),  # 5 Friends
 | 
						|
                               self.has_relationship("10", 8),  # 10 friends
 | 
						|
                               self.has_relationship("Pet", 5),  # Max Pet
 | 
						|
                               self.can_complete_community_center(),  # Community Center Completion
 | 
						|
                               self.can_complete_community_center(),  # CC Ceremony first point
 | 
						|
                               self.can_complete_community_center(),  # CC Ceremony second point
 | 
						|
                               self.received("Skull Key"),  # Skull Key obtained
 | 
						|
                               self.has_rusty_key(),  # Rusty key not expected
 | 
						|
                               ]
 | 
						|
        return Count(12, rules_worth_a_point)
 | 
						|
 | 
						|
    def has_any_weapon(self) -> StardewRule:
 | 
						|
        return self.has_decent_weapon() | self.received(item.name for item in all_items if Group.WEAPON in item.groups)
 | 
						|
 | 
						|
    def has_decent_weapon(self) -> StardewRule:
 | 
						|
        return (self.has_good_weapon() |
 | 
						|
                self.received(item.name for item in all_items
 | 
						|
                              if Group.WEAPON in item.groups and
 | 
						|
                              (Group.MINES_FLOOR_50 in item.groups or Group.MINES_FLOOR_60 in item.groups)))
 | 
						|
 | 
						|
    def has_good_weapon(self) -> StardewRule:
 | 
						|
        return ((self.has_great_weapon() |
 | 
						|
                 self.received(item.name for item in all_items
 | 
						|
                               if Group.WEAPON in item.groups and
 | 
						|
                               (Group.MINES_FLOOR_80 in item.groups or Group.MINES_FLOOR_90 in item.groups))) &
 | 
						|
                self.received("Adventurer's Guild"))
 | 
						|
 | 
						|
    def has_great_weapon(self) -> StardewRule:
 | 
						|
        return ((self.has_galaxy_weapon() |
 | 
						|
                 self.received(item.name for item in all_items
 | 
						|
                               if Group.WEAPON in item.groups and Group.MINES_FLOOR_110 in item.groups)) &
 | 
						|
                self.received("Adventurer's Guild"))
 | 
						|
 | 
						|
    def has_galaxy_weapon(self) -> StardewRule:
 | 
						|
        return (self.received(item.name for item in all_items
 | 
						|
                              if Group.WEAPON in item.groups and Group.GALAXY_WEAPONS in item.groups) &
 | 
						|
                self.received("Adventurer's Guild"))
 | 
						|
 | 
						|
    def has_year_two(self) -> StardewRule:
 | 
						|
        return self.has_lived_months(4)
 | 
						|
 | 
						|
    def has_spring_summer_or_fall(self) -> StardewRule:
 | 
						|
        return self.has_season("Spring") | self.has_season("Summer") | self.has_season("Fall")
 | 
						|
 | 
						|
    def can_find_museum_item(self, item: MuseumItem) -> StardewRule:
 | 
						|
        region_rule = self.can_reach_all_regions(item.locations)
 | 
						|
        geodes_rule = self.has(item.geodes)
 | 
						|
        # monster_rule = self.can_farm_monster(item.monsters)
 | 
						|
        # extra_rule = True_()
 | 
						|
        return region_rule & geodes_rule  # & monster_rule & extra_rule
 | 
						|
 | 
						|
    def can_complete_museum(self) -> StardewRule:
 | 
						|
        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:
 | 
						|
            rules.append(self.can_find_museum_item(donation))
 | 
						|
        return And(rules)
 | 
						|
 | 
						|
    def has_season(self, season: str) -> StardewRule:
 | 
						|
        seasons_order = ["Spring", "Summer", "Fall", "Winter"]
 | 
						|
        if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_progressive:
 | 
						|
            return self.received("Progressive Season", seasons_order.index(season))
 | 
						|
        if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_disabled:
 | 
						|
            if season == "Spring":
 | 
						|
                return True_()
 | 
						|
            return self.has_lived_months(1)
 | 
						|
        return self.received(season)
 | 
						|
 | 
						|
    def has_any_season(self, seasons: Iterable[str]):
 | 
						|
        if not seasons:
 | 
						|
            return True_()
 | 
						|
        return Or([self.has_season(season) for season in seasons])
 | 
						|
 | 
						|
    def has_all_seasons(self, seasons: Iterable[str]):
 | 
						|
        if not seasons:
 | 
						|
            return True_()
 | 
						|
        return And([self.has_season(season) for season in seasons])
 | 
						|
 | 
						|
    def has_lived_months(self, number: int) -> StardewRule:
 | 
						|
        number = max(0, min(number, 8))
 | 
						|
        return self.received("Month End", number)
 | 
						|
 | 
						|
    def has_rusty_key(self) -> StardewRule:
 | 
						|
        if self.options[options.Museumsanity] == options.Museumsanity.option_none:
 | 
						|
            return True_()
 | 
						|
        return self.received("Rusty Key")
 |