Stardew Valley: speed up rules creation by 4% (#2371)

* Stardew Valley: speed up rules creation by 4%

No class should ever inherit from And, Or, False_ or True_ and isinstance is not free.
Sadly there is no cheap way to forbid inheritance, but it was tested using metaclass.

* Stardew Valley: save calls to type()

Local variable is a bit faster than fetching type again

* Stardew Valley: save calls to True_() and False_(), also use 'in' operator

* Stardew Valley: optimize And and Or simplification

* Stardew Valley: optimize logic constructors
This commit is contained in:
black-sliver 2023-10-27 18:09:12 +02:00 committed by GitHub
parent 6a2407468a
commit fc2855ca6d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 61 additions and 50 deletions

View File

@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import Iterable, Dict, List, Union, FrozenSet from typing import Iterable, Dict, List, Union, FrozenSet, Set
from BaseClasses import CollectionState, ItemClassification from BaseClasses import CollectionState, ItemClassification
from .items import item_table from .items import item_table
@ -14,13 +14,13 @@ class StardewRule:
raise NotImplementedError raise NotImplementedError
def __or__(self, other) -> StardewRule: def __or__(self, other) -> StardewRule:
if isinstance(other, Or): if type(other) is Or:
return Or(self, *other.rules) return Or(self, *other.rules)
return Or(self, other) return Or(self, other)
def __and__(self, other) -> StardewRule: def __and__(self, other) -> StardewRule:
if isinstance(other, And): if type(other) is And:
return And(other.rules.union({self})) return And(other.rules.union({self}))
return And(self, other) return And(self, other)
@ -80,28 +80,36 @@ class False_(StardewRule): # noqa
return 999999999 return 999999999
false_ = False_()
true_ = True_()
assert false_ is False_()
assert true_ is True_()
class Or(StardewRule): class Or(StardewRule):
rules: FrozenSet[StardewRule] rules: FrozenSet[StardewRule]
def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule): def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
rules_list = set() rules_list: Set[StardewRule]
if isinstance(rule, Iterable): if isinstance(rule, Iterable):
rules_list.update(rule) rules_list = {*rule}
else: else:
rules_list.add(rule) rules_list = {rule}
if rules is not None: if rules is not None:
rules_list.update(rules) rules_list.update(rules)
assert rules_list, "Can't create a Or conditions without rules" assert rules_list, "Can't create a Or conditions without rules"
new_rules = set() if any(type(rule) is Or for rule in rules_list):
for rule in rules_list: new_rules: Set[StardewRule] = set()
if isinstance(rule, Or): for rule in rules_list:
new_rules.update(rule.rules) if type(rule) is Or:
else: new_rules.update(rule.rules)
new_rules.add(rule) else:
rules_list = new_rules new_rules.add(rule)
rules_list = new_rules
self.rules = frozenset(rules_list) self.rules = frozenset(rules_list)
@ -112,11 +120,11 @@ class Or(StardewRule):
return f"({' | '.join(repr(rule) for rule in self.rules)})" return f"({' | '.join(repr(rule) for rule in self.rules)})"
def __or__(self, other): def __or__(self, other):
if isinstance(other, True_): if other is true_:
return other return other
if isinstance(other, False_): if other is false_:
return self return self
if isinstance(other, Or): if type(other) is Or:
return Or(self.rules.union(other.rules)) return Or(self.rules.union(other.rules))
return Or(self.rules.union({other})) return Or(self.rules.union({other}))
@ -131,17 +139,17 @@ class Or(StardewRule):
return min(rule.get_difficulty() for rule in self.rules) return min(rule.get_difficulty() for rule in self.rules)
def simplify(self) -> StardewRule: def simplify(self) -> StardewRule:
if any(isinstance(rule, True_) for rule in self.rules): if true_ in self.rules:
return True_() return true_
simplified_rules = {rule.simplify() for rule in self.rules} simplified_rules = [simplified for simplified in {rule.simplify() for rule in self.rules}
simplified_rules = {rule for rule in simplified_rules if rule is not False_()} if simplified is not false_]
if not simplified_rules: if not simplified_rules:
return False_() return false_
if len(simplified_rules) == 1: if len(simplified_rules) == 1:
return next(iter(simplified_rules)) return simplified_rules[0]
return Or(simplified_rules) return Or(simplified_rules)
@ -150,25 +158,26 @@ class And(StardewRule):
rules: FrozenSet[StardewRule] rules: FrozenSet[StardewRule]
def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule): def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
rules_list = set() rules_list: Set[StardewRule]
if isinstance(rule, Iterable): if isinstance(rule, Iterable):
rules_list.update(rule) rules_list = {*rule}
else: else:
rules_list.add(rule) rules_list = {rule}
if rules is not None: if rules is not None:
rules_list.update(rules) rules_list.update(rules)
if len(rules_list) < 1: if not rules_list:
rules_list.add(True_()) rules_list.add(true_)
elif any(type(rule) is And for rule in rules_list):
new_rules = set() new_rules: Set[StardewRule] = set()
for rule in rules_list: for rule in rules_list:
if isinstance(rule, And): if type(rule) is And:
new_rules.update(rule.rules) new_rules.update(rule.rules)
else: else:
new_rules.add(rule) new_rules.add(rule)
rules_list = new_rules rules_list = new_rules
self.rules = frozenset(rules_list) self.rules = frozenset(rules_list)
@ -179,11 +188,11 @@ class And(StardewRule):
return f"({' & '.join(repr(rule) for rule in self.rules)})" return f"({' & '.join(repr(rule) for rule in self.rules)})"
def __and__(self, other): def __and__(self, other):
if isinstance(other, True_): if other is true_:
return self return self
if isinstance(other, False_): if other is false_:
return other return other
if isinstance(other, And): if type(other) is And:
return And(self.rules.union(other.rules)) return And(self.rules.union(other.rules))
return And(self.rules.union({other})) return And(self.rules.union({other}))
@ -198,17 +207,17 @@ class And(StardewRule):
return max(rule.get_difficulty() for rule in self.rules) return max(rule.get_difficulty() for rule in self.rules)
def simplify(self) -> StardewRule: def simplify(self) -> StardewRule:
if any(isinstance(rule, False_) for rule in self.rules): if false_ in self.rules:
return False_() return false_
simplified_rules = {rule.simplify() for rule in self.rules} simplified_rules = [simplified for simplified in {rule.simplify() for rule in self.rules}
simplified_rules = {rule for rule in simplified_rules if rule is not True_()} if simplified is not true_]
if not simplified_rules: if not simplified_rules:
return True_() return true_
if len(simplified_rules) == 1: if len(simplified_rules) == 1:
return next(iter(simplified_rules)) return simplified_rules[0]
return And(simplified_rules) return And(simplified_rules)
@ -218,11 +227,12 @@ class Count(StardewRule):
rules: List[StardewRule] rules: List[StardewRule]
def __init__(self, count: int, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule): def __init__(self, count: int, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
rules_list = [] rules_list: List[StardewRule]
if isinstance(rule, Iterable): if isinstance(rule, Iterable):
rules_list.extend(rule) rules_list = [*rule]
else: else:
rules_list.append(rule) rules_list = [rule]
if rules is not None: if rules is not None:
rules_list.extend(rules) rules_list.extend(rules)
@ -260,11 +270,12 @@ class TotalReceived(StardewRule):
player: int player: int
def __init__(self, count: int, items: Union[str, Iterable[str]], player: int): def __init__(self, count: int, items: Union[str, Iterable[str]], player: int):
items_list = [] items_list: List[str]
if isinstance(items, Iterable): if isinstance(items, Iterable):
items_list.extend(items) items_list = [*items]
else: else:
items_list.append(items) items_list = [items]
assert items_list, "Can't create a Total Received conditions without items" assert items_list, "Can't create a Total Received conditions without items"
for item in items_list: for item in items_list: