You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Not sure if it's in the scope of the lib, but almost always when I create tests I want to cache the evaluation of the engine, to avoid flakiness.
Here is my (wonky, file-based) current implementation that I use for async,
importasyncioimportinspectimportzlibimportchessfromtypingimportList, Union, Dict, Any, Callable, TuplefrompathlibimportPathfromchessimportWHITE, BLACK, Movefromchess.engineimportScore, Cp, Mate, PovScore, InfoDictSTOCKFISH="stockfish"ANALYSE_SIGN=inspect.signature(chess.engine.Protocol.analyse)
defget_checksum_args(*args, **kwargs) ->int:
""" Calculate a checksum for the given arguments. This is used to identify unique calls to the analyse method. """# `apply_defaults` does not seem necessary as it would be considered a breaking change?original_dict=ANALYSE_SIGN.bind(*args, **kwargs).arguments# remove self from the arguments, because it contains the pid changing every time# deepcopy is not possible, due to asyncio shenaniganschecksum_dict= {k: vfork, vinoriginal_dict.items() ifk!="self"}
returnstr(checksum_dict).encode("utf-8")
classCachedEngine(chess.engine.UciProtocol):
def__init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.__used_checksums=set()
# named after cassettes in VCRself.__diskette_dir=Path("diskettes")
self.__diskette_dir.mkdir(exist_ok=True)
asyncdefanalyse(self, *args, **kwargs) ->Union[List[InfoDict], InfoDict]:
print(f"signature {ANALYSE_SIGN}")
print(f"Analysing with args: {args}, kwargs: {kwargs}")
checksum_arg=get_checksum_args(self, *args, **kwargs)
checksum=zlib.adler32(checksum_arg)
self.__used_checksums.add(checksum)
path=self.__diskette_dir/f"{checksum}.py"print(f"checksum of args {checksum_arg}, is {checksum}")
ifpath.exists():
withopen(path) asf:
returneval(f.read())
res=awaitsuper().analyse(*args, **kwargs)
withopen(path, "w") asf:
f.write(f"#{checksum_arg}\n")
f.write(str(res))
returnresdeflist_unused_evals(self) ->List[int]:
# list all files in the diskette directoryreturn [
int(x.stem)
forxinself.__diskette_dir.iterdir()
ifint(x.stem) notinself.__used_checksums
]
# pasted from python-chess source codeasyncdefpopen_uci(
command: Union[str, List[str]], *, setpgrp: bool=False, **popen_args: Any
) ->Tuple[asyncio.SubprocessTransport, CachedEngine]:
transport, protocol=awaitCachedEngine.popen(
command, setpgrp=setpgrp, **popen_args
)
try:
awaitprotocol.initialize()
except:
transport.close()
raisereturntransport, protocolasyncdeftest():
transport, engine=awaitpopen_uci(STOCKFISH)
info=awaitengine.analyse(chess.Board(), chess.engine.Limit(time=5))
print(info)
awaitengine.quit()
if__name__=="__main__":
print("#"*80)
# unittest.main()asyncio.run(test())
and for SimpleEngine, as found in lichess-puzzler (less general than above)
classCachedEngine(SimpleEngine):
def__init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.used_checksums=set()
# named after cassettes in VCRself.diskette_dir=Path("diskettes")
self.diskette_dir.mkdir(exist_ok=True)
# a more general implementation should use the `inspect` module and `Signature.bind`defanalyse(self, board: Board, multipv: int, limit: chess.engine.Limit) ->Union[List[InfoDict], InfoDict]:
checksum_arg=f"{board.fen()}{multipv}{limit}".encode()
checksum=zlib.adler32(checksum_arg)
self.used_checksums.add(checksum)
path=self.diskette_dir/f"{checksum}.py"print(f"checksum of args {checksum_arg}, is {checksum}")
ifpath.exists():
withopen(path) asf:
returneval(f.read())
res=super().analyse(board=board,multipv=multipv,limit=limit)
withopen(path, "w") asf:
f.write(f"#{checksum_arg}\n")
f.write(str(res))
returnresdeflist_unused_evals(self) ->List[int]:
# list all files in the diskette directoryreturn [int(x.stem) forxinself.diskette_dir.iterdir() ifint(x.stem) notinself.used_checksums]
I can see a more general abstract class CachedEngine where only get_cache(checksum: int) -> info | Noneset_cache(checksum: int, info) -> None need to be implemented, with a default file-based also provided.
The text was updated successfully, but these errors were encountered:
Not sure if it's in the scope of the lib, but almost always when I create tests I want to cache the evaluation of the engine, to avoid flakiness.
Here is my (wonky, file-based) current implementation that I use for async,
and for
SimpleEngine
, as found inlichess-puzzler
(less general than above)I can see a more general abstract class
CachedEngine
where onlyget_cache(checksum: int) -> info | None
set_cache(checksum: int, info) -> None
need to be implemented, with a default file-based also provided.The text was updated successfully, but these errors were encountered: