Files
garmin-padel/docs/superpowers/specs/2026-07-02-padel-scoring-design.md
2026-07-04 09:58:02 +02:00

9.1 KiB
Raw Blame History

Padel Scoring App — Design Spec

Date: 2026-07-02 Target: Garmin Connect IQ watch-app (Monkey C), product venu445mm (Venu 4, 45mm, round AMOLED), minApiLevel 6.0.2. Status: Approved design, pre-implementation.

1. Purpose

A padel score tracker for the watch. It shows the live score (sets / games / points) and a top-down court diagram whose highlighted quadrant tells you who serves the next point. Points are recorded by swiping. No GPS, no recorded Garmin activity, no match history in v1.

2. Screen layout

Round screen. A top band (heart rate) and a bottom band (time) frame a central row split into three columns: left = sets + games, center = court, right = current-game points.

            ♥ 142  ▮▮▮▯▯               ← heart rate + zone (top band)

     S   G                    pts
     2   4     +------+------+  15      ← your team (upper number in each column)
     1   3     |      |      |  40      ← opponents (lower number)
               +------+------+ net
               |      |  ● ← yellow ball = next server
               +------+------+

                  14:32                 ← current time (bottom band)
  • Left column — sets & games. Two mini-columns: S (sets) and G (games). A small label (S, G) sits above each pair of numbers; under it, two stacked numbers — your team on top, opponents below. Sets in bold accent color, games in accent color.
  • Right column — points. A small pts label (same small font as S/G) above two stacked numbers for the current game — your team on top, opponents below — in white (0 / 15 / 30 / 40, and AD if advantage is enabled).
  • Center — court. Simplified top-down padel court, your team the bottom half and opponents the top half. A small yellow ball is drawn in the quadrant of the next server (see §4); it moves as serve rotates. Nothing else is highlighted.
  • Top band — heart rate. Current heart rate (bpm) with a zone indicator (the current HR zone 15, shown as a short colored bar / zone number). Requires a heart-rate sensor reading (see Permissions below).
  • Bottom band — time. Current time (HH:MM), refreshed by a 1-second timer.

The small S / G / pts labels share one small font and a muted (dim grey) color so the numbers stay dominant.

Accent color. A single constant (proposed: a vivid padel green/teal) used for the sets and games numbers, so it can be changed in one place. The serve ball is yellow (its own constant).

Permissions. Reading live heart rate requires the Sensor permission; deriving the zone from the user's profile requires the UserProfile permission. Both are added via the Monkey C: Edit Permissions palette command (which regenerates manifest.xml).

3. Interaction model

During play (gestures):

  • Swipe up → point to your team.
  • Swipe down → point to the opponents.
  • Swipe leftundo the last recorded point (steps back through game/set boundaries too).

Before the first point (tap):

  • A tap toggles the starting server between bottom-right (your team serves first) and upper-left (opponents serve first) — the yellow ball jumps between those two quadrants. These are the only two options because the first point of any game is served from the deuce (right) court, and the opponents' right-hand court maps to the screen's upper-left. Tapping is only accepted while the match is at its very start (no points recorded yet).

At match end:

  • The court shows a "won by your team / opponents" state and stops accepting points; undo still works. A tap starts a fresh match, returning to server-positioning.

4. Serving / court geometry

Your team = bottom half, opponents = top half. The yellow ball is drawn in the quadrant determined by (serving team) × (serve side):

Serving team Serve side Quadrant
Your team deuce (right) bottom-right
Your team ad (left) bottom-left
Opponents deuce (their right) upper-left
Opponents ad (their left) upper-right

Serve side by point parity. Even number of points played so far in the current game → deuce (right); odd → ad (left). Game start (0-0) is always deuce.

Serving team.

  • Normal game: one team serves the entire game; the serving team alternates after each completed game.
  • The first server of the match is chosen by tap (see §3).

Tiebreak serving (when tiebreak is enabled and reached, see §5):

  • Serve side still follows point parity: even total tiebreak points → deuce, odd → ad.
  • The first server serves 1 point, then service alternates every 2 points. (Server for the point with P points already played = the team given by floor((P+1)/2) mod 2 offset from the team due to serve at 6-6.)

