diff --git a/scoring/converter.py b/scoring/converter.py index 2dbaa73..95c4ac3 100644 --- a/scoring/converter.py +++ b/scoring/converter.py @@ -15,11 +15,13 @@ Converter as BaseConverter, InputForm, OutputForm, + parse_int, + render_int, ZoneId, ) from sr.comp.types import ScoreArenaZonesData, ScoreData, ScoreTeamData, TLA -from sr2025 import DISTRICTS, RawDistrict +from sr2025 import DISTRICTS, RawDistrict, ZONE_COLOURS class SR2025ScoreTeamData(ScoreTeamData): @@ -49,7 +51,10 @@ def form_district_to_score(self, form: InputForm, name: str) -> RawDistrict: """ return RawDistrict({ 'highest': form.get(f'district_{name}_highest', ''), - 'pallets': form.get(f'district_{name}_pallets', ''), + 'pallets': { + x: parse_int(form.get(f'district_{name}_pallets_{x}')) + for x in ZONE_COLOURS + }, }) def form_to_score(self, match: Match, form: InputForm) -> ScoreData: @@ -93,7 +98,10 @@ def score_team_to_form(self, tla: TLA, info: ScoreTeamData) -> OutputForm: def score_district_to_form(self, name: str, district: RawDistrict) -> OutputForm: return OutputForm({ f'district_{name}_highest': district['highest'].upper(), - f'district_{name}_pallets': district['pallets'].upper(), + **{ + f'district_{name}_pallets_{x}': render_int(district['pallets'].get(x)) + for x in ZONE_COLOURS + }, }) def score_to_form(self, score: ScoreData) -> OutputForm: @@ -132,6 +140,7 @@ def match_to_form(self, match: Match) -> OutputForm: for name in DISTRICTS: form[f'district_{name}_highest'] = '' - form[f'district_{name}_pallets'] = '' + for x in ZONE_COLOURS: + form[f'district_{name}_pallets_{x}'] = None return form diff --git a/scoring/score.py b/scoring/score.py index d2bbbb9..ca61a37 100644 --- a/scoring/score.py +++ b/scoring/score.py @@ -19,8 +19,6 @@ TOKENS_PER_ZONE = 6 -class District(RawDistrict): - pallet_counts: collections.Counter[str] class InvalidScoresheetException(Exception): @@ -72,15 +70,13 @@ def __init__(self, teams_data, arena_data): self._districts = arena_data['other']['districts'] for district in self._districts.values(): - district['pallet_counts'] = collections.Counter( - district['pallets'].replace(' ', ''), - ) + district['pallets'] = collections.Counter(district['pallets']) district['highest'] = district['highest'].replace(' ', '') - def score_district_for_zone(self, name: str, district: District, zone: int) -> int: + def score_district_for_zone(self, name: str, district: RawDistrict, zone: int) -> int: colour = ZONE_COLOURS[zone] - num_tokens = district['pallet_counts'][colour] + num_tokens = district['pallets'][colour] score = num_tokens * DISTRICT_SCORE_MAP[name] if colour in district['highest']: @@ -135,7 +131,7 @@ def validate(self, other_data): # Check that the pallets are valid colours. bad_pallets = {} for name, district in self._districts.items(): - extra = district['pallet_counts'].keys() - ZONE_COLOURS + extra = district['pallets'].keys() - ZONE_COLOURS if extra: bad_pallets[name] = extra if bad_pallets: @@ -151,8 +147,8 @@ def validate(self, other_data): bad_highest2 = {} for name, district in self._districts.items(): highest = district['highest'] - if highest and highest not in district['pallet_counts']: - bad_highest2[name] = (highest, district['pallet_counts'].keys()) + if highest and highest not in district['pallets']: + bad_highest2[name] = (highest, district['pallets'].keys()) if bad_highest2: detail = "\n".join( ( @@ -182,7 +178,7 @@ def validate(self, other_data): for name in DISTRICTS_NO_HIGH_RISE: district = self._districts[name] highest = district['highest'] - num_pallets = sum(district['pallet_counts'].values()) + num_pallets = sum(district['pallets'].values()) if num_pallets == 1 and highest: single_pallet_highest[name] = highest if single_pallet_highest: @@ -198,7 +194,7 @@ def validate(self, other_data): # arena are less than the expected number. totals = collections.Counter() for district in self._districts.values(): - totals += district['pallet_counts'] + totals += district['pallets'] bad_totals = [x for x, y in totals.items() if y > TOKENS_PER_ZONE] if bad_totals: raise InvalidScoresheetException( diff --git a/scoring/sr2025.py b/scoring/sr2025.py index 430ee8f..30134fb 100644 --- a/scoring/sr2025.py +++ b/scoring/sr2025.py @@ -4,8 +4,10 @@ class RawDistrict(TypedDict): + # Single pallet colour character highest: str - pallets: str + # Pallet colour -> count + pallets: dict[str, int] DISTRICT_SCORE_MAP = { diff --git a/scoring/template.yaml b/scoring/template.yaml index 98f333e..40f48ea 100644 --- a/scoring/template.yaml +++ b/scoring/template.yaml @@ -4,31 +4,31 @@ arena_zones: districts: outer_nw: highest: '' - pallets: '' + pallets: {G: 0, O: 0, P: 0, Y: 0} outer_ne: highest: '' - pallets: '' + pallets: {G: 0, O: 0, P: 0, Y: 0} outer_se: highest: '' - pallets: '' + pallets: {G: 0, O: 0, P: 0, Y: 0} outer_sw: highest: '' - pallets: '' + pallets: {G: 0, O: 0, P: 0, Y: 0} inner_nw: highest: '' - pallets: '' + pallets: {G: 0, O: 0, P: 0, Y: 0} inner_ne: highest: '' - pallets: '' + pallets: {G: 0, O: 0, P: 0, Y: 0} inner_se: highest: '' - pallets: '' + pallets: {G: 0, O: 0, P: 0, Y: 0} inner_sw: highest: '' - pallets: '' + pallets: {G: 0, O: 0, P: 0, Y: 0} central: highest: '' - pallets: '' + pallets: {G: 0, O: 0, P: 0, Y: 0} match_number: 0 teams: diff --git a/scoring/tests/test_scoring.py b/scoring/tests/test_scoring.py index 93d8522..ba32868 100644 --- a/scoring/tests/test_scoring.py +++ b/scoring/tests/test_scoring.py @@ -7,7 +7,7 @@ auto detect this and run the tests. """ -import pathlib +import pathlib, copy import random import sys import unittest @@ -62,15 +62,15 @@ def assertInvalidScoresheet(self, districts, *, code): f"Wrong error code, message was: {cm.exception}", ) - def setUp(self): + def setUp(self) -> None: self.teams_data = { 'GGG': {'zone': 0, 'present': True, 'left_starting_zone': False}, 'OOO': {'zone': 1, 'present': True, 'left_starting_zone': False}, } - self.districts = { + self.districts: dict[str, RawDistrict] = { name: RawDistrict({ 'highest': '', - 'pallets': '', + 'pallets': {}, }) for name in DISTRICTS } @@ -108,7 +108,7 @@ def test_left_starting_zone(self) -> None: ) def test_outer_single(self) -> None: - self.districts['outer_nw']['pallets'] = 'G' + self.districts['outer_nw']['pallets'] = {'G': 1} self.assertScores( { 'GGG': 1, @@ -118,7 +118,7 @@ def test_outer_single(self) -> None: ) def test_outer_multiple(self) -> None: - self.districts['outer_nw']['pallets'] = 'GGO' + self.districts['outer_nw']['pallets'] = {'G': 2, 'O': 1} self.assertScores( { 'GGG': 2, @@ -129,7 +129,7 @@ def test_outer_multiple(self) -> None: def test_outer_highest(self) -> None: self.districts['outer_nw']['highest'] = 'G' - self.districts['outer_nw']['pallets'] = 'GGO' + self.districts['outer_nw']['pallets'] = {'G': 2, 'O': 1} self.assertScores( { 'GGG': 4, @@ -140,7 +140,7 @@ def test_outer_highest(self) -> None: def test_outer_highest_self(self) -> None: self.districts['outer_nw']['highest'] = 'G' - self.districts['outer_nw']['pallets'] = 'GG' + self.districts['outer_nw']['pallets'] = {'G': 2} self.assertScores( { 'GGG': 4, @@ -151,7 +151,7 @@ def test_outer_highest_self(self) -> None: def test_outer_highest_use_other_team(self) -> None: self.districts['outer_nw']['highest'] = 'G' - self.districts['outer_nw']['pallets'] = 'GO' + self.districts['outer_nw']['pallets'] = {'G': 1, 'O': 1} self.assertScores( { 'GGG': 2, @@ -161,7 +161,7 @@ def test_outer_highest_use_other_team(self) -> None: ) def test_inner_single(self) -> None: - self.districts['inner_ne']['pallets'] = 'O' + self.districts['inner_ne']['pallets'] = {'O': 1} self.assertScores( { 'GGG': 0, @@ -171,7 +171,7 @@ def test_inner_single(self) -> None: ) def test_inner_multiple(self) -> None: - self.districts['inner_ne']['pallets'] = 'GOO' + self.districts['inner_ne']['pallets'] = {'G': 1, 'O': 2} self.assertScores( { 'GGG': 2, @@ -182,7 +182,7 @@ def test_inner_multiple(self) -> None: def test_inner_highest(self) -> None: self.districts['inner_ne']['highest'] = 'O' - self.districts['inner_ne']['pallets'] = 'GOO' + self.districts['inner_ne']['pallets'] = {'G': 1, 'O': 2} self.assertScores( { 'GGG': 2, @@ -192,7 +192,7 @@ def test_inner_highest(self) -> None: ) def test_central_single(self) -> None: - self.districts['central']['pallets'] = 'O' + self.districts['central']['pallets'] = {'O': 1} self.assertScores( { 'GGG': 0, @@ -202,7 +202,7 @@ def test_central_single(self) -> None: ) def test_central_multiple(self) -> None: - self.districts['central']['pallets'] = 'GOO' + self.districts['central']['pallets'] = {'G': 1, 'O': 2} self.assertScores( { 'GGG': 3, @@ -213,7 +213,7 @@ def test_central_multiple(self) -> None: def test_central_highest(self) -> None: self.districts['central']['highest'] = 'O' - self.districts['central']['pallets'] = 'GOO' + self.districts['central']['pallets'] = {'G': 1, 'O': 2} self.assertScores( { 'GGG': 3, @@ -224,9 +224,9 @@ def test_central_highest(self) -> None: def test_mixed(self) -> None: self.teams_data['OOO']['left_starting_zone'] = True - self.districts['outer_sw']['pallets'] = 'O' - self.districts['inner_sw']['pallets'] = 'G' - self.districts['central']['pallets'] = 'GO' + self.districts['outer_sw']['pallets'] = {'O': 1} + self.districts['inner_sw']['pallets'] = {'G': 1} + self.districts['central']['pallets'] = {'G': 1, 'O': 1} self.assertScores( { 'GGG': 5, @@ -236,11 +236,11 @@ def test_mixed(self) -> None: ) def test_mixed_highest(self) -> None: - self.districts['outer_sw']['pallets'] = 'O' + self.districts['outer_sw']['pallets'] = {'O': 1} self.districts['inner_sw']['highest'] = 'G' - self.districts['inner_sw']['pallets'] = 'G' + self.districts['inner_sw']['pallets'] = {'G': 1} self.districts['central']['highest'] = 'G' - self.districts['central']['pallets'] = 'GO' + self.districts['central']['pallets'] = {'G': 1, 'O': 1} self.assertScores( { 'GGG': 10, @@ -259,7 +259,7 @@ def test_bad_highest_pallet_letter(self) -> None: ) def test_bad_pallet_letter(self) -> None: - self.districts['outer_sw']['pallets'] = 'o' + self.districts['outer_sw']['pallets'] = {'o': 1} self.assertInvalidScoresheet( self.districts, code='invalid_pallets', @@ -267,7 +267,7 @@ def test_bad_pallet_letter(self) -> None: def test_outer_highest_requires_multiple_tokens_self(self) -> None: self.districts['outer_sw']['highest'] = 'O' - self.districts['outer_sw']['pallets'] = 'O' + self.districts['outer_sw']['pallets'] = {'O': 1} self.assertInvalidScoresheet( self.districts, code='impossible_highest_single_pallet', @@ -281,7 +281,7 @@ def test_missing_district(self) -> None: ) def test_extra_district(self) -> None: - self.districts['bees'] = dict(self.districts['central']) + self.districts['bees'] = copy.deepcopy(self.districts['central']) self.assertInvalidScoresheet( self.districts, code='invalid_districts', @@ -291,7 +291,7 @@ def test_extra_district(self) -> None: def test_spacey(self) -> None: self.districts['outer_nw']['highest'] = ' O ' - self.districts['outer_nw']['pallets'] = ' G O Y ' + self.districts['outer_nw']['pallets'] = {'G': 1, 'O': 1, 'Y': 1} self.assertScores( { 'GGG': 1, @@ -311,14 +311,14 @@ def test_highest_when_no_pallets(self) -> None: def test_highest_when_team_not_present(self) -> None: self.districts['outer_sw']['highest'] = 'Y' - self.districts['outer_sw']['pallets'] = 'OP' + self.districts['outer_sw']['pallets'] = {'O': 1, 'P': 1} self.assertInvalidScoresheet( self.districts, code='impossible_highest_pallet', ) def test_too_many_pallets(self) -> None: - self.districts['outer_sw']['pallets'] = ('G' * 7) + 'O' + self.districts['outer_sw']['pallets'] = {'G': 7, 'O': 1} self.assertInvalidScoresheet( self.districts, code='too_many_pallets', diff --git a/scoring/update.html b/scoring/update.html index 934be6f..2281ec6 100644 --- a/scoring/update.html +++ b/scoring/update.html @@ -15,17 +15,19 @@ {% endmacro %} -{% macro input_pallets(x, y, name) %} - +{% macro input_pallets(x, y, name, colour_symbol) %} + + + + {% endmacro %} @@ -33,8 +35,11 @@ {% macro district(name, x, y) %} - {{ input_highest(x + 2.5, y + 15, name) }} - {{ input_pallets(x, y + 65, name) }} + {{ input_highest(x + 2.5, y + 5, name) }} + {{ input_pallets(x, y + 40, name, 'O') }} + {{ input_pallets(x + 55, y + 40, name, 'P') }} + {{ input_pallets(x + 55, y + 75, name, 'Y') }} + {{ input_pallets(x, y + 75, name, 'G') }} {% endmacro %}