def run_match( ld: LeagueDir, match_details: MatchDetails, bots: Mapping[BotID, BotConfigBundle], replay_preference: ReplayPreference ) -> Tuple[MatchResult, Optional[ReplayData]]: """ Run a match, wait for it to finish, and return the result. """ with setup_manager_context() as setup_manager: # Expose data to overlay make_overlay(ld, match_details, bots) # Prepare the match exercise print( f"Starting match: {match_details.blue} vs {match_details.orange}. Waiting for match to finish..." ) match = MatchExercise(name=match_details.name, match_config=match_details.to_config(bots), grader=MatchGrader(replay_monitor=ReplayMonitor( replay_preference=replay_preference), )) # If any bots have signed up for early start, give them 10 seconds. # This is typically enough for Scratch. setup_manager.early_start_seconds = 10 # For loop, but should only run exactly once for exercise_result in run_playlist( [match], setup_manager=setup_manager, render_policy=RenderPolicy.DEFAULT): replay_data = None # Warn if no replay was found replay_data = exercise_result.exercise.grader.replay_monitor.replay_data( ) if isinstance(exercise_result.grade, Fail) and replay_data.replay_id is None: print( f"WARNING: No replay was found for the match '{match_details.name}'." ) else: if replay_preference != ReplayPreference.NONE and replay_data.replay_path is not None: try: dst = ld.replays / f"{replay_data.replay_id}.replay" shutil.copy(replay_data.replay_path, dst) print( "Replay successfully copied to replays directory") except: pass match_result = confirm_match_result( exercise_result.exercise.grader.match_result) return match_result, replay_data
def load(ld: LeagueDir) -> 'TicketSystem': ticket_sys = TicketSystem() if any(ld.tickets.iterdir()): # Assume last tickets file is the newest, since they are prefixed with a time stamp with open(list(ld.tickets.iterdir())[-1]) as f: ticket_sys.tickets = json.load(f) settings = LeagueSettings.load(ld) ticket_sys.new_bot_ticket_count = settings.new_bot_ticket_count ticket_sys.ticket_increase_rate = settings.ticket_increase_rate ticket_sys.game_catchup_boost = settings.game_catchup_boost matches_in_session = MatchDetails.latest(ld, settings.last_summary) for match in matches_in_session: bots = match.blue + match.orange ticket_sys.ensure(bots) for bot_id in bots: ticket_sys.session_game_counts[bot_id] += 1 return ticket_sys
def make_next(bots: Mapping[BotID, BotConfigBundle], rank_sys: RankingSystem, ticket_sys: TicketSystem) -> MatchDetails: """ Make the next match to play. This will use to TicketSystem and the RankingSystem to find a fair match between some bots that haven't played for a while. It is assumed that the match is guaranteed to finish (since the TicketSystem is updated). """ time_stamp = make_timestamp() blue, orange = MatchMaker.decide_on_players(bots.keys(), rank_sys, ticket_sys) name = "_".join([time_stamp] + blue + ["vs"] + orange) map = choice([ "ChampionsField", "DFHStadium", "NeoTokyo", "ChampionsField", "UrbanCentral", "BeckwithPark", ]) return MatchDetails(time_stamp, name, blue, orange, map)
from pathlib import Path from matplotlib.colors import ListedColormap from match import MatchDetails from paths import LeagueDir from ranking_system import RankingSystem from settings import PersistentSettings settings = PersistentSettings.load() ld = LeagueDir(Path(settings.league_dir_raw)) ranks = RankingSystem.latest(ld, 1)[0] bots = sorted(ranks.ratings.keys(), key=lambda bot: -ranks.get_mmr(bot)) N = len(bots) matches = MatchDetails.all(ld) win_rate = np.full((N, N), -0.01) for i in range(N): for j in range(N): if i < j: wins_i = 0 wins_j = 0 for match in matches: if bots[i] in match.blue and bots[j] in match.orange: if match.result.blue_goals > match.result.orange_goals: wins_i += 1 else: wins_j += 1 elif bots[j] in match.blue and bots[i] in match.orange: if match.result.blue_goals < match.result.orange_goals:
def make_summary(ld: LeagueDir, count: int): """ Make a summary of the N latest matches and the resulting ranks and tickets. If N is 0 the summary will just contain the current ratings. """ summary = {} tickets = TicketSystem.load(ld) # ========== Matches ========== matches = [] bot_wins = defaultdict(list) # Maps bots to list of booleans, where true=win and false=loss if count > 0: latest_matches = MatchDetails.latest(ld, count) for i, match in enumerate(latest_matches): matches.append({ "index": i, "blue_names": [defmt_bot_name(bot_id) for bot_id in match.blue], "orange_names": [defmt_bot_name(bot_id) for bot_id in match.orange], "blue_goals": match.result.blue_goals, "orange_goals": match.result.orange_goals, }) for bot in match.blue: bot_wins[bot].append(match.result.blue_goals > match.result.orange_goals) for bot in match.orange: bot_wins[bot].append(match.result.blue_goals < match.result.orange_goals) summary["matches"] = matches # ========= Ranks/Ratings ========= bots = load_all_unretired_bots(ld) retired = load_retired_bots(ld) bots_by_rank = [] if count <= 0: # Old rankings and current rankings is the same, but make sure all bots have a rank currently old_rankings = RankingSystem.load(ld).as_sorted_list(exclude=retired) cur_rankings = RankingSystem.load(ld).ensure_all(list(bots.keys())).as_sorted_list(exclude=retired) else: # Determine current rank and their to N matches ago n_rankings = RankingSystem.latest(ld, count + 1) old_rankings = n_rankings[0].as_sorted_list(exclude=retired) cur_rankings = n_rankings[-1].ensure_all(list(bots.keys())).as_sorted_list(exclude=retired) for i, (bot, mrr, sigma) in enumerate(cur_rankings): cur_rank = i + 1 old_rank = None old_mmr = None for j, (other_bot, other_mrr, _) in enumerate(old_rankings): if bot == other_bot: old_rank = j + 1 old_mmr = other_mrr break bots_by_rank.append({ "bot_id": defmt_bot_name(bot), "mmr": mrr, "old_mmr": old_mmr, "sigma": sigma, "cur_rank": cur_rank, "old_rank": old_rank, "tickets": tickets.get(bot) or tickets.new_bot_ticket_count, "wins": bot_wins[bot], }) summary["bots_by_rank"] = bots_by_rank # =========== Write ============= with open(PackageFiles.overlay_summary, 'w') as f: json.dump(summary, f, indent=4) league_settings = LeagueSettings.load(ld) league_settings.last_summary = count league_settings.save(ld)
def make_test_match(bot_id: BotID) -> MatchDetails: allstar_config = get_bot_config_bundle(PackageFiles.psyonix_allstar) allstar_id = fmt_bot_name(allstar_config.name) team = [bot_id, allstar_id, allstar_id] return MatchDetails("", f"test_{bot_id}", team, team, "ChampionsField")
def parse_subcommand_match(args: List[str]): assert args[0] == "match" help_msg = """Usage: autoleague match run Run a standard 3v3 soccer match autoleague match undo Undo the last match autoleague match list [n] Show the latest matches""" ld = require_league_dir() if len(args) == 1 or args[1] == "help": print(help_msg) elif args[1] == "run" and len(args) == 2: # Load bots = load_all_bots(ld) rank_sys = RankingSystem.load(ld) ticket_sys = TicketSystem.load(ld) # Run match = MatchMaker.make_next(bots, rank_sys, ticket_sys) result, replay = run_match(ld, match, bots, ReplayPreference.SAVE) rank_sys.update(match, result) match.result = result match.replay_id = replay.replay_id # Save match.save(ld) rank_sys.save(ld, match.time_stamp) ticket_sys.save(ld, match.time_stamp) # Print new ranks rank_sys.print_ranks_and_mmr() # Make summary league_settings = LeagueSettings.load(ld) make_summary(ld, league_settings.last_summary + 1) print( f"Created summary of the last {league_settings.last_summary + 1} matches." ) elif args[1] == "undo" and len(args) == 2: # Undo latest match ld = require_league_dir() latest_matches = MatchDetails.latest(ld, 1) if len(latest_matches) == 0: print("No matches to undo") else: latest_match = latest_matches[0] # Prompt user print(f"Latest match was {latest_match.name}") if prompt_yes_no( "Are you sure you want to undo the latest match?"): # Undo latest update to all systems RankingSystem.undo(ld) TicketSystem.undo(ld) MatchDetails.undo(ld) # New latest match new_latest_match = MatchDetails.latest(ld, 1) if new_latest_match: print(f"Reverted to {new_latest_match[0].name}") else: print("Reverted to beginning of league (no matches left)") elif args[1] == "list" and len(args) <= 3: count = 999999 if len(args) == 3: count = int(args[2]) # Show list of latest n matches played latest_matches = MatchDetails.latest(ld, count) if len(latest_matches) == 0: print("No matches have been played yet.") else: print(f"Match history (latest {len(latest_matches)} matches):") for match in latest_matches: print( f"{match.time_stamp}: {', '.join(match.blue) + ' ':.<46} {match.result.blue_goals} VS {match.result.orange_goals} {' ' + ', '.join(match.orange):.>46}" ) else: print(help_msg)
def run_match( ld: LeagueDir, match_details: MatchDetails, bots: Mapping[BotID, BotConfigBundle], replay_preference: ReplayPreference ) -> Tuple[MatchResult, Optional[ReplayData]]: """ Run a match, wait for it to finish, and return the result. """ settings = PersistentSettings.load() with setup_manager_context(settings.launcher()) as setup_manager: # Expose data to overlay make_overlay(ld, match_details, bots) # Prepare the match exercise print( f"Starting match: {match_details.blue} vs {match_details.orange}. Waiting for match to finish..." ) match = MatchExercise(name=match_details.name, match_config=match_details.to_config(bots), grader=MatchGrader(replay_monitor=ReplayMonitor( replay_preference=replay_preference), )) # If any bots have signed up for early start, give them 10 seconds. # This is typically enough for Scratch. setup_manager.early_start_seconds = 10 # For loop, but should only run exactly once with use_or_create(setup_manager, setup_manager_context) as setup_manager: wrapped_exercises = [TrainingExerciseAdapter(match)] for rlbot_result in run_exercises(setup_manager, wrapped_exercises, 4, reload_agent=False): exercise_result = ExerciseResult( grade=rlbot_result.grade, exercise=rlbot_result.exercise. exercise, # unwrap the TrainingExerciseAdapter. reproduction_info=None) # Warn if no replay was found replay_data = exercise_result.exercise.grader.replay_monitor.replay_data( ) if isinstance(exercise_result.grade, Fail) and replay_data.replay_id is None: print( f"WARNING: No replay was found for the match '{match_details.name}'." ) else: if replay_preference != ReplayPreference.NONE and replay_data.replay_path is not None: try: dst = ld.replays / f"{replay_data.replay_id}.replay" shutil.copy(replay_data.replay_path, dst) print( "Replay successfully copied to replays directory" ) except: pass match_result = exercise_result.exercise.grader.match_result return match_result, replay_data