def test_on_briefing_tweak(self): logger = get_logger('steering optimization') # We want to assert that this constant is better than other constants. # Hold the setup_manager here such that we don't need to shutdown/relaunch everything all the time. with setup_manager_context() as setup_manager: def time_to_goal(steering_coefficient: float) -> float: ex = TurnAndDriveToBall( name= f'Turn to ball (steering_coefficient={steering_coefficient:.2f})', steering_coefficient=steering_coefficient) result = list(run_playlist([ex], setup_manager=setup_manager))[0] grade = result.grade assert isinstance( grade, GameTickPacketWrapperGrader.WrappedPass) or isinstance( grade, GameTickPacketWrapperGrader.WrappedFail ), f'Unexpected grade: {grade}' duration_seconds = grade.last_tick.game_info.seconds_elapsed - grade.first_tick.game_info.seconds_elapsed logger.debug(f'intermediate result: {duration_seconds}') return duration_seconds result = naive_function_minimization(time_to_goal, .4, 9) logger.debug('Best steering_coefficient: ', result.best_input) logger.debug('all_samples:') for k, v in sorted(result.all_samples.items()): logger.debug(f'{k},{v}') self.assertLess(0.4, result.best_input) self.assertLess(1.0, result.best_output) self.assertLess(result.best_output, 4.0)
def test_render_call(self): test_self = self class RenderTestExercise(Exercise): def get_name(self): return 'RenderTestExercise' def get_match_config(self) -> MatchConfig: return read_match_config_from_file(Path(__file__).parent / 'training_test.cfg') def setup(self, rng: random.Random) -> GameState: self.num_render_calls = 0 self.num_on_tick_calls = 0 return GameState() def on_tick(self, game_tick_packet: GameTickPacket) -> Optional[Result]: self.num_on_tick_calls += 1 if self.num_on_tick_calls >= 100: nonlocal test_self test_self.assertEqual(self.num_on_tick_calls-1, self.num_render_calls) return Pass() def render(self, renderer: RenderingManager): self.num_render_calls += 1 with setup_manager_context() as setup_manager: results = list(run_exercises(setup_manager, [RenderTestExercise()], 4)) self.assertEqual(len(results), 1) result = results[0] self.assertIsInstance(result.grade, Pass)
def run_module(python_file_with_playlist: Path, history_dir: Optional[Path] = None, reload_policy=ReloadPolicy.EACH_EXERCISE, render_policy=RenderPolicy.DEFAULT): """ This function repeatedly runs exercises in the module and reloads the module to pick up any new changes to the Exercise. e.g. make_game_state() can be updated or you could implement a new Grader without needing to terminate the training. """ # load the playlist initially, keep trying if we fail playlist_factory = None playlist: Playlist = None while playlist is None: try: playlist_factory = load_default_playlist(python_file_with_playlist) playlist = playlist_factory() except Exception: traceback.print_exc() time.sleep(1.0) log = get_logger(LOGGER_ID) with setup_manager_context() as setup_manager: apply_render_policy(render_policy, setup_manager) for seed in infinite_seed_generator(): playlist = playlist_factory() wrapped_exercises = [TrainingExerciseAdapter(ex) for ex in playlist] result_iter = rlbot_run_exercises(setup_manager, wrapped_exercises, seed) for i, rlbot_result in enumerate(result_iter): result = ExerciseResult( grade=rlbot_result.grade, exercise=rlbot_result.exercise.exercise, # unwrap the TrainingExerciseAdapter. reproduction_info=ReproductionInfo( seed=seed, python_file_with_playlist=str(python_file_with_playlist.absolute()), playlist_index=i, ) ) log_result(result, log) if history_dir: store_result(result, history_dir) # Reload the module and apply the new exercises if reload_policy == ReloadPolicy.EACH_EXERCISE: try: new_playlist_factory = load_default_playlist(python_file_with_playlist) new_playlist = new_playlist_factory() except Exception: traceback.print_exc() continue # keep running previous exercises until new ones are fixed. playlist_factory = new_playlist_factory if len(new_playlist) != len(playlist) or any(e1.name != e2.name for e1,e2 in zip(new_playlist, playlist)): log.warning(f'Need to restart to pick up new exercises.') playlist = new_playlist break # different set of exercises. Can't monkeypatch. for new_exercise, old_exercise in zip(new_playlist, playlist): _monkeypatch_copy(new_exercise, old_exercise)
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 match_running(start_event: Event, stop_event: Event, exit_event: Event): with setup_manager_context() as manager: while True: if start_event.is_set(): start_match(manager) start_event.clear() elif stop_event.is_set(): stop_match(manager) stop_event.clear() elif exit_event.is_set(): exit_event.clear() break
def run_playlist(playlist: Playlist, seed: int = 4) -> Iterator[ExerciseResult]: with setup_manager_context() as setup_manager: wrapped_exercises = [TrainingExerciseAdapter(ex) for ex in playlist] for i, rlbot_result in enumerate(rlbot_run_exercises(setup_manager, wrapped_exercises, seed)): yield ExerciseResult( grade=rlbot_result.grade, exercise=rlbot_result.exercise.exercise, # unwrap the TrainingExerciseAdapter. reproduction_info=ReproductionInfo( seed=seed, playlist_index=i, ) )
def run_test_match(participant_1: str, participant_2: str, match_config) -> Optional[Grade]: # Play the match print(f'Starting test match: {participant_1} vs {participant_2}...') match = MatchExercise(name=f'{participant_1} vs {participant_2}', match_config=match_config, grader=AliveGrader()) with setup_manager_context() as setup_manager: # 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): return exercise_result.grade
def test_run_exercises(self): ownGoalExercise = BallInFrontOfCar('BallInFrontOfCar(facing own goal)', Vector3(0, -4000, 0), ball_location=Vector3(0,-4500,100)) seed = 4 with setup_manager_context() as setup_manager: result_iter = run_exercises( setup_manager, [ BallInFrontOfCar('BallInFrontOfCar(goal 1)', Vector3(0, 3500, 0)), BallInFrontOfCar('BallInFrontOfCar(goal 2)', Vector3(1000, 3500, 0)), ownGoalExercise, BallInFrontOfCar('BallInFrontOfCar(sideways)', Vector3(-1500, 0, 0), ball_location=Vector3(1500, 0, 100)), ], seed ) result = next(result_iter) self.assertEqual(result.exercise.get_name(), 'BallInFrontOfCar(goal 1)') self.assertIsInstance(result.grade, Pass) result = next(result_iter) self.assertEqual(result.exercise.get_name(), 'BallInFrontOfCar(goal 2)') self.assertIsInstance(result.grade, Pass) result = next(result_iter) self.assertEqual(result.exercise.get_name(), 'BallInFrontOfCar(facing own goal)') self.assertIsInstance(result.grade, Fail) self.assertIsInstance(result.grade, FailWithReason) self.assertEqual(result.grade.reason, 'own goal') self.assertIs(result.exercise, ownGoalExercise) self.assertIsInstance(result.seed, int) result = next(result_iter) self.assertEqual(result.exercise.get_name(), 'BallInFrontOfCar(sideways)') self.assertIsInstance(result.grade, Fail) self.assertIsInstance(result.grade, FailDueToExerciseException) self.assertIsInstance(result.grade.exception, Exception) self.assertIsInstance(result.grade.exception, ArithmeticError) # 1/0 try: next(result_iter) self.Fail('expected the result_iter to be finished.') except StopIteration: pass
def run_match(participant_1: str, participant_2: str, match_config, replay_preference: ReplayPreference) -> MatchResult: with setup_manager_context() as setup_manager: # Prepare the match exercise print( f'Starting match: {participant_1} vs {participant_2}. Waiting for match to finish...' ) match = MatchExercise( name=f'{participant_1} vs {participant_2}', match_config=match_config, grader=MatchGrader( mercy_rule=MercyRule( game_interface=setup_manager.game_interface), 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.NO_TRAINING_RENDER): # Warn users if no replay was found if isinstance( exercise_result.grade, Fail ) and exercise_result.exercise.grader.replay_monitor.replay_id == None: print( f'WARNING: No replay was found for the match \'{participant_1} vs {participant_2}\'. Is Bakkesmod injected and \'Automatically save all replays\' enabled?' ) return exercise_result.exercise.grader.match_result
def run_league_play(working_dir: WorkingDir, odd_week: bool, replay_preference: ReplayPreference): """ Run a league play event by running round robins for half the divisions. When done, a new ladder file is created. """ bots = load_all_bots(working_dir) ladder = Ladder.read(working_dir.ladder) # We need the result of every match to create the next ladder. For each match in each round robin, if a result # exist already, it will be parsed, if it doesn't exist, it will be played. # When all results have been found, the new ladder can be completed and saved. new_ladder = Ladder(ladder.bots) event_results = [] # playing_division_indices contains either even or odd indices. # If there is only one division always play that division (division 0, quantum). playing_division_indices = range( ladder.division_count())[int(odd_week) % 2::2] if ladder.division_count() > 1 else [0] # The divisions play in reverse order, so quantum/overclocked division plays last for div_index in playing_division_indices[::-1]: print( f'Starting round robin for the {Ladder.DIVISION_NAMES[div_index]} division' ) rr_bots = ladder.round_robin_participants(div_index) rr_matches = generate_round_robin_matches(rr_bots) rr_results = [] for match_participants in rr_matches: # Check if match has already been play, i.e. the result file already exist result_path = working_dir.get_match_result(div_index, match_participants[0], match_participants[1]) if result_path.exists(): # Found existing result try: print(f'Found existing result {result_path.name}') result = MatchResult.read(result_path) rr_results.append(result) except Exception as e: print( f'Error loading result {result_path.name}. Fix/delete the result and run script again.' ) raise e else: assert match_participants[ 0] in bots, f'{match_participants[0]} was not found in \'{working_dir.bots}\'' assert match_participants[ 1] in bots, f'{match_participants[1]} was not found in \'{working_dir.bots}\'' # Play the match print( f'Starting match: {match_participants[0]} vs {match_participants[1]}. Waiting for match to finish...' ) match_config = make_match_config(working_dir, bots[match_participants[0]], bots[match_participants[1]]) match = MatchExercise( name=f'{match_participants[0]} vs {match_participants[1]}', match_config=match_config, grader=MatchGrader(replay_monitor=ReplayMonitor( replay_preference=replay_preference), )) # Let overlay know which match we are about to start overlay_data = OverlayData( div_index, bots[match_participants[0]].config_path, bots[match_participants[1]].config_path) overlay_data.write(working_dir.overlay_interface) with setup_manager_context() as setup_manager: # Disable rendering by replacing renderer with a renderer that does nothing setup_manager.game_interface.renderer = FakeRenderer() # For loop, but should only run exactly once for exercise_result in run_playlist( [match], setup_manager=setup_manager): # Warn users if no replay was found if isinstance( exercise_result.grade, Fail ) and exercise_result.exercise.grader.replay_monitor.replay_id == None: print( f'WARNING: No replay was found for the match \'{match_participants[0]} vs {match_participants[1]}\'. Is Bakkesmod injected and \'Automatically save all replays\' enabled?' ) # Save result in file result = exercise_result.exercise.grader.match_result result.write(result_path) print( f'Match finished {result.blue_goals}-{result.orange_goals}. Saved result as {result_path}' ) rr_results.append(result) # Let the winner celebrate and the scoreboard show for a few seconds. # This sleep not required. time.sleep(8) print(f'{Ladder.DIVISION_NAMES[div_index]} division done') event_results.append(rr_results) # Find bots' overall score for the round robin overall_scores = [ CombinedScore.calc_score(bot, rr_results) for bot in rr_bots ] sorted_overall_scores = sorted(overall_scores)[::-1] print( f'Bots\' overall performance in {Ladder.DIVISION_NAMES[div_index]} division:' ) for score in sorted_overall_scores: print( f'> {score.bot}: goal_diff={score.goal_diff}, goals={score.goals}, shots={score.shots}, saves={score.saves}, points={score.points}' ) # Rearrange bots in division on the new ladder first_bot_index = new_ladder.division_size * div_index bots_to_rearrange = len(rr_bots) for i in range(bots_to_rearrange): new_ladder.bots[first_bot_index + i] = sorted_overall_scores[i].bot # Save new ladder Ladder.write(new_ladder, working_dir.new_ladder) print(f'Done. Saved new ladder as {working_dir.new_ladder.name}') # Remove overlay interface file now that we are done if working_dir.overlay_interface.exists(): working_dir.overlay_interface.unlink() return new_ladder
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