Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated Tournaments functionality #25

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 152 additions & 14 deletions berserk/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
from .formats import JSON, LIJSON, PGN, NDJSON, TEXT
from . import models


__all__ = [
'Client',
'Account',
Expand All @@ -23,7 +22,6 @@
'Users',
]


# Base URL for the API
API_URL = 'https://lichess.org/'

Expand Down Expand Up @@ -576,7 +574,7 @@ def create_ai(self, level=8, clock_limit=None, clock_increment=None,
:return: success indicator
:rtype: bool
"""
path = f'api/challenge/ai'
path = "api/challenge/ai"
payload = {
'level': level,
'clock.limit': clock_limit,
Expand All @@ -603,7 +601,7 @@ def create_open(self, clock_limit=None, clock_increment=None,
:return: challenge data
:rtype: dict
"""
path = f'api/challenge/open'
path = "api/challenge/open"
payload = {
'clock.limit': clock_limit,
'clock.increment': clock_increment,
Expand Down Expand Up @@ -886,10 +884,12 @@ def get(self):
path = 'api/tournament'
return self._r.get(path, converter=models.Tournaments.convert_values)

def create(self, clock_time, clock_increment, minutes, name=None,
wait_minutes=None, variant=None, berserkable=None, rated=None,
start_date=None, position=None, password=None, conditions=None):
"""Create a new tournament.
def create_arena(self, clock_time, clock_increment, minutes, name=None,
wait_minutes=None, start_date=None, variant=None,
rated=None, position=None, berserkable=None,
streakable=None, hasChat=None, description=None,
password=None, teambBattleByTeam=None, conditions=None):
"""Create a new arena tournament.

.. note::

Expand All @@ -900,17 +900,24 @@ def create(self, clock_time, clock_increment, minutes, name=None,

If ``name`` is left blank then one is automatically created.

:param int clock_time: intial clock time in minutes
:param int clock_time: initial clock time in minutes
:param int clock_increment: clock increment in seconds
:param int minutes: length of the tournament in minutes
:param str name: tournament name
:param int wait_minutes: future start time in minutes
:param str start_date: when to start the tournament
:param str variant: variant to use if other than standard
:param bool rated: whether the game affects player ratings
:param str berserkable: whether players can use berserk
:param str position: custom initial position in FEN
:param str password: password (makes the tournament private)
:param str berserkable: whether players can use berserk
:param bool streakable: whether players get streaks
:param bool hasChat: whether players can
discuss in a chat
:param string description: anything you want to
tell players about the tournament
:param str password: password
:param str teambBattleByTeam: Id of a team you lead
to create a team battle
:param dict conditions: conditions for participation
:return: created tournament info
:rtype: dict
Expand All @@ -927,15 +934,64 @@ def create(self, clock_time, clock_increment, minutes, name=None,
'rated': rated,
'position': position,
'berserkable': berserkable,
'streakable': streakable,
'hasChat': hasChat,
'description': description,
'password': password,
'teambBattleByTeam': teambBattleByTeam,
**{f'conditions.{c}': v for c, v in (conditions or {}).items()},
}
return self._r.post(path, json=payload,
converter=models.Tournament.convert)

def export_games(self, id_, as_pgn=False, moves=None, tags=None,
clocks=None, evals=None, opening=None):
"""Export games from a tournament.
def create_swiss(self, teamId_, clock_limit, clock_increment, nbRounds,
name=None, startsAt=None, roundInterval=None,
variant=None, description=None, rated=None, chatFor=None):
"""Create a new swiss tournament

.. note::

If ``name`` is left blank then one is automatically created.

.. note::

If ``startsAt`` is left blank then the
tournament begins 10 minutes after creation

:param string teamId_: team Id, required for swiss tournaments
:param int clock_limit: initial clock time in seconds
:param int clock_increment: clock increment in seconds
:param int nbRounds: maximum number of rounds to play
:param string name: tournament name
:param int startsAt: when to start tournament, in ms timestamp
:param int roundInterval: interval between rounds in seconds
:param string variant: variant to use if other than standard
:param string description: tournament description
:param bool rated: whether the game affects player ratings
:param int chatFor: who can read and write in the chat
:return: created tournament info
:rtype: dict
"""
path = f'api/swiss/new/{teamId_}'

payload = {
'name': name,
'clock.limit': clock_limit,
'clock.increment': clock_increment,
'nbRounds': nbRounds,
'startsAt': startsAt,
'roundInterval': roundInterval,
'variant': variant,
'description': description,
'rated': rated,
'chatFor': chatFor
}
return self._r.post(path, json=payload,
converter=models.Tournament.convert)

def export_arena_games(self, id_, as_pgn=False, moves=None, tags=None,
clocks=None, evals=None, opening=None):
"""Export games from a arena tournament.

:param str id_: tournament ID
:param bool as_pgn: whether to return PGN instead of JSON
Expand All @@ -961,6 +1017,88 @@ def export_games(self, id_, as_pgn=False, moves=None, tags=None,
return self._r.get(path, params=params, fmt=fmt,
converter=models.Game.convert)

def export_swiss_games(self, id_, as_pgn=False, moves=None, pgnInJson=None,
tags=None, clocks=None, evals=None, opening=None):
"""Export games from a swiss tournament

:param str id_: tournament id
:param bool as_pgn: whether to return pgn instead of JSON
:param bool moves: include moves
:param bool pgnInJson: include the full PGN within the
JSON response, in a pgn field
:param bool tags: include tags
:param bool clocks: include clock comments
:param bool evals: include analysis evaluation
comments in the PGN, when available
:param bool opening: include the opening name
:return: games
:rtype: list
"""
path = f'api/swiss/{id_}/games'
params = {
'moves:': moves,
'pgnInJson': pgnInJson,
'tags': tags,
'clocks': clocks,
'evals': evals,
'opening': opening,
}
fmt = PGN if self._use_pgn(as_pgn) else NDJSON
return self._r.get(path, params=params, fmt=fmt,
converter=models.Game.convert)

def tournaments_by_user(self, username, nb=None, as_pgn=False):
"""Get tournaments created by a user

:param string username: username
:param int nb: max number of tournaments to fetch
:param bool as_pgn: whether to return pgn instead of Json
:return: tournaments
:rtype: list
"""

path = f'api/user/{username}/tournament/created'
params = {
'nb': nb,
}
fmt = PGN if self._use_pgn(as_pgn) else NDJSON
return self._r.get(path, params=params, fmt=fmt,
converter=models.Game.convert)

def arenas_by_team(self, teamId, maxT=None, as_pgn=False):
"""Get arenas created for a team

:param string teamId: Id of the team
:param int maxT: how many tournaments to download
:param bool as_pgn: whether to return pgn instead of Json
:return: tournaments
:rtype: list
"""
path = f'api/team/{teamId}/arena'
params = {
'max': maxT,
}
fmt = PGN if self._use_pgn(as_pgn) else NDJSON
return self._r.get(path, params=params, fmt=fmt,
converter=models.Game.convert)

def swiss_by_team(self, teamId, maxT=None, as_pgn=False):
"""Get swiss tournaments created for a team

:param string teamId: Id of the team
:param int maxT: how many tournaments to download
:param bool as_pgn: whether to return pgn instead of Json
:return: tournaments
:rtype: list
"""
path = f'api/team/{teamId}/swiss'
params = {
'max': maxT,
}
fmt = PGN if self._use_pgn(as_pgn) else NDJSON
return self._r.get(path, params=params, fmt=fmt,
converter=models.Game.convert)

def stream_results(self, id_, limit=None):
"""Stream the results of a tournament.

Expand Down
27 changes: 26 additions & 1 deletion docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ tournament is easy:

.. code-block:: python

>>> client.tournaments.create(clock_time=10, clock_increment=3, minutes=180)
>>> client.tournaments.create_arena(clock_time=10, clock_increment=3, minutes=180)
{'berserkable': True,
'clock': {'increment': 3, 'limit': 600},
'createdBy': 'rhgrant10',
Expand Down Expand Up @@ -428,6 +428,31 @@ provided enum value in ``berserk.enums.Position``:
>>> client.tournaments.create(clock_time=10, clock_increment=3, minutes=180,
position=berserk.enums.Position.KINGS_PAWN)

You can also create Swiss tournaments easily, specifying the team id, clock time,
clock increment, and number of rounds.

.. code-block:: python

>>> client.tournaments.create_swiss(teamid_="coders", clock_limit=10,
clock_increment=0, nbRounds=5)
{'rated': true,
'clock': {'increment': 0, 'limit': 600},
'createdBy': "zccze",
'greatPlayer': {'name': "Wang',
'url':'https://wikipedia.org/wiki/Wang_Hao_(chess_player)' },
'id': '3uwyXjiC'
'name': 'Wang',
'nbOngoing': 0,
'nbPlayers': 0,
'nbRounds': 5,
'nextRound': { 'at': '2021-05-18T12:23:18.233-06:00', 'in': 600},
'quote': {'author': 'Bent Larsen',
'text': 'I often play a move I know how to refute.'},
'round': 0,
'startsAt': '2021-05-18T12:23:18.233-06:00',
'status': 'created',
'variant': 'standard'
}

Additionally you can see tournaments that have recently finished, are in
progress, and are about to start:
Expand Down