Skip to content

Commit

Permalink
Add max simultaneous games per user option (#1061)
Browse files Browse the repository at this point in the history
A bot can now limit the number of games it is playing at a given time with the same user.
  • Loading branch information
AttackingOrDefending authored Jan 3, 2025
1 parent 9374052 commit 00f2b8e
Show file tree
Hide file tree
Showing 6 changed files with 15 additions and 7 deletions.
1 change: 1 addition & 0 deletions config.yml.default
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ challenge: # Incoming challenges.
# recent_bot_challenge_age: 60 # Maximum age of a bot challenge to be considered recent in seconds
# max_recent_bot_challenges: 2 # Maximum number of recent challenges that can be accepted from the same bot
bullet_requires_increment: false # Require that bullet game challenges from bots have a non-zero increment
max_simultaneous_games_per_user: 5 # Maximum number of simultaneous games with the same user

greeting:
# Optional substitution keywords (include curly braces):
Expand Down
1 change: 1 addition & 0 deletions lib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ def insert_default_values(CONFIG: CONFIG_DICT_TYPE) -> None:
set_config_default(CONFIG, "challenge", key="min_days", default=1)
set_config_default(CONFIG, "challenge", key="block_list", default=[], force_empty_values=True)
set_config_default(CONFIG, "challenge", key="allow_list", default=[], force_empty_values=True)
set_config_default(CONFIG, "challenge", key="max_simultaneous_games_per_user", default=5)
set_config_default(CONFIG, "correspondence", key="checkin_period", default=600)
set_config_default(CONFIG, "correspondence", key="move_time", default=60, force_empty_values=True)
set_config_default(CONFIG, "correspondence", key="disconnect_time", default=300)
Expand Down
5 changes: 4 additions & 1 deletion lib/lichess_bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from http.client import RemoteDisconnected
from queue import Empty
from multiprocessing.pool import Pool
from collections import Counter
from typing import Optional, Union, TypedDict, cast
from types import FrameType
MULTIPROCESSING_LIST_TYPE = MutableSequence[model.Challenge]
Expand Down Expand Up @@ -614,7 +615,9 @@ def handle_challenge(event: EventType, li: LICHESS_TYPE, challenge_queue: MULTIP
if chlng.from_self:
return

is_supported, decline_reason = chlng.is_supported(challenge_config, recent_bot_challenges)
players_with_active_games = Counter(game["opponent"]["username"] for game in li.get_ongoing_games())

is_supported, decline_reason = chlng.is_supported(challenge_config, recent_bot_challenges, players_with_active_games)
if is_supported:
challenge_queue.append(chlng)
sort_challenges(challenge_queue, challenge_config)
Expand Down
8 changes: 5 additions & 3 deletions lib/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from enum import Enum
from lib.timer import Timer, msec, seconds, sec_str, to_msec, to_seconds, years
from lib.config import Configuration
from collections import defaultdict
from collections import defaultdict, Counter
from lib.types import UserProfileType, ChallengeType, GameEventType, PlayerType

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -91,8 +91,8 @@ def decline_due_to(self, requirement_met: bool, decline_reason: str) -> str:
"""
return "" if requirement_met else decline_reason

def is_supported(self, config: Configuration,
recent_bot_challenges: defaultdict[str, list[Timer]]) -> tuple[bool, str]:
def is_supported(self, config: Configuration, recent_bot_challenges: defaultdict[str, list[Timer]],
players_with_active_games: Counter[str]) -> tuple[bool, str]:
"""Whether the challenge is supported."""
try:
if self.from_self:
Expand All @@ -109,6 +109,8 @@ def is_supported(self, config: Configuration,
or self.decline_due_to(self.challenger.name not in config.block_list, "generic")
or self.decline_due_to(self.challenger.name in allowed_opponents, "generic")
or self.decline_due_to(self.is_supported_recent(config, recent_bot_challenges), "later")
or self.decline_due_to(players_with_active_games[self.challenger.name]
< config.max_simultaneous_games_per_user, "later")
or self.decline_due_to(is_supported_extra(self), "generic"))

return not decline_reason, decline_reason
Expand Down
6 changes: 3 additions & 3 deletions test_bot/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from lib import model
import yaml
from lib import config
from collections import defaultdict
from collections import defaultdict, Counter
from lib.timer import Timer
from lib.types import ChallengeType, UserProfileType, GameEventType, PlayerType

Expand Down Expand Up @@ -52,10 +52,10 @@ def test_challenge() -> None:
assert challenge_model.speed == "bullet"
assert challenge_model.time_control["show"] == "1.5+1"
assert challenge_model.color == "white"
assert challenge_model.is_supported(configuration, recent_challenges) == (True, "")
assert challenge_model.is_supported(configuration, recent_challenges, Counter()) == (True, "")

CONFIG["challenge"]["min_base"] = 120
assert challenge_model.is_supported(configuration, recent_challenges) == (False, "timeControl")
assert challenge_model.is_supported(configuration, recent_challenges, Counter()) == (False, "timeControl")


def test_game() -> None:
Expand Down
1 change: 1 addition & 0 deletions wiki/Configure-lichess-bot.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ will precede the `go` command to start thinking with `sd 5`. The other `go_comma
- `allow_list`: An indented list of usernames from which challenges are exclusively accepted. A challenge from a user not on this list is declined. If this option is not present or empty, any user's challenge may be accepted.
- `recent_bot_challenge_age`: Maximum age of a bot challenge to be considered recent in seconds
- `max_recent_bot_challenges`: Maximum number of recent challenges that can be accepted from the same bot
- `max_simultaneous_games_per_user`: Maximum number of games that can be played simultaneously with the same user

## Greeting
- `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 `}}`.
Expand Down

0 comments on commit 00f2b8e

Please sign in to comment.