Skip to content

Commit ad85c3b

Browse files
authored
Merge pull request #1 from sourcebots/show-match-scores
Show match scores
2 parents 4b04692 + 2a515ba commit ad85c3b

File tree

4 files changed

+238
-1
lines changed

4 files changed

+238
-1
lines changed

script/typing/check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ if [ -z "$MYPY" ]; then
66
MYPY=mypy
77
fi
88

9-
exec "$MYPY" sr tests "$@"
9+
exec "$MYPY" setup.py sr tests "$@"

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ exclude =
99
script,
1010
tests/dummy
1111
ignore =
12+
# Don't worry about shadowing built-in module names. Modules are namespaced
13+
# anyway, so this is far less a concern than other built-in shadowing.
14+
A005
1215
# Don't require set literals
1316
C401
1417
C405

sr/comp/cli/command_line.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
scorer,
2323
shift_matches,
2424
show_league_table,
25+
show_match_scores,
2526
show_schedule,
2627
summary,
2728
top_match_points,
@@ -66,6 +67,7 @@ def argument_parser() -> argparse.ArgumentParser:
6667
scorer.add_subparser(subparsers)
6768
shift_matches.add_subparser(subparsers)
6869
show_league_table.add_subparser(subparsers)
70+
show_match_scores.add_subparser(subparsers)
6971
show_schedule.add_subparser(subparsers)
7072
summary.add_subparser(subparsers)
7173
top_match_points.add_subparser(subparsers)

