50 lines
1.8 KiB
Python
50 lines
1.8 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import TYPE_CHECKING, Callable, Hashable, TypeVar
|
|
|
|
if TYPE_CHECKING:
|
|
from .bot_ai import BotAI
|
|
|
|
T = TypeVar("T")
|
|
|
|
|
|
class CacheDict(dict):
|
|
|
|
def retrieve_and_set(self, key: Hashable, func: Callable[[], T]) -> T:
|
|
""" Either return the value at a certain key,
|
|
or set the return value of a function to that key, then return that value. """
|
|
if key not in self:
|
|
self[key] = func()
|
|
return self[key]
|
|
|
|
|
|
class property_cache_once_per_frame(property):
|
|
"""This decorator caches the return value for one game loop,
|
|
then clears it if it is accessed in a different game loop.
|
|
Only works on properties of the bot object, because it requires
|
|
access to self.state.game_loop
|
|
|
|
This decorator compared to the above runs a little faster, however you should only use this decorator if you are sure that you do not modify the mutable once it is calculated and cached.
|
|
|
|
Copied and modified from https://tedboy.github.io/flask/_modules/werkzeug/utils.html#cached_property
|
|
# """
|
|
|
|
def __init__(self, func: Callable[[BotAI], T], name=None):
|
|
# pylint: disable=W0231
|
|
self.__name__ = name or func.__name__
|
|
self.__frame__ = f"__frame__{self.__name__}"
|
|
self.func = func
|
|
|
|
def __set__(self, obj: BotAI, value: T):
|
|
obj.cache[self.__name__] = value
|
|
obj.cache[self.__frame__] = obj.state.game_loop
|
|
|
|
def __get__(self, obj: BotAI, _type=None) -> T:
|
|
value = obj.cache.get(self.__name__, None)
|
|
bot_frame = obj.state.game_loop
|
|
if value is None or obj.cache[self.__frame__] < bot_frame:
|
|
value = self.func(obj)
|
|
obj.cache[self.__name__] = value
|
|
obj.cache[self.__frame__] = bot_frame
|
|
return value
|