Allow EloTracker to run matches for multiple games

bram/mute
Bram van den Heuvel 2026-07-01 17:23:14 +02:00
parent e2daa9a205
commit ca7bfdc50a
2 changed files with 32 additions and 14 deletions

8
elo.py
View File

@ -3,7 +3,7 @@
""" """
from elo_tracker import EloTracker from elo_tracker import EloTracker
from pyclient.games import TicTacToe from pyclient.games import RoyalGameOfUr, TicTacToe
GAME_FILE = "games.jsonl" GAME_FILE = "games.jsonl"
PLAYER_FILE = "known_players.json" PLAYER_FILE = "known_players.json"
@ -21,6 +21,12 @@ def main() -> int:
player_count=2, player_count=2,
) )
tracker.start_periodic_matches(
game=RoyalGameOfUr.empty(),
interval_seconds=9 * 60,
player_count=2,
)
try: try:
tracker.start_server(import_name=__name__) tracker.start_server(import_name=__name__)
except KeyboardInterrupt: except KeyboardInterrupt:

View File

@ -16,6 +16,7 @@ from typing import Any, Sequence
import agents import agents
import pyclient import pyclient
import time
from pyclient.games import FinishState, Game from pyclient.games import FinishState, Game
@ -280,7 +281,7 @@ class EloTracker:
# Threading variables # Threading variables
self.__lock = threading.RLock() self.__lock = threading.RLock()
self.__scheduler_stop = threading.Event() self.__scheduler_stop = threading.Event()
self.__scheduler_thread: threading.Thread | None = None self.__scheduler_threads: list[threading.Thread] = []
# Immutable values # Immutable values
self.debug: bool = debug self.debug: bool = debug
@ -551,7 +552,11 @@ class EloTracker:
:rtype: bool :rtype: bool
""" """
with self.__lock: with self.__lock:
return self.__scheduler_thread is not None and self.__scheduler_thread.is_alive() for thread in self.__scheduler_threads:
if thread.is_alive():
return True
else:
return False
def load_players(self) -> None: def load_players(self) -> None:
""" """
@ -665,33 +670,40 @@ class EloTracker:
raise ValueError("player_count must be greater than one.") raise ValueError("player_count must be greater than one.")
with self.__lock: with self.__lock:
if self.is_running_periodic_matches():
self.stop_periodic_matches()
self.__scheduler_stop.clear() self.__scheduler_stop.clear()
self.__scheduler_thread = threading.Thread( thread = threading.Thread(
target=self.__scheduler_loop, target=self.__scheduler_loop,
args=(game, interval_seconds, player_count), args=(game, interval_seconds, player_count),
daemon=True, daemon=True,
) )
self.__scheduler_thread.start() self.__scheduler_threads.append(thread)
thread.start()
def stop_periodic_matches(self) -> None: def stop_periodic_matches(self) -> None:
""" """
Stop the periodic match scheduler and wait briefly for it to exit. Stop the periodic match scheduler and wait briefly for it to exit.
""" """
thread: threading.Thread | None max_wait_time_s = 5
now = time.time()
time_left = lambda : max(0.1, now + max_wait_time_s - time.time())
to_remove : list[threading.Thread] = []
# Announce that all threads need to be stopped
with self.__lock: with self.__lock:
self.__scheduler_stop.set() self.__scheduler_stop.set()
thread = self.__scheduler_thread to_remove = [ thread for thread in self.__scheduler_threads ]
# Then, quietly remove all existing threads
for thread in self.__scheduler_threads:
if thread is not None: if thread is not None:
thread.join(timeout=5) thread.join(timeout=time_left())
# Remove all stopped threads
# Keep in mind that another threads might've added new threads in the
# meantime.
with self.__lock: with self.__lock:
if self.__scheduler_thread is thread: for thread in to_remove:
self.__scheduler_thread = None self.__scheduler_threads.remove(thread)
def get_json_players(self, game_name : str) -> list[dict[str, Any]]: def get_json_players(self, game_name : str) -> list[dict[str, Any]]:
""" """