Skip to content

Commit

Permalink
Swap to 2×2 grid of pallet counts
Browse files Browse the repository at this point in the history
Following user testing at yesterday's Tech Day this turns out to
be the preferred input format for the paper forms.

This commit fully moves over to that style -- UI, converter, storage
and scoring logic.
  • Loading branch information
PeterJCLaw committed Nov 24, 2024
1 parent 4ca07e1 commit 67dbf89
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 64 deletions.
17 changes: 13 additions & 4 deletions scoring/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
24 changes: 9 additions & 15 deletions scoring/score.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@
TOKENS_PER_ZONE = 6


class District(RawDistrict):
pallet_counts: collections.Counter[str]


class InvalidScoresheetException(Exception):
def __init__(self, message: str, *, code: str) -> None:
super().__init__(message)
Expand Down Expand Up @@ -72,15 +68,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']:
Expand Down Expand Up @@ -135,7 +129,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:
Expand All @@ -151,8 +145,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(
(
Expand Down Expand Up @@ -182,7 +176,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:
Expand All @@ -198,7 +192,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(
Expand All @@ -210,5 +204,5 @@ def validate(self, other_data):


if __name__ == '__main__':
import libproton
import libproton # type: ignore[import-untyped]
libproton.main(Scorer)
4 changes: 3 additions & 1 deletion scoring/sr2025.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
18 changes: 9 additions & 9 deletions scoring/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
53 changes: 27 additions & 26 deletions scoring/tests/test_scoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
auto detect this and run the tests.
"""

import copy
import pathlib
import random
import sys
Expand Down Expand Up @@ -62,15 +63,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
}
Expand Down Expand Up @@ -108,7 +109,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,
Expand All @@ -118,7 +119,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,
Expand All @@ -129,7 +130,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,
Expand All @@ -140,7 +141,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,
Expand All @@ -151,7 +152,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,
Expand All @@ -161,7 +162,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,
Expand All @@ -171,7 +172,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,
Expand All @@ -182,7 +183,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,
Expand All @@ -192,7 +193,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,
Expand All @@ -202,7 +203,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,
Expand All @@ -213,7 +214,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,
Expand All @@ -224,9 +225,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,
Expand All @@ -236,11 +237,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,
Expand All @@ -259,15 +260,15 @@ 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',
)

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',
Expand All @@ -281,7 +282,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',
Expand All @@ -291,7 +292,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,
Expand All @@ -311,14 +312,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',
Expand Down
Loading

0 comments on commit 67dbf89

Please sign in to comment.