def parse_file(replay): replay_name = write_files_to_disk([replay])[0] replay = analyze_replay_file(os.path.join(folder_location, replay_name), os.path.join(folder_location, replay_name) + '.json') proto = replay.protobuf_game guid = proto.game_metadata.match_guid return replay, proto, guid
def _process(self, id_) -> AnalysisManager: if id_ in self.REPLAYS_MAP: return self.REPLAYS_MAP[id_] path = os.path.join(self.REPLAYS_DIR, id_ + '.replay') manager = carball.analyze_replay_file(path, "replay.json") self.REPLAYS_MAP[id_] = manager return manager
def parse_replay_task(self, fn, preserve_upload_date=False, custom_file_location: str = None, force_reparse=False): if custom_file_location is None: pickled = os.path.join(os.path.dirname(__file__), '..', '..', 'data', 'parsed', os.path.basename(fn)) else: pickled = os.path.join(custom_file_location, os.path.basename(fn)) if custom_file_location is None: failed_dir = os.path.join(os.path.dirname(os.path.dirname(pickled)), 'failed') else: failed_dir = custom_file_location if os.path.isfile(pickled) and not force_reparse: return # try: try: analysis_manager = analyze_replay_file(fn) # type: ReplayGame except Exception as e: if not os.path.isdir(failed_dir): os.makedirs(failed_dir) shutil.move(fn, os.path.join(failed_dir, os.path.basename(fn))) with open(os.path.join(failed_dir, os.path.basename(fn) + '.txt'), 'a') as f: f.write(str(e)) f.write(traceback.format_exc()) raise e with open(pickled + '.pts', 'wb') as fo: analysis_manager.write_proto_out_to_file(fo) with gzip.open(pickled + '.gzip', 'wb') as fo: analysis_manager.write_pandas_out_to_file(fo) g = analysis_manager.protobuf_game sess = self.session() game, player_games, players, teamstats = convert_pickle_to_db(g) add_objs_to_db(game, player_games, players, teamstats, sess, preserve_upload_date=preserve_upload_date) sess.commit() sess.close() replay_id = g.game_metadata.match_guid if replay_id == '': replay_id = g.game_metadata.id shutil.move(fn, os.path.join(os.path.dirname(fn), replay_id + '.replay')) shutil.move( pickled + '.pts', os.path.join(os.path.dirname(pickled), replay_id + '.replay.pts')) shutil.move( pickled + '.gzip', os.path.join(os.path.dirname(pickled), replay_id + '.replay.gzip')) return replay_id
def write_proto_pandas_to_file(filename): proto_manager = analyze_replay_file(filename) _, proto_name = tempfile.mkstemp(dir=get_test_folder()) with open(proto_name, 'wb') as f: proto_manager.write_proto_out_to_file(f) _, pandas_name = tempfile.mkstemp(dir=get_test_folder()) with open(pandas_name, 'wb') as f: proto_manager.write_pandas_out_to_file(f) return proto_name, pandas_name, proto_manager.protobuf_game
def convert_replay_to_game_frames(inputName, JSONpath, save_json=True): manager = carball.analyze_replay_file(inputName, output_path=JSONpath, overwrite=True) result = convert_json_to_game_frames(JSONpath) if not save_json: os.remove(JSONpath) return result
def setUp(self): engine, sessionmaker = startup.startup() self.session = sessionmaker() _, path = tempfile.mkstemp() with open(path, 'wb') as tmp: tmp.write(download_replay_discord(get_complex_replay_list()[0])) self.replay = analyze_replay_file(path, path + '.json') self.proto = self.replay.protobuf_game self.guid = self.proto.game_metadata.match_guid
def parse_file(replay): replay_name = write_files_to_disk([replay])[0] replay = analyze_replay_file( os.path.join(get_test_folder(), replay_name), os.path.join(get_test_folder(), replay_name) + '.json') proto = replay.protobuf_game if proto.game_metadata.match_guid is not None and proto.game_metadata.match_guid != '': guid = proto.game_metadata.match_guid else: guid = proto.game_metadata.id return replay, proto, guid
def write_proto_pandas_to_file(filename): proto_manager = analyze_replay_file(filename) _, proto_name = tempfile.mkstemp(dir=TestFolderManager.get_test_folder()) proto_name = proto_name + PROTO_EXTENSION with open(proto_name, 'wb') as f: proto_manager.write_proto_out_to_file(f) _, pandas_name = tempfile.mkstemp(dir=TestFolderManager.get_test_folder()) pandas_name = pandas_name + PANDAS_EXTENSION with gzip.open(pandas_name, 'wb') as f: proto_manager.write_pandas_out_to_file(f) return proto_name, pandas_name, proto_manager.protobuf_game
def parse_replay_wrapper(replay_to_parse_path: str, parsed_data_path: str, failed_dir: str, force_reparse: bool, logger, query_params=None) -> Optional[AnalysisManager]: """ Parses a replay with the given parameters. :param replay_to_parse_path: The path where the replay that is being parsed is stored :param parsed_data_path: The path where the post parsing data will be stored :param failed_dir: The path where we will store a replay that failed to parse :param force_reparse: If true the replay will parse even if it has already been parsed before. :param logger: The logger that is logging the error messages :return: An analysis manager if the replay was successfully parsed. It will return None if the replay has already been parsed. """ # Todo preparse replay ID here to save on extra parsing and on locks. (remember to delete locks older than 1 day) if os.path.isfile(parsed_data_path) and not force_reparse: return # try: try: analysis_manager = analyze_replay_file(replay_to_parse_path) # type: AnalysisManager except Exception as e: if not os.path.isdir(failed_dir): os.makedirs(failed_dir) shutil.move(replay_to_parse_path, os.path.join(failed_dir, os.path.basename(replay_to_parse_path))) with open(os.path.join(failed_dir, os.path.basename(replay_to_parse_path) + '.txt'), 'a') as f: f.write(str(e)) f.write(traceback.format_exc()) payload = { 'replay_uuid': os.path.basename(replay_to_parse_path), 'error_type': type(e).__name__, 'stack': traceback.format_exc(), 'game_hash': None } ErrorLogger.log_replay_error(payload, query_params) raise e try: write_replay_to_disk(analysis_manager, parsed_data_path) except Exception as e: ErrorLogger.log_error(e, logger=logger) with open(os.path.join(failed_dir, os.path.basename(replay_to_parse_path) + '.txt'), 'a') as f: f.write(str(e)) f.write(traceback.format_exc()) return analysis_manager
def predict_boost_values(rp, model): if isinstance(rp, str): rp = cb.analyze_replay_file(rp, controls=ControlsCreator(), logging_level=logging.CRITICAL) pp = replay_to_dfs(rp, skip_ties=False) cpx, cpy = convert_dfs(pp, frame_mode=1) normalize(cpx) results = np.zeros((cpx[0].shape[0], len(pp["players"]), 101)) # xs = np.zeros((cpx[0].shape[0], len(pp["players"]))) # ys = np.zeros((cpx[0].shape[0], len(pp["players"]))) model.eval() with torch.no_grad(): for p, row in pp["players"].iterrows(): print(p) cpxc = [np.copy(v) for v in cpx] # # For making histogram # preds = model(*[torch.from_numpy(v).float().cuda() for v in cpxc]) # preds = preds[0].cpu().detach().numpy() # preds = preds[:, 1] - preds[:, 0] # if row["color"] == "blue": # preds = -preds # xs[:, p] = cpxc[2][:, p, 19] # ys[:, p] = preds # For changing the boost amount at every step for b in range(101): print("\t", b) cpxc[2][:, p, 19] = b / 100 cpxc[2][:, p, 19][cpxc[2][:, p, 20] == 1] = 0 preds = model( *[torch.from_numpy(v).float().cuda() for v in cpxc]) preds = preds[0].cpu().detach().numpy() preds = preds[:, 1] - preds[:, 0] if row["color"] == "blue": preds = -preds results[:, p, b] = preds return results
def parse_file(analysis_manager, temp_folder=None): replay_name = write_files_to_disk([analysis_manager], temp_folder=temp_folder)[0] try: analysis_manager = analyze_replay_file( os.path.join( TestFolderManager.get_test_folder(temp_folder=temp_folder), replay_name)) except Exception as e: print('error parsing ', replay_name) raise e proto = analysis_manager.protobuf_game if proto.game_metadata.match_guid is not None and proto.game_metadata.match_guid != '': guid = proto.game_metadata.match_guid else: guid = proto.game_metadata.id replay_path = os.path.join( TestFolderManager.get_test_folder(temp_folder=temp_folder), guid) + REPLAY_EXTENSION write_replay_to_disk(analysis_manager, replay_path) return analysis_manager, proto, guid, replay_path
def replays_to_csv(in_files: List[str], output_path: str, shared: List[Value]): """ Multiprocessing function called by pre_process_parallel. Converts replay files to csv files. :param in_files: A list of files to process. :type in_files: List[str] :param output_path: The path to save CSVs :type output_path: str :param shared: A list of multiprocessing values to track errors. :type shared: List[Value] :return: None :rtype: None """ try: ordered_cols = get_ordered_columns(NUM_PLAYERS) print("Starting {} with {} files".format(current_process().name, len(in_files))) file_average = [timedelta(), 0] for file in in_files: file_start = datetime.now() try: with HiddenPrints(): try: e = False # Analysis has a lot of possible errors analysis = carball.analyze_replay_file(replay_path + file) except IndexError: with shared[0].get_lock(): shared[0].value += 1 e = True continue except KeyError: with shared[1].get_lock(): shared[1].value += 1 e = True continue except RattleTrapException: # Currently can't figure out how to except "carball.rattletrap.run_rattletrap.RattleTrapException" with shared[2].get_lock(): shared[2].value += 1 e = True continue except UnboundLocalError: with shared[3].get_lock(): shared[3].value += 1 e = True continue except KeyboardInterrupt: print("Exiting") break except(): with shared[4].get_lock(): shared[4].value += 1 e = True continue finally: if e: copyfile(replay_path + file, error_path + file) proto_game = analysis.get_protobuf_data() gdf = analysis.get_data_frame() # Check if game has overtime, and skip it if it has extra columns (very rare, but can happen) gdf.columns = gdf.columns.to_flat_index() game_has_overtime = False if len(gdf.columns) != 65: if len(gdf.columns) == 66: if ('game', 'is_overtime') in gdf.columns: game_has_overtime = True else: copyfile(replay_path + file, skip_path + file) continue goal_seconds, goal_frames, goal_scorers, goal_teams, gdf = restructure_and_get_goals(proto_game, gdf) # Skip if no goals or if game is too short # Take this out or change it depending on how much you care about dataset quality if 210 not in gdf['game_seconds_remaining']: copyfile(replay_path + file, skip_path + file) continue if len(goal_frames) == 0: copyfile(replay_path + file, skip_path + file) assert (len(proto_game.game_metadata.goals) == 0) continue # Order the columns of the df gdf = gdf[ordered_cols] # Get times of each goal try: for i in goal_frames: goal_seconds.append(gdf.iloc[i]['game_seconds_remaining']) except IndexError: with shared[5].get_lock(): shared[5].value += 1 copyfile(replay_path + file, error_path + file) print('Index Error') continue # Skip overtimes. if game_has_overtime: if len(goal_seconds) == 1: copyfile(replay_path + file, skip_path + file) continue else: goal_seconds = goal_seconds[:-1] goal_frames = goal_frames[:-1] trunc_length = add_game_columns(gdf, goal_frames, goal_seconds, goal_teams) # Fixing up missing values and rows after the last goal gdf = gdf.truncate(after=trunc_length - 1) # Drop when all player values are NA sub = ['z_0_pos_x', 'o_0_pos_x', 'z_1_pos_x', 'o_1_pos_x', 'z_2_pos_x', 'o_2_pos_x'] gdf = gdf.dropna(how='all', subset=sub[:NUM_PLAYERS*2]) # forward fill demos (Single player NA), then fill empty values (Ball values) for team in ['z_', 'o_']: for i in range(NUM_PLAYERS): num = str(i) + '_' gen_list = ['pos_x', 'pos_y', 'pos_z', 'rot_x', 'rot_y', 'rot_z', 'vel_x', 'vel_y', 'vel_z', 'ang_vel_x', 'ang_vel_y', 'ang_vel_z', 'boost_active', 'jump_active', 'double_jump_active', 'dodge_active'] fill_list = [team + num + entry for entry in gen_list] # Change demo column using presence of NA values gdf[team + num + 'is_demo'] = gdf[fill_list].isna().replace({True: 1, False: 0}).mean(axis=1) # Turn NA values into value before demo for _ in fill_list: gdf.loc[:, fill_list] = gdf.loc[:, fill_list].ffill(axis=0) # Drop the time after a goal is scored but before reset (in these casesm game_goal_number is N/A) gdf = gdf.dropna(axis='index', subset=['game_goal_number']) gdf = gdf.drop(['game_goal_number'], axis=1) gdf = gdf[gdf['secs_to_goal'] > 0] # Fill rest of NA value with 0 gdf = gdf.fillna(0) # Change active values to boolean gdf['z_0_jump_active'] = ((gdf['z_0_jump_active'] % 2) != 0).astype(int) gdf['o_0_jump_active'] = ((gdf['o_0_jump_active'] % 2) != 0).astype(int) gdf['z_0_double_jump_active'] = ((gdf['z_0_double_jump_active'] % 2) != 0).astype(int) gdf['o_0_double_jump_active'] = ((gdf['o_0_double_jump_active'] % 2) != 0).astype(int) gdf['z_0_dodge_active'] = ((gdf['z_0_dodge_active'] % 2) != 0).astype(int) gdf['o_0_dodge_active'] = ((gdf['o_0_dodge_active'] % 2) != 0).astype(int) # Convert all booleans to 0 or 1 gdf = gdf.replace({True: 1, False: 0}) # Reduce size in memory # Write out to CSV after shrinking gdf = shrink_df(gdf) gdf.to_csv(output_path + file.split('.')[0] + '.csv') file_average[1] += 1 file_average[0] += (datetime.now() - file_start) sys.stdout.flush() except(KeyboardInterrupt, SystemExit): break except(KeyboardInterrupt, SystemExit): pass # Update number of processes so reporting doesn't idle with shared[6].get_lock(): shared[6].value -= 1 print("{} Exiting".format(current_process().name)) return
def decompile_replay(path: str) -> Union[pd.DataFrame, pd.DataFrame]: analysis_manager = carball.analyze_replay_file(path, logging_level=logging.CRITICAL) frames = analysis_manager.get_data_frame().fillna(value=0) stats = dict(analysis_manager.get_json_data()) return frames, stats
def save_replay_dataframe(replay_file: str, save_file: str): data_frame = carball.analyze_replay_file(replay_file).data_frame data_frame.to_csv(save_file)
def replay_to_dfs(replay, skip_ties=True, skip_kickoffs=True): import pandas as pd import carball as cb if isinstance(replay, (str, bytes)): from carball.controls.controls import ControlsCreator replay = cb.analyze_replay_file(replay, controls=ControlsCreator(), logging_level=logging.CRITICAL) blue_team, orange_team = replay.game.teams if blue_team.is_orange: blue_team, orange_team = orange_team, blue_team frames = pd.DataFrame(index=replay.data_frame.index) frames[f"ball/pos_x"] = replay.game.ball["pos_x"].fillna(0.) frames[f"ball/pos_y"] = replay.game.ball["pos_y"].fillna(0.) frames[f"ball/pos_z"] = replay.game.ball["pos_z"].fillna(0.) frames[f"ball/vel_x"] = replay.game.ball["vel_x"].fillna(0.) / 10 frames[f"ball/vel_y"] = replay.game.ball["vel_y"].fillna(0.) / 10 frames[f"ball/vel_z"] = replay.game.ball["vel_z"].fillna(0.) / 10 frames[f"ball/ang_vel_x"] = replay.game.ball["ang_vel_x"].fillna(0.) / 1000 frames[f"ball/ang_vel_y"] = replay.game.ball["ang_vel_y"].fillna(0.) / 1000 frames[f"ball/ang_vel_z"] = replay.game.ball["ang_vel_z"].fillna(0.) / 1000 boost_grabs = pd.DataFrame(columns=["frame", "boost_id", "player"]) player_index = {} player_df = pd.DataFrame(columns=["color", "online_id", "name"]) i = 0 for color, team in ("blue", blue_team), ("orange", orange_team): for player in team.players: player_index[player.online_id] = i frames[f"{i}/pos_x"] = player.data["pos_x"].fillna(0.) frames[f"{i}/pos_y"] = player.data["pos_y"].fillna(0.) frames[f"{i}/pos_z"] = player.data["pos_z"].fillna(0.) yaw = player.data["rot_y"].fillna(0.) pitch = player.data["rot_x"].fillna(0.) roll = player.data["rot_z"].fillna(0.) forward, up = rotator_to_matrix(yaw, pitch, roll) frames[f"{i}/forward_x"] = forward[0] frames[f"{i}/forward_y"] = forward[1] frames[f"{i}/forward_z"] = forward[2] frames[f"{i}/up_x"] = up[0] frames[f"{i}/up_y"] = up[1] frames[f"{i}/up_z"] = up[2] frames[f"{i}/vel_x"] = player.data["vel_x"].fillna(0.) / 10 frames[f"{i}/vel_y"] = player.data["vel_y"].fillna(0.) / 10 frames[f"{i}/vel_z"] = player.data["vel_z"].fillna(0.) / 10 frames[f"{i}/ang_vel_x"] = player.data["ang_vel_x"].fillna(0.) / 1000 frames[f"{i}/ang_vel_y"] = player.data["ang_vel_y"].fillna(0.) / 1000 frames[f"{i}/ang_vel_z"] = player.data["ang_vel_z"].fillna(0.) / 1000 frames[f"{i}/boost_amount"] = player.data["boost"].fillna(0.) / 2.55 for col in player.controls.columns: frames[f"{i}/{col}_control"] = player.controls[col].astype(float) boost_pickups = player.data["boost"].diff() > 0 for frame, row in player.data[boost_pickups].iterrows(): xyz = (row["pos_x"], row["pos_y"], row["pos_z"]) # TODO use boost ID instead closest_boost = min(range(len(boost_locations)), key=lambda j: sum((a - b) ** 2 for a, b in zip(xyz, boost_locations[j]))) boost_grabs.loc[len(boost_grabs)] = [frame, closest_boost, i] player_df.loc[len(player_df)] = [color, player.online_id, player.name] i += 1 rallies = pd.DataFrame(columns=["start_frame", "end_frame", "team"]) for kf1, kf2 in zip(replay.game.kickoff_frames, replay.game.kickoff_frames[1:] + [replay.game.frames.index[-1]]): for goal in replay.game.goals: if kf1 < goal.frame_number < kf2: rallies.loc[len(rallies)] = [kf1, goal.frame_number, ["blue", "orange"][goal.player_team]] break else: # No goal between kickoffs rallies.loc[len(rallies)] = [kf1, kf2, None] demos = pd.DataFrame(columns=["frame", "attacker", "victim"]) for demo in replay.game.demos: frame = demo.get("frame_number", None) attacker = demo.get("attacker", None) victim = demo.get("victim", None) if frame and attacker and victim: demos.loc[len(demos)] = [frame, player_index[attacker.online_id], player_index[victim.online_id]] touches = pd.DataFrame(columns=["frame", "player"]) for touch in replay.protobuf_game.game_stats.hits: touches.loc[len(touches)] = [touch.frame_number, player_index[touch.player_id.id]] if skip_ties: goal_rallies = rallies[rallies["team"].notna()] frames = frames.loc[ np.r_[tuple(slice(row["start_frame"], row["end_frame"]) for _, row in goal_rallies.iterrows())]] elif skip_kickoffs: goal_rallies = rallies frames = frames.loc[ np.r_[tuple(slice(row["start_frame"], row["end_frame"]) for _, row in goal_rallies.iterrows())]] # if isinstance(frame_mode, int): # frames = frames.iloc[np.random.randint(0, frame_mode)::frame_mode] # else: # frames = frames.sample(frac=frame_mode) return {"frames": frames, "rallies": rallies, "touches": touches, "boost_grabs": boost_grabs, "demos": demos, "players": player_df}
def parse_replay( self, filename, preserve_upload_date: bool = False, # url parameters query_params: Dict[str, any] = None, # test parameters custom_file_location: str = None, force_reparse: bool = False): """ :param self: :param filename: filename :param query_params: The arguments from the url :param preserve_upload_date: If true the upload date is retained :param custom_file_location: If a custom file path should be used instead :param force_reparse: if true parsing will happen even if a file already exists. :return: """ if custom_file_location is None: pickled = os.path.join(get_default_parse_folder(), os.path.basename(filename)) else: pickled = os.path.join(custom_file_location, os.path.basename(filename)) if custom_file_location is None: failed_dir = os.path.join(os.path.dirname(get_default_parse_folder()), 'failed') else: failed_dir = custom_file_location if os.path.isfile(pickled) and not force_reparse: return # try: try: analysis_manager = analyze_replay_file(filename) # type: ReplayGame except Exception as e: if not os.path.isdir(failed_dir): os.makedirs(failed_dir) shutil.move(filename, os.path.join(failed_dir, os.path.basename(filename))) with open( os.path.join(failed_dir, os.path.basename(filename) + '.txt'), 'a') as f: f.write(str(e)) f.write(traceback.format_exc()) raise e try: if not os.path.isdir(os.path.dirname(pickled)): os.makedirs(os.path.dirname(pickled)) with open(pickled + '.pts', 'wb') as fo: analysis_manager.write_proto_out_to_file(fo) with gzip.open(pickled + '.gzip', 'wb') as fo: analysis_manager.write_pandas_out_to_file(fo) except Exception as e: log_error(e, logger=logger) with open( os.path.join(failed_dir, os.path.basename(filename) + '.txt'), 'a') as f: f.write(str(e)) f.write(traceback.format_exc()) # success! proto_game = analysis_manager.protobuf_game if proto_game.game_metadata.match_guid is None or proto_game.game_metadata.match_guid == '': proto_game.game_metadata.match_guid = proto_game.game_metadata.id parsed_replay_processing(proto_game, query_params, preserve_upload_date=preserve_upload_date) return save_replay(proto_game, filename, pickled)
def main(program_args=None): parser = argparse.ArgumentParser( description='Rocket League replay parsing and analysis.') parser.add_argument( '-i', '--input', type=str, required=True, help= 'Path to replay file that will be analyzed. Carball expects a raw replay file unless ' '--skip-decompile is provided.') parser.add_argument( '--proto', type=str, required=False, help= 'The result of the analysis will be saved to this file in protocol buffers format.' ) parser.add_argument( '--json', type=str, required=False, help= 'The result of the analysis will be saved to this file in json file format.' ) parser.add_argument( '--gzip', type=str, required=False, help= 'The pandas data frame containing the replay frames will be saved to this file in a ' 'compressed gzip format.') parser.add_argument( '-v', '--verbose', action='count', default=0, help= 'Set the logging level to INFO. To set the logging level to DEBUG use -vv.' ) parser.add_argument('-s', '--silent', action='store_true', default=False, help='Disable logging altogether.') parser.add_argument( '-dr', '--dry-run', action='store_true', default=False, help= 'Explicitly notifying that there is not going to be any output to be saved' ) if program_args is not None: args = parser.parse_args(program_args) else: args = parser.parse_args() if not args.proto and not args.json and not args.gzip and not args.dry_run: parser.error( 'at least one of the following arguments are required: --proto, --json, --gzip' ) log_level = logging.WARNING if args.verbose == 1: log_level = logging.INFO elif args.verbose >= 2: log_level = logging.DEBUG if args.silent: logging.basicConfig(handlers=[logging.NullHandler()]) else: logging.basicConfig(handlers=[logging.StreamHandler()], level=log_level) manager = carball.analyze_replay_file(args.input) if args.proto: with open(args.proto, 'wb') as f: manager.write_proto_out_to_file(f) if args.json: with open(args.json, 'w') as f: manager.write_json_out_to_file(f) if args.gzip: with gzip.open(args.gzip, 'wb') as f: manager.write_pandas_out_to_file(f)
def make_summary(rp, model): if isinstance(rp, str): rp = cb.analyze_replay_file(rp, logging_level=logging.CRITICAL) pp = replay_to_dfs(rp, skip_ties=False) cpx, cpy = convert_dfs(pp, frame_mode=1) # print(cpy) normalize(cpx) pred_df = pd.DataFrame(index=pp["frames"].index) model.eval() with torch.no_grad(): def get_predictions(x_data): preds = None bs = 1024 lo, hi = 0, bs for batch in range(bs, len(x_data[0]) + bs, bs): hi = min(batch, len(x_data[0])) batch_x = [v[lo:hi] for v in x_data] batch_preds_b = model( *[torch.from_numpy(v).float().cuda() for v in batch_x]) batch_preds_b = [ bp.cpu().detach().numpy() for bp in batch_preds_b ] # swap_teams(batch_x) # batch_preds_s = model(*[torch.from_numpy(v).float().cuda() for v in batch_x]) # batch_preds_s = [bp.cpu().detach().numpy() for bp in batch_preds_s] # # swap_left_right(batch_x) # batch_preds_sm = model(*[torch.from_numpy(v).float().cuda() for v in batch_x]) # batch_preds_sm = [bp.cpu().detach().numpy() for bp in batch_preds_sm] # # swap_teams(batch_x) # batch_preds_m = model(*[torch.from_numpy(v).float().cuda() for v in batch_x]) # batch_preds_m = [bp.cpu().detach().numpy() for bp in batch_preds_m] # # batch_preds = [(b + s + m + sm) / 4 for b, s, m, sm in # zip(batch_preds_b, batch_preds_s, batch_preds_m, batch_preds_sm)] batch_preds = batch_preds_b if preds is None: preds = batch_preds else: preds = [ np.concatenate([p, bp]) for p, bp in zip(preds, batch_preds) ] lo = hi return preds # 1. Get predictions with all players preds = get_predictions(cpx) pred_df["pred"] = preds[0][:, 0] - preds[0][:, 1] for i, row in pp["players"].iterrows(): color = row["color"] sgn = 1 if color == "blue" else -1 cpxc = [np.copy(v) for v in cpx] cpxc[2] = cpxc[2][:, [k for k in range(cpx[2].shape[1]) if k != i]] # 2. Get predictions with one player removed preds = get_predictions(cpxc) pred_df[f"{i}/pred_removed"] = preds[0][:, 0] - preds[0][:, 1] cpxc = [np.copy(v) for v in cpx] cpxc[2] = cpxc[2][:, [i]] # 3. Get predictions with teammates removed preds = get_predictions(cpxc) pred_df[f"{i}/pred_solo"] = preds[0][:, 0] - preds[0][:, 1] # pred_df[f"{identifier}/pred"] = sigmoid(sgn * pred_df["pred"]) * \ # sigmoid(sgn * pred_df[f"{identifier}/pred_solo"]) / \ # sigmoid(sgn * pred_df[f"{identifier}/pred_removed"]) pred_df[f"{i}/pred"] = sgn * (pred_df["pred"] - pred_df[f"{i}/pred_removed"]) # pred_df[f"{identifier}/pred"] = sgn * ( # pred_df["pred"] - pred_df[f"{identifier}/pred_removed"] + pred_df[f"{identifier}/pred_solo"]) return pred_df, pp
def plot_replay(rp_path, model, plot_players=True, invert=None, plot_goal_distance=False): if plot_players is True: plot_players = ("blue", "orange") elif plot_players is False: plot_players = () elif plot_players == "blue": plot_players = ("blue", ) elif plot_players == "orange": plot_players = ("orange", ) else: plot_players = () rp = cb.analyze_replay_file(rp_path, controls=ControlsCreator(), logging_level=logging.CRITICAL) id_team = { p.online_id: ["blue", "orange"][p.team.is_orange] for p in rp.game.players } id_name = {p.online_id: p.name for p in rp.game.players} pred_df, dfs = make_summary(rp, model) if invert is None: d = 0 for goal in rp.game.goals: if goal.player.is_orange: d += 1 else: d -= 1 invert = d < 0 # pp["pred"][pp["ball/ball/vel_y"] == 0] = float("nan") # pp["pred"][pp["ball/ball/vel_y"].isna()] = float("nan") fig, ax = plt.subplots(figsize=((6.4 * dfs["frames"].index[-1] + 1000) / 5000, 4.8), dpi=400, constrained_layout=True) ax.hlines(0.5, xmin=0, xmax=dfs["frames"].index[-1], color="black", linestyle="dashed") blue_shots = [] orange_shots = [] blue_saves = [] orange_saves = [] for hit in rp.protobuf_game.game_stats.hits: if hit.shot: if id_team[hit.player_id.id] == "blue": blue_shots.append(hit.frame_number) else: orange_shots.append(hit.frame_number) if hit.save: if id_team[hit.player_id.id] == "blue": blue_saves.append(hit.frame_number) else: orange_saves.append(hit.frame_number) blue_ymin, blue_ymax, orange_ymin, orange_ymax = (0.5, 1, 0, 0.5) if invert else (0, 0.5, 0.5, 1) ax.vlines(blue_shots, ymin=blue_ymin, ymax=blue_ymax, color="m", linestyle="dotted", label="Shot") ax.vlines(orange_shots, ymin=orange_ymin, ymax=orange_ymax, color="m", linestyle="dotted") ax.vlines(blue_saves, ymin=orange_ymin, ymax=orange_ymax, color="c", linestyle="dotted", label="Save") ax.vlines(orange_saves, ymin=blue_ymin, ymax=blue_ymax, color="c", linestyle="dotted") if plot_goal_distance: goal_location = np.array([0, -5120 if invert else 5120, 642 / 2]) distances = np.linalg.norm( np.tile(goal_location, len(dfs["frames"])).reshape( (len(dfs["frames"]), 3)) - dfs["frames"].filter(regex="ball/pos_").values, axis=-1) ax.plot(dfs["frames"].index, distances / 11216, label="Ball-Goal Distance") pred = sigmoid(pred_df["pred"]) ax.plot(dfs["frames"].index, pred if invert else 1 - pred, color="blue", label="Advantage") blue_colors = ["springgreen", "mediumseagreen", "green"] orange_colors = ["lightcoral", "indianred", "brown"] # plt.show() b = o = 0 for i, row in dfs["players"].iterrows(): name = row["name"] color = row["color"] avg_score = np.exp(pred_df[f"{i}/pred"].mean()) print(name, avg_score) # pred_df[f"{identifier}/pred"].plot.hist(title=name, bins=100) # plt.show() pred_col = sigmoid(pred_df[f"{i}/pred"]) if color == "blue" and "blue" in plot_players: ax.plot(dfs["frames"].index, pred_col if invert else 1 - pred_col, color=blue_colors[b], label=name, linewidth=0.5) b += 1 elif color == "orange" and "orange" in plot_players: ax.plot(dfs["frames"].index, 1 - pred_col if invert else pred_col, color=orange_colors[o], label=name, linewidth=0.5) o += 1 rallies = dfs["rallies"] ax.vlines(rallies[rallies["team"].notna()]["end_frame"], ymin=0, ymax=1, color="red", label="Goal") ax.set_xlim(0, dfs["frames"].index[-1]) ax.set_ylim(0, 1) ax.set_xlabel("Frame") ax.set_ylabel("Orange <-> Blue" if invert else "Blue <-> Orange") ax.set_title(rp.game.name) ax.legend(bbox_to_anchor=(1.01, 1), loc='upper left') secax = ax.twiny() new_tick_locations = [] new_ticks = [] taken = None for frame in reversed(rp.game.frames.index): left = rp.game.frames["seconds_remaining"].loc[frame] if np.isnan(left): continue left = int(left) if left % 10 == 0 and left != taken: new_tick_locations.append(frame) new_ticks.append("{0:.0f}:{1:02.0f}".format( *divmod(rp.game.frames["seconds_remaining"].loc[frame], 60))) taken = left secax.set_xlim(*ax.get_xlim()) secax.set_xticks(new_tick_locations) secax.set_xticklabels(new_ticks) secax.set_xlabel("Time") ax.grid() # df["pred"].plot(x=df["game/info/frame"], color="blue") return fig, dfs
if replay_parsing_file.is_file(): with replay_parsing_file.open("r") as f: replays_parsing = json.load(f) else: replays_parsing = {} for replay in replays: print("\n") print(replay) if replays_parsing.get(str(replay), False) is True: print("\tReplay previously parsed. Skipping...") continue try: analysis_manager = carball.analyze_replay_file(str(replay)) proto_game = analysis_manager.get_protobuf_data() dataframe = analysis_manager.get_data_frame() print( f"\tplayers name and score: {[(player.name, player.score) for player in proto_game.players]}" ) print(f"\tdf length: {len(dataframe)}") replays_parsing[str(replay)] = True except: import traceback traceback.print_exc() replays_parsing[str(replay)] = traceback.format_exc()
def main(): parser = argparse.ArgumentParser( description='Rocket League replay parsing and analysis.') parser.add_argument( '-i', '--input', type=str, required=True, help= 'Path to replay file that will be analyzed. Carball expects a raw replay file unless ' '--skip-decompile is provided.') parser.add_argument( '--proto', type=str, required=False, help= 'The result of the analysis will be saved to this file in protocol buffers format.' ) parser.add_argument( '--json', type=str, required=False, help= 'The result of the analysis will be saved to this file in json file format. This is not ' 'the decompiled replay json from rattletrap.') parser.add_argument( '--gzip', type=str, required=False, help= 'The pandas data frame containing the replay frames will be saved to this file in a ' 'compressed gzip format.') parser.add_argument( '-sd', '--skip-decompile', action='store_true', default=False, help= 'If set, carball will treat the input file as a json file that Rattletrap outputs.' ) parser.add_argument( '-v', '--verbose', action='count', default=0, help= 'Set the logging level to INFO. To set the logging level to DEBUG use -vv.' ) parser.add_argument('-s', '--silent', action='store_true', default=False, help='Disable logging altogether.') args = parser.parse_args() if not args.proto and not args.json and not args.gzip: parser.error( 'at least one of the following arguments are required: --proto, --json, --gzip' ) log_level = logging.WARNING if args.verbose == 1: log_level = logging.INFO elif args.verbose >= 2: log_level = logging.DEBUG if args.silent: logging.basicConfig(handlers=[logging.NullHandler()]) else: logging.basicConfig(handlers=[logging.StreamHandler()], level=log_level) if args.skip_decompile: game = Game() game.initialize(loaded_json=args.input) manager = AnalysisManager(game) manager.create_analysis() else: manager = carball.analyze_replay_file(args.input) if args.proto: with open(args.proto, 'wb') as f: manager.write_proto_out_to_file(f) if args.json: proto_game = manager.get_protobuf_data() with open(args.json, 'w') as f: f.write(MessageToJson(proto_game)) if args.gzip: with gzip.open(args.gzip, 'wb') as f: manager.write_pandas_out_to_file(f)