Skip to main content

Scoring System

Comprehensive scoring for stroke play and stableford formats, including countback tie resolution.

Scorecard workflow

┌──────────┐    ┌─────────────┐    ┌──────────┐    ┌────────┐
│ PENDING │───▶│ IN_PROGRESS │───▶│ ATTESTED │───▶│ LOCKED │
└──────────┘ └─────────────┘ └──────────┘ └────────┘
│ │ │ │
Entry Scoring Player Admin
created started verified finalized

States

StateDescription
PENDINGEntry exists, no scores entered
IN_PROGRESSScoring started, not complete
ATTESTEDPlayer has verified/signed scorecard
LOCKEDAdmin finalized, no further changes

Stableford scoring

Points table

Points awarded based on net score relative to par:

Net ScorePointsName
≥ Double Bogey0-
Bogey1Net Bogey
Par2Net Par
Birdie3Net Birdie
Eagle4Net Eagle
≤ Albatross5Net Albatross+

Strokes received

Based on stroke index (SI) and course handicap (CH):

function calculateStrokesReceived(
strokeIndex: number,
courseHandicap: number,
holesInPlay = 18
): number {
if (courseHandicap >= 0) {
// Receiving strokes
if (courseHandicap <= holesInPlay) {
return strokeIndex <= courseHandicap ? 1 : 0;
} else {
// High handicap - some holes get 2 strokes
const extraStrokes = courseHandicap - holesInPlay;
if (strokeIndex <= extraStrokes) return 2;
if (strokeIndex <= courseHandicap) return 1;
return 0;
}
} else {
// Plus handicap - giving strokes
const strokesToGive = Math.abs(courseHandicap);
return strokeIndex <= strokesToGive ? -1 : 0;
}
}

Examples:

Course HandicapSI 1SI 10SI 18
101 stroke1 stroke0 strokes
181 stroke1 stroke1 stroke
242 strokes1 stroke1 stroke
+2 (plus)-1 stroke0 strokes0 strokes

Calculation example

Hole 5: Par 4, SI 3, Gross 6
Course Handicap: 15

Strokes received: 1 (SI 3 ≤ CH 15)
Net score: 6 - 1 = 5
Net to par: 5 - 4 = +1 (Net Bogey)
Stableford points: 1

Stroke play scoring

Net score calculation

Net Score = Gross Score - Course Handicap

For hole-by-hole:

Net Hole Score = Gross Strokes - Strokes Received

To-par calculation

To Par = Gross Total - Course Par
Net To Par = Net Total - Course Par

Net Double Bogey (NDB)

Maximum score for handicapping purposes. Scores above NDB are adjusted down.

Formula

NDB = Par + 2 + Strokes Received

Examples

HoleParSICH 12CH 24NDB (CH 12)NDB (CH 24)
1451278
23150156
3511289

Application

function adjustScoreForHandicapping(
grossStrokes: number,
par: number,
strokeIndex: number,
courseHandicap: number,
holesInPlay = 18
): number {
const maxScore = par + 2 + calculateStrokesReceived(strokeIndex, courseHandicap, holesInPlay);
return Math.min(grossStrokes, maxScore);
}

Countback tie resolution

Standard WHS/R&A procedure for breaking ties.

Sequence

  1. Back 9: Sum of last 9 holes (holes 10-18)
  2. Back 6: Sum of last 6 holes (holes 13-18)
  3. Back 3: Sum of last 3 holes (holes 16-18)
  4. Last hole: Score on hole 18

Format-specific comparison

FormatBetter Score
StablefordHigher points
StrokeLower strokes

Data structure

interface CountbackScores {
back9: number; // Holes 10-18
back6: number; // Holes 13-18
back3: number; // Holes 16-18
last: number; // Hole 18
}

Example (Stableford)

PlayerTotalBack 9Back 6Back 3LastPosition
Alice362014831st
Bob362014722nd
Carol361913733rd

Alice and Bob tied on total (36) and back 9 (20) and back 6 (14), but Alice wins on back 3 (8 vs 7).

Position assignment

const positions = assignPositionsWithTies(results, 'STABLEFORD');
// Returns: Map<entryId, { position: number, isTied: boolean }>

Tied entries after full countback share position:

  • T1, T1, 3rd (two tied for 1st)
  • 1st, T2, T2, 4th (two tied for 2nd)

Round scoring

Calculate round totals

function calculateRoundStableford(
holes: Array<{ grossStrokes: number | null; par: number; strokeIndex: number }>,
courseHandicap: number
): { totalPoints: number; holesScored: number } {
let totalPoints = 0;
let holesScored = 0;

for (const hole of holes) {
if (hole.grossStrokes === null) continue;

const strokesReceived = calculateStrokesReceived(
hole.strokeIndex,
courseHandicap,
holes.length
);

const result = calculateStablefordPoints({
grossStrokes: hole.grossStrokes,
par: hole.par,
strokesReceived,
});

totalPoints += result.stablefordPoints;
holesScored++;
}

return { totalPoints, holesScored };
}

Scorecard data

interface Scorecard {
id: string;
entryId: string;
roundId: string;
status: 'PENDING' | 'IN_PROGRESS' | 'ATTESTED' | 'LOCKED';
grossTotal: number | null;
netTotal: number | null;
stablefordPoints: number | null;
courseHandicap: number | null;
playingHandicap: number | null;
countbackScores: CountbackScores | null;
scores: HoleScore[];
}

interface HoleScore {
holeNumber: number;
par: number;
strokeIndex: number;
grossStrokes: number | null;
netStrokes: number | null;
stablefordPoints: number | null;
isNoReturn: boolean;
}

9-hole rounds

TeeTime supports 9-hole competitions with adjusted calculations:

  • Strokes received calculated for 9 holes
  • Course handicap = 18-hole CH ÷ 2 (rounded)
  • Countback uses holes 5-9, 7-9, 8-9, 9
{
roundNumber: 1,
date: new Date('2025-12-15'),
courseId: 'course-123',
isNineHole: true,
holesInPlay: 9,
}

No Return (NR)

When a player picks up without completing a hole:

{
holeNumber: 7,
grossStrokes: null,
isNoReturn: true,
stablefordPoints: 0, // Zero points for Stableford
netStrokes: null, // NR for stroke play
}
  • Stableford: 0 points for NR holes
  • Stroke play: Typically disqualified or NR recorded