Skip to content

Commit 4305e0e

Browse files
authoredOct 15, 2020
Merge branch 'master' into bumps
2 parents 04044a1 + 3e66f17 commit 4305e0e

File tree

13 files changed

+109
-15
lines changed

13 files changed

+109
-15
lines changed
 

‎CARBALL_VERSION

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0
1+
5

‎README.md

+11
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ cd carball/
3737
python init.py
3838
```
3939

40+
##### Mac
41+
In MacOS Catalina, zsh replaced bash as the default shell, which may cause permission issues when trying to run `install-protoc.sh` in the above fashion. Simply invoking bash should resolve this issue, like so:
42+
```
43+
git clone https://github.com/SaltieRL/carball
44+
cd carball/
45+
bash ./_travis/install-protoc.sh
46+
python init.py
47+
```
48+
Apple's decision to replace bash as the default shell may foreshadow the removal of bash in a future version of MacOS. In such a case, Homebrew users can [install protoc](http://google.github.io/proto-lens/installing-protoc.html) by replacing `bash ./travis/install-protoc.sh` with `brew install protobuf`.
49+
50+
4051
## Examples / Usage
4152
One of the main data structures used in carball is the pandas.DataFrame, to learn more, see [its wiki page](https://github.com/SaltieRL/carball/wiki/data_frame).
4253

‎api/stats/player_stats.proto

+8
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,15 @@ message PlayerStats {
6969
optional CarryDribbles ball_carries = 13;
7070
optional CumulativeKickoffStats kickoff_stats = 14;
7171
optional api.stats.DropshotStats dropshot_stats = 15;
72+
optional DemoStats demo_stats = 16;
7273
}
7374

7475
message Controller {
7576
optional bool is_keyboard = 1;
7677
optional float analogue_steering_input_percent = 2;
7778
optional float analogue_throttle_input_percent = 3;
79+
optional float time_ballcam = 4;
80+
optional float time_handbrake = 5;
7881
}
7982

8083
// Stats for carrying
@@ -100,3 +103,8 @@ message CumulativeKickoffStats {
100103
optional int32 num_time_first_touch = 7;
101104
optional float average_boost_used = 8; // The average amount of boost used over all kickoff events
102105
}
106+
107+
message DemoStats {
108+
optional int32 num_demos_inflicted = 1;
109+
optional int32 num_demos_taken = 2;
110+
}

‎carball/analysis/analysis_manager.py

+11-8
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import logging
22
import time
3-
from typing import Dict, Callable
3+
from typing import Dict, Callable, Union
44

55
import pandas as pd
66
import json
77
import os
8+
import gzip
89

910
from google.protobuf.json_format import _Printer
1011
from typing.io import IO
@@ -116,20 +117,22 @@ def write_proto_out_to_file(self, file: IO):
116117
raise IOError("Proto files must be binary use open(path,\"wb\")")
117118
ProtobufManager.write_proto_out_to_file(file, self.protobuf_game)
118119

119-
def write_pandas_out_to_file(self, file: IO):
120+
def write_pandas_out_to_file(self, file: Union[IO, gzip.GzipFile]):
120121
"""
121-
Writes the pandas data to the specified file, as bytes.
122+
Writes the pandas data to the specified file, as bytes. File may be a GzipFile object to compress the data
123+
frame.
122124
123125
NOTES:
124126
The data is written as bytes (i.e. in binary), and the buffer mode must be 'wb'.
125-
E.g. open(file_name, 'wb')
127+
E.g. gzip.open(file_name, 'wb')
126128
The file will NOT be human-readable.
127129
128130
:param file: The file object (or a buffer).
129131
"""
130-
131-
if 'b' not in file.mode:
132-
raise IOError("Proto files must be binary use open(path,\"wb\")")
132+
if isinstance(file.mode, str) and 'b' not in file.mode:
133+
raise IOError("Data frame files must be binary use open(path,\"wb\")")
134+
if isinstance(file.mode, int) and file.mode != gzip.WRITE:
135+
raise IOError("Gzip compressed data frame files must be opened in WRITE mode.")
133136
if self.df_bytes is not None:
134137
file.write(self.df_bytes)
135138
elif not self.should_store_frames:
@@ -229,7 +232,7 @@ def _get_game_metadata(self, game: Game, proto_game: game_pb2.Game) -> Dict[str,
229232
for player in game.players:
230233
player_proto = proto_game.players.add()
231234
ApiPlayer.create_from_player(player_proto, player, self.id_creator)
232-
player_map[str(player.online_id)] = player_proto
235+
player_map[player.online_id] = player_proto
233236

234237
return player_map
235238

‎carball/analysis/events/kickoff_detection/kickoff_analysis.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ def get_kickoffs_from_game(game: Game, proto_game: game_pb2, id_creation:Callabl
4242
summed_time = smaller_data_frame['game']['delta'][frame:end_frame].sum()
4343
if summed_time > 0:
4444
cur_kickoff.touch_time = summed_time
45-
logger.error("STRAIGHT TIME " + str(time))
46-
logger.error("SUM TIME" + str(summed_time))
45+
logger.info("STRAIGHT TIME " + str(time))
46+
logger.info("SUM TIME" + str(summed_time))
4747
sum_vs_adding_diff = time - summed_time
4848

4949

‎carball/analysis/stats/controls/controls.py

+17
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from logging import getLogger
22
from typing import Dict
33

4+
import numpy as np
45
import pandas as pd
56

67
from ....analysis.stats.stats import BaseStat
8+
from ....analysis.stats.utils.pandas_utils import sum_deltas_by_truthy_data
79
from ....generated.api import game_pb2
810
from ....generated.api.player_pb2 import Player
911
from ....generated.api.stats.player_stats_pb2 import PlayerStats
@@ -20,6 +22,7 @@ def calculate_player_stat(self, player_stat_map: Dict[str, PlayerStats], game: G
2022
for player_key, stats in player_map.items():
2123
try:
2224
player_name = player_map[player_key].name
25+
player_data_frame = data_frame[player_name].copy()
2326

2427
steering_percentage = self.get_analogue_percentage("steer", data_frame, player_name)
2528
throttle_percentage = self.get_analogue_percentage("throttle", data_frame, player_name)
@@ -29,10 +32,24 @@ def calculate_player_stat(self, player_stat_map: Dict[str, PlayerStats], game: G
2932
controller_stats.is_keyboard = is_keyboard
3033
controller_stats.analogue_steering_input_percent = throttle_percentage
3134
controller_stats.analogue_throttle_input_percent = steering_percentage
35+
if 'ball_cam' in player_data_frame:
36+
time_ballcam = self.get_ballcam_duration(data_frame, player_data_frame)
37+
controller_stats.time_ballcam = time_ballcam
38+
if 'handbrake' in player_data_frame:
39+
time_handbrake = self.get_handbrake_duration(data_frame, player_data_frame)
40+
controller_stats.time_handbrake = time_handbrake
3241
except KeyError as e:
3342
logger.warning('Player never pressed control %s', e)
3443

3544
def get_analogue_percentage(self, column: str, data_frame: pd.DataFrame, player_name: str):
3645
total_frames = len(data_frame[player_name][column])
3746
count = (data_frame[player_name][column] == 0).sum() + (data_frame[player_name][column] == 128).sum() + (data_frame[player_name][column] == 255).sum() + data_frame[player_name][column].isna().sum()
3847
return 100 - ((count * 100) / total_frames)
48+
49+
@staticmethod
50+
def get_ballcam_duration(data_frame: pd.DataFrame, player_dataframe: pd.DataFrame) -> np.float64:
51+
return sum_deltas_by_truthy_data(data_frame, player_dataframe.ball_cam)
52+
53+
@staticmethod
54+
def get_handbrake_duration(data_frame: pd.DataFrame, player_dataframe: pd.DataFrame) -> np.float64:
55+
return sum_deltas_by_truthy_data(data_frame, player_dataframe.handbrake)

‎carball/analysis/stats/demos/__init__.py

Whitespace-only changes.

‎carball/analysis/stats/demos/demos.py

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import logging
2+
from typing import Dict
3+
4+
import numpy as np
5+
import pandas as pd
6+
from carball.analysis.constants.field_constants import FieldConstants
7+
8+
from carball.analysis.stats.utils.pandas_utils import sum_deltas_by_truthy_data
9+
from ....analysis.stats.stats import BaseStat
10+
from ....generated.api import game_pb2
11+
from ....generated.api.player_pb2 import Player
12+
from ....generated.api.stats.player_stats_pb2 import PlayerStats
13+
from ....json_parser.actor.boost import BOOST_PER_SECOND
14+
from ....json_parser.game import Game
15+
16+
logger = logging.getLogger(__name__)
17+
18+
19+
class DemoStat(BaseStat):
20+
def calculate_player_stat(self, player_stat_map: Dict[str, PlayerStats], game: Game, proto_game: game_pb2.Game,
21+
player_map: Dict[str, Player], data_frame: pd.DataFrame):
22+
player_demo_counts = {}
23+
player_got_demoed_counts = {}
24+
for demo in game.demos:
25+
attacker = demo['attacker'].online_id
26+
victim = demo['victim'].online_id
27+
if attacker not in player_demo_counts:
28+
player_demo_counts[attacker] = 1
29+
else:
30+
player_demo_counts[attacker] += 1
31+
if victim not in player_got_demoed_counts:
32+
player_got_demoed_counts[victim] = 1
33+
else:
34+
player_got_demoed_counts[victim] += 1
35+
for player in player_demo_counts:
36+
player_stat_map[player].demo_stats.num_demos_inflicted = player_demo_counts[player]
37+
for player in player_got_demoed_counts:
38+
player_stat_map[player].demo_stats.num_demos_taken = player_got_demoed_counts[player]

‎carball/analysis/stats/stats_list.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import List
22

3+
from carball.analysis.stats.demos.demos import DemoStat
34
from carball.analysis.stats.dribbles.ball_carry import CarryStat
45
from carball.analysis.stats.kickoffs.kickoff_stat import KickoffStat
56
from carball.analysis.stats.possession.per_possession import PerPossessionStat
@@ -42,7 +43,8 @@ def get_player_stats() -> List[BaseStat]:
4243
SpeedTendencies(),
4344
RumbleItemStat(),
4445
KickoffStat(),
45-
DropshotStats()
46+
DropshotStats(),
47+
DemoStat()
4648
]
4749

4850
@staticmethod

‎carball/json_parser/player.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ def __repr__(self):
4949
else:
5050
return '%s: %s' % (self.__class__.__name__, self.name)
5151

52+
def _get_player_id(self, online_id):
53+
if type(online_id) == dict:
54+
return online_id['online_id']
55+
return online_id
56+
5257
def create_from_actor_data(self, actor_data: dict, teams: List['Team'], objects: List[str]):
5358
self.name = actor_data['name']
5459
if 'Engine.PlayerReplicationInfo:bBot' in actor_data and actor_data['Engine.PlayerReplicationInfo:bBot']:
@@ -57,7 +62,8 @@ def create_from_actor_data(self, actor_data: dict, teams: List['Team'], objects:
5762

5863
else:
5964
actor_type = list(actor_data["Engine.PlayerReplicationInfo:UniqueId"]['remote_id'].keys())[0]
60-
self.online_id = actor_data["Engine.PlayerReplicationInfo:UniqueId"]['remote_id'][actor_type]
65+
self.online_id = self._get_player_id(actor_data["Engine.PlayerReplicationInfo:UniqueId"]
66+
['remote_id'][actor_type])
6167
try:
6268
self.score = actor_data["TAGame.PRI_TA:MatchScore"]
6369
except KeyError:

‎carball/tests/export_test.py

+9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from tempfile import NamedTemporaryFile
22

3+
import gzip
34
import pytest
45

56
from carball.analysis.analysis_manager import AnalysisManager
@@ -22,6 +23,14 @@ def test(analysis: AnalysisManager):
2223

2324
run_analysis_test_on_replay(test, get_raw_replays()["DEFAULT_3_ON_3_AROUND_58_HITS"], cache=replay_cache)
2425

26+
def test_gzip_export(self, replay_cache):
27+
def test(analysis: AnalysisManager):
28+
with NamedTemporaryFile(mode='wb') as f:
29+
gzip_file = gzip.GzipFile(mode='wb', fileobj=f)
30+
analysis.write_pandas_out_to_file(gzip_file)
31+
32+
run_analysis_test_on_replay(test, get_raw_replays()["DEFAULT_3_ON_3_AROUND_58_HITS"], cache=replay_cache)
33+
2534
def test_unicode_names(self, replay_cache):
2635
def test(analysis: AnalysisManager):
2736
with NamedTemporaryFile(mode='wb') as f:

‎requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ numpy==1.18.2
22
protobuf==3.6.1
33
pandas==1.0.3
44
xlrd==1.1.0
5-
boxcars-py==0.1.3
5+
boxcars-py==0.1.*

‎setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def run(self):
4747
version=version_string,
4848
packages=setuptools.find_packages(),
4949
include_package_data=True,
50-
install_requires=['pandas==0.24.2', 'protobuf==3.6.1', 'xlrd==1.1.0', 'numpy==1.17.0', 'boxcars-py==0.1.3'],
50+
install_requires=['pandas==1.0.3', 'protobuf==3.6.1', 'xlrd==1.1.0', 'numpy==1.18.2', 'boxcars-py==0.1.*'],
5151
url='https://github.com/SaltieRL/carball',
5252
keywords=['rocket-league'],
5353
license='Apache 2.0',

3 commit comments

Comments
 (3)

github-actions[bot] commented on Oct 15, 2020

@github-actions[bot]

Carball Benchmarks short_sample

Benchmark suite Current: 4305e0e Previous: 3e66f17 Ratio
carball/tests/benchmarking/benchmarking.py::test_short_sample 0.9724857256532954 iter/sec (stddev: 0.014489486537814192) 0.9068065034710597 iter/sec (stddev: 0.00924296428449896) 0.93

This comment was automatically generated by workflow using github-action-benchmark.

github-actions[bot] commented on Oct 15, 2020

@github-actions[bot]

Carball Benchmarks short_dropshot

Benchmark suite Current: 4305e0e Previous: 3e66f17 Ratio
carball/tests/benchmarking/benchmarking.py::test_short_dropshot 0.6707021716982366 iter/sec (stddev: 0.01816230047862237) 0.6936401698877517 iter/sec (stddev: 0.0052686922532796596) 1.03

This comment was automatically generated by workflow using github-action-benchmark.

github-actions[bot] commented on Oct 15, 2020

@github-actions[bot]

Carball Benchmarks intensive_oce_rlcs

Benchmark suite Current: 4305e0e Previous: 3e66f17 Ratio
carball/tests/benchmarking/benchmarking.py::test_intensive_oce_rlcs 0.0484132228330891 iter/sec (stddev: 0.3029039001351704) 0.06058107089228804 iter/sec (stddev: 0.18968435257771193) 1.25

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.