From c250d0626969e8e98af8f457573f44094f0fb504 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Fri, 6 May 2022 20:04:48 +0300 Subject: [PATCH 01/27] Add matchmaking --- README.md | 29 ++++++++++++++++++--- config.yml.default | 10 ++++++++ lichess-bot.py | 18 +++++++++++-- lichess.py | 20 ++++++++++++--- matchmaking.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 10 deletions(-) create mode 100644 matchmaking.py diff --git a/README.md b/README.md index f524cc794..8b1498d82 100644 --- a/README.md +++ b/README.md @@ -219,18 +219,39 @@ will precede the `go` command to start thinking with `sd 5`. The other `go_comma -rated -casual ``` - - `greeting`: Send messages via chat to the bot's opponent. The string `{me}` will be replaced by the bot's lichess account name. The string `{opponent}` will be replaced by the opponent's lichess account name. Any other word between curly brackets will be removed. If you want to put a curly bracket in the message, use two: `{{` or `}}`. - - `hello`: Message to send to opponent before the bot makes its first move. - - `goodbye`: Message to send to opponent once the game is over. +- `greeting`: Send messages via chat to the bot's opponent. The string `{me}` will be replaced by the bot's lichess account name. The string `{opponent}` will be replaced by the opponent's lichess account name. Any other word between curly brackets will be removed. If you want to put a curly bracket in the message, use two: `{{` or `}}`. + - `hello`: Message to send to opponent before the bot makes its first move. + - `goodbye`: Message to send to opponent once the game is over. ```yml greeting: hello: Hi, {opponent}! I'm {me}. Good luck! goodbye: Good game! ``` - - `pgn_directory`: Write a record of every game played in PGN format to files in this directory. Each bot move will be annotated with the bot's calculated score and principal variation. The score is written with a tag of the form `[%eval s,d]`, where `s` is the score in pawns (positive means white has the advantage), and `d` is the depth of the search. Each game will be written to a uniquely named file. +- `pgn_directory`: Write a record of every game played in PGN format to files in this directory. Each bot move will be annotated with the bot's calculated score and principal variation. The score is written with a tag of the form `[%eval s,d]`, where `s` is the score in pawns (positive means white has the advantage), and `d` is the depth of the search. Each game will be written to a uniquely named file. ```yml pgn_directory: "game_records" ``` +- `matchmaking`: Challenge a random bot. + - `allow_matchmaking`: Whether to challenge other bots. + - `challenge_interval`: How often to challenge a bot. + - `challenge_initial_time`: The initial time (in seconds) for the challenges. + - `challenge_increment`: The increment (in seconds) for the challenges. + - `challenge_days`: The days for a correspondence challenge. If this option is enabled, a correspondence challenge will be created even if `challenge_initial_time` is enabled. + - `opponent_min_rating`: The minimum rating of the opponent bot. + - `opponent_max_rating`: The maximum rating of the opponent bot. + - `challenge_mode`: Possible options are `casual`, `rated` and `random`. +```yml +matchmaking: + allow_matchmaking: false + challenge_interval: 30 + challenge_initial_time: 60 + challenge_increment: 3 +# challenge_days: 2 + opponent_min_rating: 2000 + opponent_max_rating: 3000 + challenge_mode: "random" + +``` ## Lichess Upgrade to Bot Account **WARNING: This is irreversible. [Read more about upgrading to bot account](https://lichess.org/api#operation/botAccountUpgrade).** diff --git a/config.yml.default b/config.yml.default index 9fa8f7601..ea7c7bb6d 100644 --- a/config.yml.default +++ b/config.yml.default @@ -126,3 +126,13 @@ greeting: goodbye: "Good game!" # Message to send to chat at the end of a game # pgn_directory: "game_records" # A directory where PGN-format records of the bot's games are kept + +matchmaking: + allow_matchmaking: false # Set it to 'true' to challenge other bot in a set interval, time control and range. + challenge_interval: 30 # Interval in minutes between two challenges. + challenge_initial_time: 60 # Initial time in seconds of the challenge. + challenge_increment: 3 # Increment in seconds of the challenge. +# challenge_days: 2 # Days for correspondence challenge. If this option is enabled, a correspondence challenge will be created even if 'challenge_initial_time' is enabled. + opponent_min_rating: 2000 # Opponents rating should be above this value + opponent_max_rating: 3000 # Opponents rating should be below this value + challenge_mode: "random" # Set it to the mode in which challenges are sent. Possible options are 'casual', 'rated' and 'random'. diff --git a/lichess-bot.py b/lichess-bot.py index fc1d580d8..22da27336 100644 --- a/lichess-bot.py +++ b/lichess-bot.py @@ -5,6 +5,7 @@ import chess.polyglot import engine_wrapper import model +import matchmaking import json import lichess import logging @@ -117,6 +118,10 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) correspondence_queue.put("") startup_correspondence_games = [game["gameId"] for game in li.get_ongoing_games() if game["perf"] == "correspondence"] wait_for_correspondence_ping = False + matchmaker = matchmaking.Matchmaking(li, config) + last_challenge_created = time.time() + challenge_id = None + challenge_expire_time = 25 # The challenge expires 20 seconds after creating it. busy_processes = 0 queued_processes = 0 @@ -156,7 +161,7 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) list_c = list(challenge_queue) list_c.sort(key=lambda c: -c.score()) challenge_queue = list_c - else: + elif chlng.id != challenge_id: try: reason = "generic" challenge = config["challenge"] @@ -214,12 +219,20 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) logger.info(f"Accept {chlng}") queued_processes += 1 li.accept_challenge(chlng.id) + if challenge_id == chlng.id: + challenge_id = None logger.info(f"--- Process Queue. Total Queued: {queued_processes}. Total Used: {busy_processes}") except (HTTPError, ReadTimeout) as exception: if isinstance(exception, HTTPError) and exception.response.status_code == 404: # ignore missing challenge logger.info(f"Skip missing {chlng}") queued_processes -= 1 + if queued_processes + busy_processes < max_games and not challenge_queue and (config.get("matchmaking") or {}).get("allow_matchmaking") and ((last_challenge_created + ((config.get("matchmaking") or {}).get("challenge_interval") or 30) * 60) < time.time() or last_challenge_created + challenge_expire_time < time.time() and challenge_id): + logger.debug("Challenging a random bot") + challenge_id = matchmaker.challenge() + if challenge_id: + last_challenge_created = time.time() + control_queue.task_done() logger.info("Terminated") @@ -280,9 +293,10 @@ def play_game(li, game_id, control_queue, user_profile, config, challenge_queue, else: binary_chunk = next(lines) upd = json.loads(binary_chunk.decode("utf-8")) if binary_chunk else None - logger.debug(f"Game state: {upd}") u_type = upd["type"] if upd else "ping" + if u_type != "ping": + logger.debug(f"Game state: {upd}") if u_type == "chatLine": conversation.react(ChatLine(upd), game) elif u_type == "gameState": diff --git a/lichess.py b/lichess.py index 197e876d5..38e09cac2 100644 --- a/lichess.py +++ b/lichess.py @@ -1,3 +1,4 @@ +import json import requests from urllib.parse import urljoin from requests.exceptions import ConnectionError, HTTPError, ReadTimeout @@ -19,7 +20,9 @@ "decline": "/api/challenge/{}/decline", "upgrade": "/api/bot/account/upgrade", "resign": "/api/bot/game/{}/resign", - "export": "/game/export/{}" + "export": "/game/export/{}", + "online_bots": "/api/bot/online", + "challenge": "/api/challenge/{}" } @@ -61,11 +64,12 @@ def api_get(self, path, raise_for_status=True, get_raw_text=False, params=None): giveup=is_final, backoff_log_level=logging.DEBUG, giveup_log_level=logging.DEBUG) - def api_post(self, path, data=None, headers=None, params=None): + def api_post(self, path, data=None, headers=None, params=None, payload=None, raise_for_status=True): logging.getLogger("backoff").setLevel(self.logging_level) url = urljoin(self.baseUrl, path) - response = self.session.post(url, data=data, headers=headers, params=params, timeout=2) - response.raise_for_status() + response = self.session.post(url, data=data, headers=headers, params=params, json=payload, timeout=2) + if raise_for_status: + response.raise_for_status() return response.json() def get_game(self, game_id): @@ -119,3 +123,11 @@ def get_game_pgn(self, game_id): return self.api_get(ENDPOINTS["export"].format(game_id), get_raw_text=True, params={"literate": "true"}) + + def get_online_bots(self): + online_bots = self.api_get(ENDPOINTS["online_bots"], get_raw_text=True) + online_bots = list(filter(bool, online_bots.split("\n"))) + return list(map(lambda bot: json.loads(bot), online_bots)) + + def challenge(self, username, params): + return self.api_post(ENDPOINTS["challenge"].format(username), payload=params, raise_for_status=False) diff --git a/matchmaking.py b/matchmaking.py new file mode 100644 index 000000000..e554ab30b --- /dev/null +++ b/matchmaking.py @@ -0,0 +1,63 @@ +import random +import logging + +logger = logging.getLogger(__name__) + + +class Matchmaking: + def __init__(self, li, config): + self.li = li + self.variants = config["challenge"]["variants"].copy() + if "fromPosition" in self.variants: + self.variants.pop(self.variants.index("fromPosition")) + if "correspondence" in self.variants: + self.variants.pop(self.variants.index("correspondence")) + self.matchmaking_cfg = config["matchmaking"] + + def create_challenge(self, username, base_time, increment, days, variant): + mode = self.matchmaking_cfg.get("challenge_mode") or "random" + if mode == "random": + mode = random.choice(["casual", "rated"]) + rated = mode == "rated" + params = {"rated": rated, "variant": variant} + if days: + params["days"] = days + else: + params["clock.limit"] = base_time + params["clock.increment"] = increment + challenge_id = self.li.challenge(username, params).get("challenge", {}).get("id") + return challenge_id + + def choose_opponent(self): + variant = random.choice(self.variants) + base_time = self.matchmaking_cfg.get("challenge_initial_time") or 60 + increment = self.matchmaking_cfg.get("challenge_increment") or 2 + days = self.matchmaking_cfg.get("challenge_days") + game_duration = base_time + increment * 40 + if variant != "standard": + game_type = variant + elif days: + game_type = "correspondence" + elif game_duration < 179: + game_type = "bullet" + elif game_duration < 479: + game_type = "blitz" + elif game_duration < 1499: + game_type = "rapid" + else: + game_type = "classical" + + min_rating = self.matchmaking_cfg.get("opponent_min_rating") or 2000 + max_rating = self.matchmaking_cfg.get("opponent_max_rating") or 3000 + + online_bots = self.li.get_online_bots() + online_bots = list(filter(lambda bot: min_rating <= ((bot["perfs"].get(game_type) or {}).get("rating") or 0) <= max_rating, online_bots)) + bot_username = random.choice(online_bots)["username"] if online_bots else None + return bot_username, base_time, increment, days, variant + + def challenge(self): + bot_username, base_time, increment, days, variant = self.choose_opponent() + logger.debug(f"Will challenge {bot_username} for a {variant} game.") + challenge_id = self.create_challenge(bot_username, base_time, increment, days, variant) if bot_username else None + logger.debug(f"Challenge id is {challenge_id}.") + return challenge_id From 41ff4942487990e712d400e51f618ff06ad5e586 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Fri, 6 May 2022 20:07:30 +0300 Subject: [PATCH 02/27] Delete some whitespace --- README.md | 2 +- config.yml.default | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8b1498d82..75d154623 100644 --- a/README.md +++ b/README.md @@ -246,7 +246,7 @@ matchmaking: challenge_interval: 30 challenge_initial_time: 60 challenge_increment: 3 -# challenge_days: 2 +# challenge_days: 2 opponent_min_rating: 2000 opponent_max_rating: 3000 challenge_mode: "random" diff --git a/config.yml.default b/config.yml.default index ea7c7bb6d..58e48e6d7 100644 --- a/config.yml.default +++ b/config.yml.default @@ -132,7 +132,7 @@ matchmaking: challenge_interval: 30 # Interval in minutes between two challenges. challenge_initial_time: 60 # Initial time in seconds of the challenge. challenge_increment: 3 # Increment in seconds of the challenge. -# challenge_days: 2 # Days for correspondence challenge. If this option is enabled, a correspondence challenge will be created even if 'challenge_initial_time' is enabled. +# challenge_days: 2 # Days for correspondence challenge. If this option is enabled, a correspondence challenge will be created even if 'challenge_initial_time' is enabled. opponent_min_rating: 2000 # Opponents rating should be above this value opponent_max_rating: 3000 # Opponents rating should be below this value challenge_mode: "random" # Set it to the mode in which challenges are sent. Possible options are 'casual', 'rated' and 'random'. From 028323b24d3134212aa7b1a6f34815e7ad8ea605 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Fri, 6 May 2022 20:12:15 +0300 Subject: [PATCH 03/27] Remove new line --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 75d154623..8a98669fc 100644 --- a/README.md +++ b/README.md @@ -250,7 +250,6 @@ matchmaking: opponent_min_rating: 2000 opponent_max_rating: 3000 challenge_mode: "random" - ``` ## Lichess Upgrade to Bot Account From 9968264f7b2315115abe1ac207a4935772df6bad Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Fri, 6 May 2022 22:49:40 +0300 Subject: [PATCH 04/27] Remove useless code --- matchmaking.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/matchmaking.py b/matchmaking.py index e554ab30b..3f05b17e0 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -10,8 +10,6 @@ def __init__(self, li, config): self.variants = config["challenge"]["variants"].copy() if "fromPosition" in self.variants: self.variants.pop(self.variants.index("fromPosition")) - if "correspondence" in self.variants: - self.variants.pop(self.variants.index("correspondence")) self.matchmaking_cfg = config["matchmaking"] def create_challenge(self, username, base_time, increment, days, variant): From e598e56712ce6c1f8be0a35e4c18b4f6eb8f5aa6 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sat, 7 May 2022 08:40:39 +0300 Subject: [PATCH 05/27] Fix a small bug The bot would crash if there was no matchmaking section in config.yml. --- matchmaking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matchmaking.py b/matchmaking.py index 3f05b17e0..8436bf5dc 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -10,7 +10,7 @@ def __init__(self, li, config): self.variants = config["challenge"]["variants"].copy() if "fromPosition" in self.variants: self.variants.pop(self.variants.index("fromPosition")) - self.matchmaking_cfg = config["matchmaking"] + self.matchmaking_cfg = config.get("matchmaking") or {} def create_challenge(self, username, base_time, increment, days, variant): mode = self.matchmaking_cfg.get("challenge_mode") or "random" From 82b7512ddb10b69c05213106cf592794aa31b410 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sat, 7 May 2022 09:36:56 +0300 Subject: [PATCH 06/27] Do not challenge ourselves and move some code --- lichess-bot.py | 12 ++++-------- matchmaking.py | 16 ++++++++++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/lichess-bot.py b/lichess-bot.py index 22da27336..0a0ef7ffc 100644 --- a/lichess-bot.py +++ b/lichess-bot.py @@ -118,10 +118,8 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) correspondence_queue.put("") startup_correspondence_games = [game["gameId"] for game in li.get_ongoing_games() if game["perf"] == "correspondence"] wait_for_correspondence_ping = False - matchmaker = matchmaking.Matchmaking(li, config) - last_challenge_created = time.time() + matchmaker = matchmaking.Matchmaking(li, config, user_profile["username"]) challenge_id = None - challenge_expire_time = 25 # The challenge expires 20 seconds after creating it. busy_processes = 0 queued_processes = 0 @@ -181,6 +179,8 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) pass elif event["type"] == "gameStart": game_id = event["game"]["id"] + if challenge_id == game_id: + challenge_id = None if game_id in startup_correspondence_games: logger.info(f'--- Enqueue {config["url"] + game_id}') correspondence_queue.put(game_id) @@ -219,19 +219,15 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) logger.info(f"Accept {chlng}") queued_processes += 1 li.accept_challenge(chlng.id) - if challenge_id == chlng.id: - challenge_id = None logger.info(f"--- Process Queue. Total Queued: {queued_processes}. Total Used: {busy_processes}") except (HTTPError, ReadTimeout) as exception: if isinstance(exception, HTTPError) and exception.response.status_code == 404: # ignore missing challenge logger.info(f"Skip missing {chlng}") queued_processes -= 1 - if queued_processes + busy_processes < max_games and not challenge_queue and (config.get("matchmaking") or {}).get("allow_matchmaking") and ((last_challenge_created + ((config.get("matchmaking") or {}).get("challenge_interval") or 30) * 60) < time.time() or last_challenge_created + challenge_expire_time < time.time() and challenge_id): + if queued_processes + busy_processes < max_games and not challenge_queue and matchmaker.should_create_challenge(challenge_id): logger.debug("Challenging a random bot") challenge_id = matchmaker.challenge() - if challenge_id: - last_challenge_created = time.time() control_queue.task_done() diff --git a/matchmaking.py b/matchmaking.py index 8436bf5dc..a7e7bd6a0 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -1,16 +1,26 @@ import random +import time import logging logger = logging.getLogger(__name__) class Matchmaking: - def __init__(self, li, config): + def __init__(self, li, config, username): self.li = li self.variants = config["challenge"]["variants"].copy() if "fromPosition" in self.variants: self.variants.pop(self.variants.index("fromPosition")) self.matchmaking_cfg = config.get("matchmaking") or {} + self.username = username + self.last_challenge_created = time.time() + self.challenge_expire_time = 25 # The challenge expires 20 seconds after creating it. + + def should_create_challenge(self, challenge_id): + matchmaking_enabled = self.matchmaking_cfg.get("allow_matchmaking") + time_has_passed = self.last_challenge_created + ((self.matchmaking_cfg.get("challenge_interval") or 30) * 60) < time.time() + challenge_expired = self.last_challenge_created + self.challenge_expire_time < time.time() and challenge_id + return matchmaking_enabled and (time_has_passed or challenge_expired) def create_challenge(self, username, base_time, increment, days, variant): mode = self.matchmaking_cfg.get("challenge_mode") or "random" @@ -49,7 +59,7 @@ def choose_opponent(self): max_rating = self.matchmaking_cfg.get("opponent_max_rating") or 3000 online_bots = self.li.get_online_bots() - online_bots = list(filter(lambda bot: min_rating <= ((bot["perfs"].get(game_type) or {}).get("rating") or 0) <= max_rating, online_bots)) + online_bots = list(filter(lambda bot: bot["username"] != self.username and min_rating <= ((bot["perfs"].get(game_type) or {}).get("rating") or 0) <= max_rating, online_bots)) bot_username = random.choice(online_bots)["username"] if online_bots else None return bot_username, base_time, increment, days, variant @@ -58,4 +68,6 @@ def challenge(self): logger.debug(f"Will challenge {bot_username} for a {variant} game.") challenge_id = self.create_challenge(bot_username, base_time, increment, days, variant) if bot_username else None logger.debug(f"Challenge id is {challenge_id}.") + if challenge_id: + self.last_challenge_created = time.time() return challenge_id From 6866999f8ecb24b98828e990cf2b0be0d4ed9616 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sat, 7 May 2022 09:49:15 +0300 Subject: [PATCH 07/27] Move some code --- lichess-bot.py | 11 +++++------ matchmaking.py | 7 ++++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lichess-bot.py b/lichess-bot.py index 0a0ef7ffc..ee4071cfa 100644 --- a/lichess-bot.py +++ b/lichess-bot.py @@ -119,7 +119,6 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) startup_correspondence_games = [game["gameId"] for game in li.get_ongoing_games() if game["perf"] == "correspondence"] wait_for_correspondence_ping = False matchmaker = matchmaking.Matchmaking(li, config, user_profile["username"]) - challenge_id = None busy_processes = 0 queued_processes = 0 @@ -159,7 +158,7 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) list_c = list(challenge_queue) list_c.sort(key=lambda c: -c.score()) challenge_queue = list_c - elif chlng.id != challenge_id: + elif chlng.id != matchmaker.challenge_id: try: reason = "generic" challenge = config["challenge"] @@ -179,8 +178,8 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) pass elif event["type"] == "gameStart": game_id = event["game"]["id"] - if challenge_id == game_id: - challenge_id = None + if matchmaker.challenge_id == game_id: + matchmaker.challenge_id = None if game_id in startup_correspondence_games: logger.info(f'--- Enqueue {config["url"] + game_id}') correspondence_queue.put(game_id) @@ -225,9 +224,9 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) logger.info(f"Skip missing {chlng}") queued_processes -= 1 - if queued_processes + busy_processes < max_games and not challenge_queue and matchmaker.should_create_challenge(challenge_id): + if queued_processes + busy_processes < max_games and not challenge_queue and matchmaker.should_create_challenge(): logger.debug("Challenging a random bot") - challenge_id = matchmaker.challenge() + matchmaker.challenge() control_queue.task_done() diff --git a/matchmaking.py b/matchmaking.py index a7e7bd6a0..d3a9d7131 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -15,11 +15,12 @@ def __init__(self, li, config, username): self.username = username self.last_challenge_created = time.time() self.challenge_expire_time = 25 # The challenge expires 20 seconds after creating it. + self.challenge_id = None - def should_create_challenge(self, challenge_id): + def should_create_challenge(self): matchmaking_enabled = self.matchmaking_cfg.get("allow_matchmaking") time_has_passed = self.last_challenge_created + ((self.matchmaking_cfg.get("challenge_interval") or 30) * 60) < time.time() - challenge_expired = self.last_challenge_created + self.challenge_expire_time < time.time() and challenge_id + challenge_expired = self.last_challenge_created + self.challenge_expire_time < time.time() and self.challenge_id return matchmaking_enabled and (time_has_passed or challenge_expired) def create_challenge(self, username, base_time, increment, days, variant): @@ -70,4 +71,4 @@ def challenge(self): logger.debug(f"Challenge id is {challenge_id}.") if challenge_id: self.last_challenge_created = time.time() - return challenge_id + self.challenge_id = challenge_id From fb66668b072c50a94bd61ed25463b4a60e76f43c Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sun, 8 May 2022 12:01:08 +0300 Subject: [PATCH 08/27] Remove unnecessary debugging output --- lichess.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lichess.py b/lichess.py index 38e09cac2..a0785ba8d 100644 --- a/lichess.py +++ b/lichess.py @@ -55,6 +55,7 @@ def api_get(self, path, raise_for_status=True, get_raw_text=False, params=None): response = self.session.get(url, timeout=2, params=params) if raise_for_status: response.raise_for_status() + response.encoding = "utf-8" return response.text if get_raw_text else response.json() @backoff.on_exception(backoff.constant, From 978d042adaddc85543795e0a878a28832cb3dbd8 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sun, 8 May 2022 12:02:54 +0300 Subject: [PATCH 09/27] Add unit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8a98669fc..e1453838f 100644 --- a/README.md +++ b/README.md @@ -233,7 +233,7 @@ will precede the `go` command to start thinking with `sd 5`. The other `go_comma ``` - `matchmaking`: Challenge a random bot. - `allow_matchmaking`: Whether to challenge other bots. - - `challenge_interval`: How often to challenge a bot. + - `challenge_interval`: How often to challenge a bot (in minutes). - `challenge_initial_time`: The initial time (in seconds) for the challenges. - `challenge_increment`: The increment (in seconds) for the challenges. - `challenge_days`: The days for a correspondence challenge. If this option is enabled, a correspondence challenge will be created even if `challenge_initial_time` is enabled. From 750394b710a1d363c03d28025ab7b6481b6a923f Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sun, 8 May 2022 20:12:27 +0300 Subject: [PATCH 10/27] Add extra space --- config.yml.default | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.yml.default b/config.yml.default index 58e48e6d7..848ed784e 100644 --- a/config.yml.default +++ b/config.yml.default @@ -132,7 +132,7 @@ matchmaking: challenge_interval: 30 # Interval in minutes between two challenges. challenge_initial_time: 60 # Initial time in seconds of the challenge. challenge_increment: 3 # Increment in seconds of the challenge. -# challenge_days: 2 # Days for correspondence challenge. If this option is enabled, a correspondence challenge will be created even if 'challenge_initial_time' is enabled. +# challenge_days: 2 # Days for correspondence challenge. If this option is enabled, a correspondence challenge will be created even if 'challenge_initial_time' is enabled. opponent_min_rating: 2000 # Opponents rating should be above this value opponent_max_rating: 3000 # Opponents rating should be below this value challenge_mode: "random" # Set it to the mode in which challenges are sent. Possible options are 'casual', 'rated' and 'random'. From 9d06571deaae29c58ca9b60ac9007a088d84fd86 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Mon, 9 May 2022 14:49:46 +0300 Subject: [PATCH 11/27] Simplify code --- matchmaking.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/matchmaking.py b/matchmaking.py index d3a9d7131..5039ccdb3 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -8,9 +8,7 @@ class Matchmaking: def __init__(self, li, config, username): self.li = li - self.variants = config["challenge"]["variants"].copy() - if "fromPosition" in self.variants: - self.variants.pop(self.variants.index("fromPosition")) + self.variants = list(filter(lambda variant: variant != "fromPosition", config["challenge"]["variants"])) self.matchmaking_cfg = config.get("matchmaking") or {} self.username = username self.last_challenge_created = time.time() From a45b149b72672b30261757611e2e112e676820cc Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Tue, 10 May 2022 18:42:39 +0300 Subject: [PATCH 12/27] Fix bug Sometimes 'bot' didn't have 'perfs'. This was caused because the account was closed. We use bot.get("disabled") to first check if the account is closed. --- matchmaking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matchmaking.py b/matchmaking.py index 5039ccdb3..ce2bc7630 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -58,7 +58,7 @@ def choose_opponent(self): max_rating = self.matchmaking_cfg.get("opponent_max_rating") or 3000 online_bots = self.li.get_online_bots() - online_bots = list(filter(lambda bot: bot["username"] != self.username and min_rating <= ((bot["perfs"].get(game_type) or {}).get("rating") or 0) <= max_rating, online_bots)) + online_bots = list(filter(lambda bot: bot["username"] != self.username and not bot.get("disabled") and min_rating <= ((bot["perfs"].get(game_type) or {}).get("rating") or 0) <= max_rating, online_bots)) bot_username = random.choice(online_bots)["username"] if online_bots else None return bot_username, base_time, increment, days, variant From e6f6bb20b2f7e4372f917d75d8d731483347249d Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Tue, 10 May 2022 18:46:08 +0300 Subject: [PATCH 13/27] Use logger.info instead of logger.debug --- lichess-bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lichess-bot.py b/lichess-bot.py index ee4071cfa..44080aa19 100644 --- a/lichess-bot.py +++ b/lichess-bot.py @@ -225,7 +225,7 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) queued_processes -= 1 if queued_processes + busy_processes < max_games and not challenge_queue and matchmaker.should_create_challenge(): - logger.debug("Challenging a random bot") + logger.info("Challenging a random bot") matchmaker.challenge() control_queue.task_done() From 7ad719b61bde8fe70d4530fa1ec028dd4abab542 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Wed, 11 May 2022 07:31:47 +0300 Subject: [PATCH 14/27] Fix bug When base_time or increment was 0, because they are equal to false, they were replaced by the default values. --- matchmaking.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/matchmaking.py b/matchmaking.py index ce2bc7630..b6d617bc0 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -37,8 +37,8 @@ def create_challenge(self, username, base_time, increment, days, variant): def choose_opponent(self): variant = random.choice(self.variants) - base_time = self.matchmaking_cfg.get("challenge_initial_time") or 60 - increment = self.matchmaking_cfg.get("challenge_increment") or 2 + base_time = self.matchmaking_cfg.get("challenge_initial_time", 60) + increment = self.matchmaking_cfg.get("challenge_increment", 2) days = self.matchmaking_cfg.get("challenge_days") game_duration = base_time + increment * 40 if variant != "standard": From 2094600039f2bbfa2f498dc3165edde06d98d5d3 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Wed, 11 May 2022 14:52:41 +0300 Subject: [PATCH 15/27] Use line breaks --- matchmaking.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/matchmaking.py b/matchmaking.py index b6d617bc0..a337d2710 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -58,7 +58,9 @@ def choose_opponent(self): max_rating = self.matchmaking_cfg.get("opponent_max_rating") or 3000 online_bots = self.li.get_online_bots() - online_bots = list(filter(lambda bot: bot["username"] != self.username and not bot.get("disabled") and min_rating <= ((bot["perfs"].get(game_type) or {}).get("rating") or 0) <= max_rating, online_bots)) + online_bots = list(filter(lambda bot: bot["username"] != self.username and not bot.get("disabled") and + min_rating <= ((bot["perfs"].get(game_type) or {}).get("rating") or 0) <= max_rating, + online_bots)) bot_username = random.choice(online_bots)["username"] if online_bots else None return bot_username, base_time, increment, days, variant From d569de46c54221cecd745176307544fde27dc9ee Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Fri, 13 May 2022 14:03:02 +0300 Subject: [PATCH 16/27] Change minimum and maximum rating for matchmaking --- README.md | 8 ++++---- config.yml.default | 4 ++-- matchmaking.py | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e1453838f..114891bd6 100644 --- a/README.md +++ b/README.md @@ -237,8 +237,8 @@ will precede the `go` command to start thinking with `sd 5`. The other `go_comma - `challenge_initial_time`: The initial time (in seconds) for the challenges. - `challenge_increment`: The increment (in seconds) for the challenges. - `challenge_days`: The days for a correspondence challenge. If this option is enabled, a correspondence challenge will be created even if `challenge_initial_time` is enabled. - - `opponent_min_rating`: The minimum rating of the opponent bot. - - `opponent_max_rating`: The maximum rating of the opponent bot. + - `opponent_min_rating`: The minimum rating of the opponent bot. The minimum rating in lichess is 600. + - `opponent_max_rating`: The maximum rating of the opponent bot. The maximum rating in lichess is 4000. - `challenge_mode`: Possible options are `casual`, `rated` and `random`. ```yml matchmaking: @@ -247,8 +247,8 @@ matchmaking: challenge_initial_time: 60 challenge_increment: 3 # challenge_days: 2 - opponent_min_rating: 2000 - opponent_max_rating: 3000 + opponent_min_rating: 600 + opponent_max_rating: 4000 challenge_mode: "random" ``` diff --git a/config.yml.default b/config.yml.default index 848ed784e..e6bf26330 100644 --- a/config.yml.default +++ b/config.yml.default @@ -133,6 +133,6 @@ matchmaking: challenge_initial_time: 60 # Initial time in seconds of the challenge. challenge_increment: 3 # Increment in seconds of the challenge. # challenge_days: 2 # Days for correspondence challenge. If this option is enabled, a correspondence challenge will be created even if 'challenge_initial_time' is enabled. - opponent_min_rating: 2000 # Opponents rating should be above this value - opponent_max_rating: 3000 # Opponents rating should be below this value + opponent_min_rating: 600 # Opponents rating should be above this value (600 is the minimum rating in lichess). + opponent_max_rating: 4000 # Opponents rating should be below this value (4000 is the maximum rating in lichess). challenge_mode: "random" # Set it to the mode in which challenges are sent. Possible options are 'casual', 'rated' and 'random'. diff --git a/matchmaking.py b/matchmaking.py index a337d2710..140d72531 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -54,8 +54,8 @@ def choose_opponent(self): else: game_type = "classical" - min_rating = self.matchmaking_cfg.get("opponent_min_rating") or 2000 - max_rating = self.matchmaking_cfg.get("opponent_max_rating") or 3000 + min_rating = self.matchmaking_cfg.get("opponent_min_rating", 600) + max_rating = self.matchmaking_cfg.get("opponent_max_rating", 4000) online_bots = self.li.get_online_bots() online_bots = list(filter(lambda bot: bot["username"] != self.username and not bot.get("disabled") and From e198064985b97134bd9cd890a4c6b80301ab9898 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Fri, 13 May 2022 14:05:10 +0300 Subject: [PATCH 17/27] Change treatment of 0 in max rating --- matchmaking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matchmaking.py b/matchmaking.py index 140d72531..7f7b101cb 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -55,7 +55,7 @@ def choose_opponent(self): game_type = "classical" min_rating = self.matchmaking_cfg.get("opponent_min_rating", 600) - max_rating = self.matchmaking_cfg.get("opponent_max_rating", 4000) + max_rating = self.matchmaking_cfg.get("opponent_max_rating") or 4000 online_bots = self.li.get_online_bots() online_bots = list(filter(lambda bot: bot["username"] != self.username and not bot.get("disabled") and From 961489435d795d04cf5abced790bb7f28d1066c8 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Fri, 13 May 2022 14:07:52 +0300 Subject: [PATCH 18/27] Change treatment of 0 in min rating --- matchmaking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/matchmaking.py b/matchmaking.py index 7f7b101cb..c1a77cb1f 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -54,7 +54,7 @@ def choose_opponent(self): else: game_type = "classical" - min_rating = self.matchmaking_cfg.get("opponent_min_rating", 600) + min_rating = self.matchmaking_cfg.get("opponent_min_rating") or 600 max_rating = self.matchmaking_cfg.get("opponent_max_rating") or 4000 online_bots = self.li.get_online_bots() From 49e400ddaa26378113317f639742a26a95c530fc Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sun, 15 May 2022 11:32:44 +0300 Subject: [PATCH 19/27] Cancel challenge after 25 seconds Challenges are automatically canceled after 20 seconds except for correspondence challenges. After 25 seconds we send cancel_challenge to cancel correspondence challenges. --- lichess.py | 6 +++++- matchmaking.py | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lichess.py b/lichess.py index a0785ba8d..3f9fefd06 100644 --- a/lichess.py +++ b/lichess.py @@ -22,7 +22,8 @@ "resign": "/api/bot/game/{}/resign", "export": "/game/export/{}", "online_bots": "/api/bot/online", - "challenge": "/api/challenge/{}" + "challenge": "/api/challenge/{}", + "cancel": "/api/challenge/{}/cancel" } @@ -132,3 +133,6 @@ def get_online_bots(self): def challenge(self, username, params): return self.api_post(ENDPOINTS["challenge"].format(username), payload=params, raise_for_status=False) + + def cancel(self, challenge_id): + return self.api_post(ENDPOINTS["cancel"].format(challenge_id), raise_for_status=False) diff --git a/matchmaking.py b/matchmaking.py index c1a77cb1f..ba4c49efc 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -19,6 +19,8 @@ def should_create_challenge(self): matchmaking_enabled = self.matchmaking_cfg.get("allow_matchmaking") time_has_passed = self.last_challenge_created + ((self.matchmaking_cfg.get("challenge_interval") or 30) * 60) < time.time() challenge_expired = self.last_challenge_created + self.challenge_expire_time < time.time() and self.challenge_id + if challenge_expired: + self.li.cancel(self.challenge_id) return matchmaking_enabled and (time_has_passed or challenge_expired) def create_challenge(self, username, base_time, increment, days, variant): From 50b4d8dc9f71e252b263de2d1b2566f25af9b23e Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sun, 15 May 2022 11:37:47 +0300 Subject: [PATCH 20/27] Wait 10 seconds to not hit api rate limits --- matchmaking.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/matchmaking.py b/matchmaking.py index ba4c49efc..cf228b4ef 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -19,9 +19,11 @@ def should_create_challenge(self): matchmaking_enabled = self.matchmaking_cfg.get("allow_matchmaking") time_has_passed = self.last_challenge_created + ((self.matchmaking_cfg.get("challenge_interval") or 30) * 60) < time.time() challenge_expired = self.last_challenge_created + self.challenge_expire_time < time.time() and self.challenge_id + # Wait 10 seconds before creating a new challenge to avoid hitting the api rate limits. + ten_seconds_passed = self.last_challenge_created + 10 < time.time() if challenge_expired: self.li.cancel(self.challenge_id) - return matchmaking_enabled and (time_has_passed or challenge_expired) + return matchmaking_enabled and (time_has_passed or challenge_expired) and ten_seconds_passed def create_challenge(self, username, base_time, increment, days, variant): mode = self.matchmaking_cfg.get("challenge_mode") or "random" From c3f58cb4508b2e99949c5a15e84755628295a2a0 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sun, 15 May 2022 19:49:03 +0300 Subject: [PATCH 21/27] Minor changes --- matchmaking.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/matchmaking.py b/matchmaking.py index cf228b4ef..4a935de89 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -23,6 +23,7 @@ def should_create_challenge(self): ten_seconds_passed = self.last_challenge_created + 10 < time.time() if challenge_expired: self.li.cancel(self.challenge_id) + logger.debug(f"Challenge id {self.challenge_id} cancelled.") return matchmaking_enabled and (time_has_passed or challenge_expired) and ten_seconds_passed def create_challenge(self, username, base_time, increment, days, variant): @@ -40,7 +41,9 @@ def create_challenge(self, username, base_time, increment, days, variant): return challenge_id def choose_opponent(self): - variant = random.choice(self.variants) + variant = self.matchmaking_cfg.get("challenge_variant") or "random" + if variant == "random": + variant = random.choice(self.variants) base_time = self.matchmaking_cfg.get("challenge_initial_time", 60) increment = self.matchmaking_cfg.get("challenge_increment", 2) days = self.matchmaking_cfg.get("challenge_days") @@ -70,9 +73,9 @@ def choose_opponent(self): def challenge(self): bot_username, base_time, increment, days, variant = self.choose_opponent() - logger.debug(f"Will challenge {bot_username} for a {variant} game.") + logger.info(f"Will challenge {bot_username} for a {variant} game.") challenge_id = self.create_challenge(bot_username, base_time, increment, days, variant) if bot_username else None - logger.debug(f"Challenge id is {challenge_id}.") + logger.info(f"Challenge id is {challenge_id}.") if challenge_id: self.last_challenge_created = time.time() self.challenge_id = challenge_id From 0a4069b9fd48de93cbe73336a197ba2cb36c7f2c Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sun, 15 May 2022 19:53:02 +0300 Subject: [PATCH 22/27] Add 'challenge_variant' --- config.yml.default | 1 + 1 file changed, 1 insertion(+) diff --git a/config.yml.default b/config.yml.default index e6bf26330..12111e3a0 100644 --- a/config.yml.default +++ b/config.yml.default @@ -129,6 +129,7 @@ greeting: matchmaking: allow_matchmaking: false # Set it to 'true' to challenge other bot in a set interval, time control and range. + challenge_variant: "random" # If set to 'random', the bot will choose one variant from the variants enabled in 'challenge.variants'. challenge_interval: 30 # Interval in minutes between two challenges. challenge_initial_time: 60 # Initial time in seconds of the challenge. challenge_increment: 3 # Increment in seconds of the challenge. From a730a22af6b1100bbc0f60b984d4121f8a1f0230 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sun, 15 May 2022 19:55:46 +0300 Subject: [PATCH 23/27] Add 'challenge_variant' --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 114891bd6..87a96061c 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,7 @@ will precede the `go` command to start thinking with `sd 5`. The other `go_comma ``` - `matchmaking`: Challenge a random bot. - `allow_matchmaking`: Whether to challenge other bots. + - `challenge_variant`: The variant for the challenges. If set to `random` a variant from the ones enabled in `challenge.variants` will be chosen on random. - `challenge_interval`: How often to challenge a bot (in minutes). - `challenge_initial_time`: The initial time (in seconds) for the challenges. - `challenge_increment`: The increment (in seconds) for the challenges. @@ -243,6 +244,7 @@ will precede the `go` command to start thinking with `sd 5`. The other `go_comma ```yml matchmaking: allow_matchmaking: false + challenge_variant: "random" challenge_interval: 30 challenge_initial_time: 60 challenge_increment: 3 From 0e32b1c70099c403b7587d35ad9d1a3abaca9b81 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Thu, 19 May 2022 19:14:04 +0300 Subject: [PATCH 24/27] Remove if to avoid hitting the api rate limits Always update self.last_challenge_created to avoid hitting the api rate limits. --- matchmaking.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/matchmaking.py b/matchmaking.py index 4a935de89..3b33de137 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -76,6 +76,5 @@ def challenge(self): logger.info(f"Will challenge {bot_username} for a {variant} game.") challenge_id = self.create_challenge(bot_username, base_time, increment, days, variant) if bot_username else None logger.info(f"Challenge id is {challenge_id}.") - if challenge_id: - self.last_challenge_created = time.time() + self.last_challenge_created = time.time() self.challenge_id = challenge_id From b8075756548bb194a5a2c0791b693e74c80f44ca Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sun, 22 May 2022 09:36:16 +0300 Subject: [PATCH 25/27] Increase time between challenges from 10 to 20 Increase time between challenges from 10 to 20 seconds. --- matchmaking.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/matchmaking.py b/matchmaking.py index 3b33de137..a2eabde6f 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -19,12 +19,12 @@ def should_create_challenge(self): matchmaking_enabled = self.matchmaking_cfg.get("allow_matchmaking") time_has_passed = self.last_challenge_created + ((self.matchmaking_cfg.get("challenge_interval") or 30) * 60) < time.time() challenge_expired = self.last_challenge_created + self.challenge_expire_time < time.time() and self.challenge_id - # Wait 10 seconds before creating a new challenge to avoid hitting the api rate limits. - ten_seconds_passed = self.last_challenge_created + 10 < time.time() + # Wait 20 seconds before creating a new challenge to avoid hitting the api rate limits. + twenty_seconds_passed = self.last_challenge_created + 20 < time.time() if challenge_expired: self.li.cancel(self.challenge_id) logger.debug(f"Challenge id {self.challenge_id} cancelled.") - return matchmaking_enabled and (time_has_passed or challenge_expired) and ten_seconds_passed + return matchmaking_enabled and (time_has_passed or challenge_expired) and twenty_seconds_passed def create_challenge(self, username, base_time, increment, days, variant): mode = self.matchmaking_cfg.get("challenge_mode") or "random" From af6ea5b1e1e4dff686e9ee54449692087a8340a5 Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Fri, 27 May 2022 13:03:31 +0300 Subject: [PATCH 26/27] Use challenge timeout instead of challenge interval --- README.md | 4 ++-- config.yml.default | 4 ++-- lichess-bot.py | 3 ++- matchmaking.py | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 87a96061c..88f8bfd49 100644 --- a/README.md +++ b/README.md @@ -234,7 +234,7 @@ will precede the `go` command to start thinking with `sd 5`. The other `go_comma - `matchmaking`: Challenge a random bot. - `allow_matchmaking`: Whether to challenge other bots. - `challenge_variant`: The variant for the challenges. If set to `random` a variant from the ones enabled in `challenge.variants` will be chosen on random. - - `challenge_interval`: How often to challenge a bot (in minutes). + - `challenge_timeout`: The time (in minutes) the bot has to be idle before it creates a challenge. - `challenge_initial_time`: The initial time (in seconds) for the challenges. - `challenge_increment`: The increment (in seconds) for the challenges. - `challenge_days`: The days for a correspondence challenge. If this option is enabled, a correspondence challenge will be created even if `challenge_initial_time` is enabled. @@ -245,7 +245,7 @@ will precede the `go` command to start thinking with `sd 5`. The other `go_comma matchmaking: allow_matchmaking: false challenge_variant: "random" - challenge_interval: 30 + challenge_timeout: 30 challenge_initial_time: 60 challenge_increment: 3 # challenge_days: 2 diff --git a/config.yml.default b/config.yml.default index 12111e3a0..cd906f22b 100644 --- a/config.yml.default +++ b/config.yml.default @@ -128,9 +128,9 @@ greeting: # pgn_directory: "game_records" # A directory where PGN-format records of the bot's games are kept matchmaking: - allow_matchmaking: false # Set it to 'true' to challenge other bot in a set interval, time control and range. + allow_matchmaking: false # Set it to 'true' to challenge other bots. challenge_variant: "random" # If set to 'random', the bot will choose one variant from the variants enabled in 'challenge.variants'. - challenge_interval: 30 # Interval in minutes between two challenges. + challenge_timeout: 30 # Create a challenge after being idle for 'challenge_timeout' minutes. challenge_initial_time: 60 # Initial time in seconds of the challenge. challenge_increment: 3 # Increment in seconds of the challenge. # challenge_days: 2 # Days for correspondence challenge. If this option is enabled, a correspondence challenge will be created even if 'challenge_initial_time' is enabled. diff --git a/lichess-bot.py b/lichess-bot.py index 44080aa19..06fd3cfa8 100644 --- a/lichess-bot.py +++ b/lichess-bot.py @@ -147,6 +147,7 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) break elif event["type"] == "local_game_done": busy_processes -= 1 + matchmaking.last_game_ended = time.time() logger.info(f"+++ Process Free. Total Queued: {queued_processes}. Total Used: {busy_processes}") if one_game: break @@ -224,7 +225,7 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) logger.info(f"Skip missing {chlng}") queued_processes -= 1 - if queued_processes + busy_processes < max_games and not challenge_queue and matchmaker.should_create_challenge(): + if queued_processes + busy_processes < min(max_games, 1) and not challenge_queue and matchmaker.should_create_challenge(): logger.info("Challenging a random bot") matchmaker.challenge() diff --git a/matchmaking.py b/matchmaking.py index a2eabde6f..0f78d79fd 100644 --- a/matchmaking.py +++ b/matchmaking.py @@ -12,12 +12,13 @@ def __init__(self, li, config, username): self.matchmaking_cfg = config.get("matchmaking") or {} self.username = username self.last_challenge_created = time.time() + self.last_game_ended = time.time() self.challenge_expire_time = 25 # The challenge expires 20 seconds after creating it. self.challenge_id = None def should_create_challenge(self): matchmaking_enabled = self.matchmaking_cfg.get("allow_matchmaking") - time_has_passed = self.last_challenge_created + ((self.matchmaking_cfg.get("challenge_interval") or 30) * 60) < time.time() + time_has_passed = self.last_game_ended + ((self.matchmaking_cfg.get("challenge_timeout") or 30) * 60) < time.time() challenge_expired = self.last_challenge_created + self.challenge_expire_time < time.time() and self.challenge_id # Wait 20 seconds before creating a new challenge to avoid hitting the api rate limits. twenty_seconds_passed = self.last_challenge_created + 20 < time.time() From 6d629ea4cd844332b617324de24d7e1b1c75d13a Mon Sep 17 00:00:00 2001 From: AttackingOrDefending <40605232+AttackingOrDefending@users.noreply.github.com> Date: Sat, 4 Jun 2022 14:30:39 +0300 Subject: [PATCH 27/27] Change matchmaking to matchmaker --- lichess-bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lichess-bot.py b/lichess-bot.py index 06fd3cfa8..9be0e99f5 100644 --- a/lichess-bot.py +++ b/lichess-bot.py @@ -147,7 +147,7 @@ def start(li, user_profile, config, logging_level, log_filename, one_game=False) break elif event["type"] == "local_game_done": busy_processes -= 1 - matchmaking.last_game_ended = time.time() + matchmaker.last_game_ended = time.time() logger.info(f"+++ Process Free. Total Queued: {queued_processes}. Total Used: {busy_processes}") if one_game: break