sr/comp/cli/show_match_scores.py

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
from typing import Dict, List, Mapping, NamedTuple, Optional
2+
3+
from sr.comp.comp import SRComp
4+
from sr.comp.match_period import Match, MatchSlot
5+
from sr.comp.scores import BaseScores, LeaguePosition
6+
from sr.comp.types import ArenaName, GamePoints, MatchNumber, TLA
7+
8+
__description__ = "Show the game and league points achieved for each match"
9+
DISPLAYED_ZONES = 2
10+
11+
12+
class MatchCorner(NamedTuple):
13+
tla: Optional[TLA]
14+
ranking: Optional[LeaguePosition]
15+
game: Optional[GamePoints]
16+
league: Optional[int]
17+
18+
19+
class MatchResult(NamedTuple):
20+
num: MatchNumber
21+
arena: ArenaName
22+
display_name: str
23+
corners: Dict[int, MatchCorner]
24+
25+
26+
def collect_match_info(comp: SRComp, match: Match) -> MatchResult:
27+
from sr.comp.match_period import MatchType
28+
from sr.comp.scores import degroup
29+
30+
score_data: BaseScores
31+
league_points: Mapping[TLA, Optional[int]]
32+
game_points: Mapping[TLA, Optional[GamePoints]]
33+
ranking: Mapping[TLA, Optional[LeaguePosition]]
34+
35+
if match.type == MatchType.knockout:
36+
score_data = comp.scores.knockout
37+
elif match.type == MatchType.tiebreaker:
38+
score_data = comp.scores.tiebreaker
39+
elif match.type == MatchType.league:
40+
score_data = comp.scores.league
41+
42+
match_id = (match.arena, match.num)
43+
44+
if match_id in score_data.game_points:
45+
league_points = score_data.ranked_points[match_id]
46+
game_points = score_data.game_points[match_id]
47+
ranking = degroup(score_data.game_positions[match_id])
48+
else:
49+
league_points = {}
50+
game_points = {}
51+
ranking = {}
52+
for team in match.teams:
53+
if team is not None:
54+
league_points[team] = None
55+
game_points[team] = None
56+
ranking[team] = None
57+
58+
corner_data: Dict[int, MatchCorner] = {}
59+
60+
for corner, team in enumerate(match.teams):
61+
match_id = (match.arena, match.num)
62+
63+
if team: # corner occupied
64+
corner_data[corner] = MatchCorner(
65+
tla=team,
66+
ranking=ranking[team],
67+
game=game_points[team],
68+
league=league_points[team],
69+
)
70+
else:
71+
corner_data[corner] = MatchCorner(
72+
tla=None,
73+
ranking=None,
74+
game=None,
75+
league=None,
76+
)
77+
78+
return MatchResult(
79+
num=match.num,
80+
arena=match.arena,
81+
display_name=match.display_name,
82+
corners=corner_data,
83+
)
84+
85+
86+
def match_index(matches: List[MatchSlot], match_num: int) -> int:
87+
"Returns the index of the first slot that contains the given match number"
88+
for idx, slots in enumerate(matches):
89+
for match in slots.values():
90+
if match.num == match_num:
91+
return idx
92+
93+
# if no match is found use the last one
94+
return idx
95+
96+
97+
def generate_displayed_headings(num_corners: int) -> List[str]:
98+
displayed_heading = ["Match"]
99+
100+
for _ in range(num_corners):
101+
displayed_heading.append("Zone")
102+
displayed_heading.append("TLA")
103+
displayed_heading.append("Rank")
104+
displayed_heading.append("Game")
105+
displayed_heading.append("League")
106+
107+
return displayed_heading
108+
109+
110+
def generate_displayed_match(match: MatchResult, num_corners: int) -> List[List[str]]:
111+
displayed_match = []
112+
displayed_corners = []
113+
114+
for zone, (tla, ranking, game, league) in match.corners.items():
115+
displayed_corner: List[str] = []
116+
117+
displayed_corner.append(str(zone))
118+
if tla is not None:
119+
displayed_corner.append(tla)
120+
displayed_corner.append("??" if ranking is None else str(ranking))
121+
displayed_corner.append("??" if game is None else str(game))
122+
displayed_corner.append("??" if league is None else str(league))
123+
else:
124+
displayed_corner.extend(['', '', '', ''])
125+
126+
displayed_corners.append(displayed_corner)
127+
128+
# wrap the number of zones to the DISPLAYED_ZONES constant
129+
for corner in range(0, num_corners, DISPLAYED_ZONES):
130+
# first row displays the match and arena information,
131+
# any extra rows leave this field blank
132+
if corner == 0:
133+
match_row = [f"{match.display_name} in {match.arena}"]
134+
else:
135+
match_row = [""]
136+
137+
for idx in range(DISPLAYED_ZONES):
138+
try:
139+
match_row.extend(displayed_corners[corner + idx])
140+
except IndexError:
141+
# pad the number of corners out to a multiple of DISPLAYED_ZONES
142+
match_row.extend(['', '', '', ''])
143+
144+
displayed_match.append(match_row)
145+
146+
return displayed_match
147+
148+
149+
def command(settings):
150+
import os.path
151+
152+
from tabulate import tabulate
153+
154+
comp = SRComp(os.path.realpath(settings.compstate))
155+
156+
match_results: List[MatchResult] = []
157+
158+
filter_tla = settings.tla
159+
skip_filter = True
160+
161+
if filter_tla is not None:
162+
skip_filter = False
163+
# validate TLA exists
164+
if filter_tla not in comp.teams.keys():
165+
exit(f"TLA {filter_tla!r} not recognised")
166+
167+
if not settings.all and skip_filter:
168+
# get the index of the last scored match
169+
end_match = match_index(
170+
comp.schedule.matches,
171+
comp.scores.last_scored_match,
172+
) + 1 # include last scored match in results
173+
scan_matches = comp.schedule.matches[
174+
max(0, end_match - int(settings.limit)):end_match
175+
]
176+
else:
177+
scan_matches = comp.schedule.matches
178+
179+
for slots in scan_matches:
180+
match_results.extend(
181+
collect_match_info(comp, match)
182+
for match in slots.values()
183+
if filter_tla in match.teams or skip_filter
184+
)
185+
186+
if len(match_results) == 0:
187+
exit("No matches found for current filters")
188+
189+
num_teams_per_arena = comp.num_teams_per_arena
190+
191+
displayed_matches: List[List[str]] = []
192+
193+
for match in match_results:
194+
displayed_matches.extend(generate_displayed_match(match, num_teams_per_arena))
195+
196+
print(tabulate(
197+
displayed_matches,
198+
headers=generate_displayed_headings(DISPLAYED_ZONES),
199+
tablefmt='pretty',
200+
colalign=(
201+
('center',) + ('right', 'center', 'right', 'right', 'right') * DISPLAYED_ZONES
202+
),
203+
))
204+
205+
206+
def add_subparser(subparsers):
207+
parser = subparsers.add_parser(
208+
'show-match-scores',
209+
help=__description__,
210+
description=__description__,
211+
)
212+
parser.add_argument(
213+
'compstate',
214+
help="competition state repo",
215+
)
216+
parser.add_argument(
217+
'tla',
218+
nargs='?',
219+
help="filter to matches containing this TLA (ignores --limit)",
220+
)
221+
group = parser.add_mutually_exclusive_group()
222+
group.add_argument(
223+
'--all',
224+
action='store_true',
225+
help="show all matches (overrides --limit)",
226+
)
227+
group.add_argument(
228+
'--limit',
229+
default=15,
230+
help="how many recently scored matches to show (default: %(default)s)",
231+
)
232+
parser.set_defaults(func=command)

0 commit comments

Comments
 (0)