5. Scoring rules

Rules are held in a RulesConfig object that the engine reads as parameters, so the future settings screen can drive them without an engine rewrite. v1 defaults (all overridable later):

  • Points in a game: 0 / 15 / 30 / 40.
  • 40-40 resolution: golden point (default). Next point at 40-40 wins the game. When RulesConfig is set to advantage instead, 40-40 = deuce, then advantage (AD), then game; must win by two points.
  • Set: first to 6 games, win by 2.
  • 6-6: 7-point tiebreak (win by 2). When tiebreak is disabled in RulesConfig, the set continues until a team leads by 2 games.
  • Match: best of 3 sets (first to 2 sets).

6. Architecture (Monkey C)

Keeps the scaffold's file-per-role split; adds two logic-only files. The scoring engine imports no WatchUi/Graphics, so it is unit-testable in isolation.

  • source/PadelMatch.mc — the scoring engine. Holds points/games/sets for both teams, the serving team, tiebreak state, and match-over/winner. Public surface:
    • pointTo(team) — record a point, advancing game/set/match as needed.
    • undo() — revert the last recorded point.
    • servingQuadrant() — returns which of the four quadrants holds the serve ball.
    • setStartingServer(team) / toggle — only valid at match start.
    • accessors for the display: points (per team, formatted), games, sets, isMatchOver(), winner().
    • Undo strategy: push a full state snapshot onto a stack before each pointTo; undo pops and restores. This makes swipe-left correct across point/game/set boundaries with no special-casing.
  • source/RulesConfig.mc — the rules struct (golden-point flag, sets-to-win, games-per-set, tiebreak flag/target) plus the v1 defaults.
  • source/CourtRenderer.mc — draws the top-down court and the yellow serve ball in a given quadrant, so the view stays focused on layout. Pure drawing against a Dc.
  • source/HeartRateProvider.mc — wraps the heart-rate sensor and zone lookup. Enables Sensor.SENSOR_HEARTRATE, exposes the latest bpm, and maps it to a zone 15 using UserProfile.getHeartRateZones. Returns a "no reading" state when HR is unavailable (simulator / no strap) so the view can render a placeholder.
  • source/garmin-padelView.mcWatchUi.View. onUpdate draws the top HR band, the left sets/games column, the center court (via CourtRenderer), the right points column, and the bottom time band. A 1-second Timer calls requestUpdate to keep the clock (and HR) live.
  • source/garmin-padelDelegate.mcBehaviorDelegate. Maps onSwipe (up/down/left) and onTap (pre-match server toggle; new match after match end) to the engine, then requestUpdate.
  • source/garmin-padelApp.mc — owns the PadelMatch, RulesConfig, and HeartRateProvider instances; wires the view + delegate; enables sensors on start and disables them on stop.

Resources (resources/): the whole screen is drawn programmatically in onUpdate rather than via a static layout, since positions depend on state. strings.xml holds team labels and the match-end text; the template menu can be removed or repurposed later. Sensor + UserProfile permissions are declared in manifest.xml via the Edit Permissions command.

7. Testing

  • Engine unit tests (monkeyc --unit-test with a test runner) cover PadelMatch:
    • point → game → set → match progression (golden point and advantage modes),
    • tiebreak entry at 6-6, tiebreak win-by-2,
    • serving team alternation between games and serve-side parity within a game,
    • tiebreak serving rotation,
    • servingQuadrant() mapping for all four cases,
    • undo() across point/game/set boundaries and at match start (no-op).
  • Rendering (CourtRenderer, view, HR band) is verified in the simulator (HR via the simulator's data-simulation, since there is no real strap).

8. Deferred (not in v1)

  • Settings screen (drives RulesConfig; makes the top/bottom info slots configurable).
  • Match history / saved matches.
  • Resume-after-close persistence (engine state is in memory only for v1).
  • Recorded Garmin activity, GPS. (Heart rate is in v1 as a read-only display, but the match is still not recorded as a Garmin activity.)