diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0f6ee75..1383bd3 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,15 @@ Changelog ========= +To be released +-------------- + +* Added:: + + client.analysis.get_cloud_evaluation + +Thanks to @Anupya for their contributions to this release. + v0.13.1 (2023-11-02) -------------------- diff --git a/README.rst b/README.rst index 65c671d..05ac2bb 100644 --- a/README.rst +++ b/README.rst @@ -66,6 +66,8 @@ Most of the API is available: client.account.set_kid_mode client.account.upgrade_to_bot + client.analysis.get_cloud_evaluation + client.board.stream_incoming_events client.board.seek client.board.stream_game_state diff --git a/berserk/clients/__init__.py b/berserk/clients/__init__.py index 9a9f7fc..e7c83ac 100644 --- a/berserk/clients/__init__.py +++ b/berserk/clients/__init__.py @@ -2,6 +2,7 @@ import requests +from .analysis import Analysis from .base import BaseClient from .account import Account from .users import Users @@ -26,6 +27,7 @@ __all__ = [ "Account", + "Analysis", "Board", "Bots", "Broadcasts", @@ -54,6 +56,7 @@ class Client(BaseClient): All endpoints are namespaced into the clients below: - :class:`account ` - managing account information + - :class:`account ` - getting information about position analysis - :class:`bots ` - performing bot operations - :class:`broadcasts ` - getting and creating broadcasts - :class:`challenges ` - using challenges @@ -91,6 +94,7 @@ def __init__( session = session or requests.Session() super().__init__(session, base_url) self.account = Account(session, base_url) + self.analysis = Analysis(session, base_url) self.users = Users(session, base_url) self.relations = Relations(session, base_url) self.teams = Teams(session, base_url) diff --git a/berserk/clients/analysis.py b/berserk/clients/analysis.py new file mode 100644 index 0000000..80fcc59 --- /dev/null +++ b/berserk/clients/analysis.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from typing import cast + +from ..types import Variant +from .base import BaseClient +from ..types.analysis import PositionEvaluation + + +class Analysis(BaseClient): + """Client for analysis-related endpoints.""" + + def get_cloud_evaluation( + self, + fen: str, + num_variations: int = 1, + variant: Variant = "standard", + ) -> PositionEvaluation: + """Get the cached evaluation of a position, if available. + + Opening positions have more chances of being available. There are about 15 million positions in the database. + Up to 5 variations may be available. Variants are supported. + + :param fen: FEN of a position + :param num_variations: number of variations + :param variant: game variant to use + :return: cloud evaluation of a position + """ + path = "/api/cloud-eval" + params = {"fen": fen, "multiPv": num_variations, "variant": variant} + return cast(PositionEvaluation, self._r.get(path=path, params=params)) diff --git a/berserk/types/analysis.py b/berserk/types/analysis.py new file mode 100644 index 0000000..da6c56f --- /dev/null +++ b/berserk/types/analysis.py @@ -0,0 +1,20 @@ +from typing import List + +from typing_extensions import TypedDict + + +class PrincipleVariation(TypedDict): + moves: str + """Centipawn (cp) is the unit of measure used in chess as representation of the advantage. A centipawn is 1/100th + of a pawn. This value can be used as an indicator of the quality of play. The fewer centipawns one loses per move, + the stronger the play. + """ + cp: int + + +class PositionEvaluation(TypedDict): + fen: str + knodes: int + depth: int + """ Principle Variation (pv) is a variation composed of the "best" moves (in the opinion of the engine).""" + pvs: List[PrincipleVariation] diff --git a/tests/clients/cassettes/test_analysis/TestAnalysis.test_get_cloud_evaluation.yaml b/tests/clients/cassettes/test_analysis/TestAnalysis.test_get_cloud_evaluation.yaml new file mode 100644 index 0000000..92d081f --- /dev/null +++ b/tests/clients/cassettes/test_analysis/TestAnalysis.test_get_cloud_evaluation.yaml @@ -0,0 +1,48 @@ +interactions: +- request: + body: null + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.31.0 + method: GET + uri: https://lichess.org/api/cloud-eval?fen=rnbqkbnr%2Fppp1pppp%2F8%2F3pP3%2F8%2F8%2FPPPP1PPP%2FRNBQKBNR+b+KQkq+-+0+2&multiPv=1&variant=standard + response: + body: + string: '{"fen": "rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 2","knodes":13683,"depth":22,"pvs":[{"moves":"c8f5 d2d4 e7e6 g1f3 g8e7 c1e3 c7c5 d4c5 e7c6 b1c3","cp":-13}]}' + headers: + Access-Control-Allow-Headers: + - Origin, Authorization, If-Modified-Since, Cache-Control, Content-Type + Access-Control-Allow-Methods: + - OPTIONS, GET, POST, PUT, DELETE + Access-Control-Allow-Origin: + - '*' + Connection: + - keep-alive + Content-Type: + - application/json + Date: + - Thu, 02 Nov 2023 21:25:59 GMT + Permissions-Policy: + - interest-cohort=() + Server: + - nginx + Strict-Transport-Security: + - max-age=63072000; includeSubDomains; preload + Transfer-Encoding: + - chunked + Vary: + - Origin + X-Frame-Options: + - DENY + content-length: + - '6450' + status: + code: 200 + message: OK +version: 1 diff --git a/tests/clients/test_analysis.py b/tests/clients/test_analysis.py new file mode 100644 index 0000000..72a39ad --- /dev/null +++ b/tests/clients/test_analysis.py @@ -0,0 +1,16 @@ +import pytest + +from berserk import Client + +from berserk.types.analysis import PositionEvaluation +from utils import validate, skip_if_older_3_dot_10 + + +class TestAnalysis: + @skip_if_older_3_dot_10 + @pytest.mark.vcr + def test_get_cloud_evaluation(self): + res = Client().analysis.get_cloud_evaluation( + fen="rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 2", + ) + validate(PositionEvaluation, res)