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
 |