Core API Reference
Complete method reference for the ScoringEngine class.
The ScoringEngine supports two indexing conventions for addPoint():
- 0-based (original):
winner: 0 | 1,server: 0 | 1— where0= side 1,1= side 2 - 1-based (CODES-aligned):
winningSide: 1 | 2,serverSideNumber: 1 | 2
You can use either convention. Provide winner or winningSide (not both). Both values are always stored on the resulting Point record. Return values like getWinner() use CODES convention (1 or 2).
Scoring Methods
addPoint
addPoint(options: AddPointOptions): void
Add a point to the match. This is the primary input method for point-by-point scoring.
// 0-based convention
engine.addPoint({ winner: 0 }); // Side 1 wins the point
engine.addPoint({ winner: 1, server: 0 }); // Side 2 wins, side 1 served
// 1-based convention (CODES-aligned)
engine.addPoint({ winningSide: 1 }); // Side 1 wins the point
engine.addPoint({ winningSide: 2, serverSideNumber: 1 }); // Side 2 wins, side 1 served
// With metadata (works with either convention)
engine.addPoint({ winner: 0, result: 'Ace' }); // Side 1 wins with an ace
engine.addPoint({ winningSide: 2, rallyLength: 12 }); // Side 2 wins after 12-shot rally
engine.addPoint({ winner: 0, timestamp: new Date().toISOString() });
// Doubles with specific server
engine.addPoint({ winningSide: 1, serverSideNumber: 1, serverParticipantId: 'player-A' });
// Override point value (e.g., power points in timed formats)
engine.addPoint({ winningSide: 1, scoreValue: 2 }); // Worth 2 points instead of 1
The AddPointOptions interface:
interface AddPointOptions {
// 0-based convention
winner?: 0 | 1; // Which side won (0 = side 1, 1 = side 2)
server?: 0 | 1; // Which side served (auto-derived if omitted)
// 1-based convention (CODES-aligned)
winningSide?: 1 | 2; // Which side won (1 = side 1, 2 = side 2)
serverSideNumber?: 1 | 2; // Which side served
// Doubles rotation
serverParticipantId?: string; // Specific participant serving
// Score override
scoreValue?: number; // Override score increment (default: 1)
// Metadata
timestamp?: string; // ISO timestamp
rallyLength?: number; // Number of shots in the rally
result?: string; // Point result label (e.g., 'Ace', 'Double Fault')
penaltyType?: string; // Penalty type if point was awarded via penalty
penaltyPoint?: boolean; // Whether this was a penalty point
wrongSide?: boolean; // Tracking flag for wrong-side serve
wrongServer?: boolean; // Tracking flag for wrong server
}
Provide winner or winningSide, not both. The engine normalizes internally — the resulting Point record always contains both winner and winningSide. The same applies to server / serverSideNumber.
Stored Point Record
After addPoint(), the resulting Point in matchUp.history.points always contains both indexing conventions regardless of which was passed in:
interface Point {
pointNumber: number; // Sequential (1-indexed)
winner: 0 | 1; // Always set
winningSide: 1 | 2; // Always set
server?: 0 | 1; // Set if server was provided or derived
serverSideNumber?: 1 | 2; // Set if server was provided or derived
serverParticipantId?: string; // Set if provided (doubles)
scoreValue?: number; // Set if override was provided
timestamp?: string; // ISO 8601
activePlayers?: [string, string] | [string[], string[]]; // Set if lineups exist
// ... plus result, rallyLength, serve metadata, etc.
}
addGame
addGame(options: AddGameOptions): void
Add a complete game result. Used for game-by-game tracking without point detail.
engine.addGame({ winner: 0 }); // Side 1 wins a game
engine.addGame({ winner: 1, tiebreakScore: [7, 5] }); // Side 2 wins tiebreak 7-5
addSet
addSet(options: AddSetOptions): void
Add a complete set score. Used by scoring modals where users enter finished set scores directly.
engine.addSet({ side1Score: 6, side2Score: 4 }); // 6-4
engine.addSet({ side1Score: 7, side2Score: 6, side1TiebreakScore: 7, side2TiebreakScore: 3 }); // 7-6(3)
engine.addSet({ side1Score: 3, side2Score: 6, winningSide: 2 }); // Explicit winner
endSegment
endSegment(options?: EndSegmentOptions): void
End a timed segment/period. For timed formats (S:T10P, etc.), this finalizes the current set's score and checks match completion.
engine.endSegment(); // End current segment
engine.endSegment({ setNumber: 2 }); // End specific segment
setInitialScore
setInitialScore(options: InitialScoreOptions): void
Set the score before beginning point-by-point tracking. Used when a tracker arrives mid-match.
engine.setInitialScore({
sets: [
{ side1Score: 6, side2Score: 3 },
{ side1Score: 4, side2Score: 6 },
],
currentSet: { side1Score: 2, side2Score: 1 },
currentGame: { side1Score: 30, side2Score: 15 },
});
State Management
setState
setState(matchUp: MatchUp): void
Load matchUp state from a CODES MatchUp JSON object. Replaces all internal state, clears redo stack.
const savedMatchUp = JSON.parse(localStorage.getItem('matchUp'));
engine.setState(savedMatchUp);
getState
getState(): MatchUp
Get current matchUp state as a CODES MatchUp object. Returns a direct reference (not a copy).
const matchUp = engine.getState();
localStorage.setItem('matchUp', JSON.stringify(matchUp));
reset
reset(): void
Reset match to initial state. Clears all points, entries, undo/redo stacks, and lineups.
engine.reset();
engine.getPointCount(); // 0
engine.isComplete(); // false
Undo / Redo
undo
undo(count?: number): boolean
Undo the last N actions. Works across all input types (points, games, sets, segments). Returns true if undo succeeded.
engine.undo(); // Undo last action
engine.undo(3); // Undo last 3 actions
redo
redo(count?: number): boolean
Redo the last N undone actions. Returns true if redo succeeded.
engine.redo(); // Redo last undone action
engine.redo(2); // Redo last 2 undone actions
canUndo
canUndo(): boolean
Check if there are actions available to undo.
canRedo
canRedo(): boolean
Check if there are actions available to redo.
getUndoDepth
getUndoDepth(): number
Get the number of actions that can be undone.
getRedoDepth
getRedoDepth(): number
Get the number of actions that can be redone.
Score Queries
getScore
getScore(): ScoreResult
Get the current score as a structured object with sets, current game score, and set scores.
const score = engine.getScore();
// { sets: [{ side1Score: 6, side2Score: 4, winningSide: 1 }, ...], ... }
getScoreboard
getScoreboard(options?: GetScoreboardOptions): string
Get the score as a display string.
engine.getScoreboard(); // '6-4 3-6 2-1 30-15'
getWinner
getWinner(): number | undefined
Get the match winner. Returns 1 (side 1) or 2 (side 2), or undefined if not complete.
isComplete
isComplete(): boolean
Check if the match is complete.
getPointCount
getPointCount(): number
Get the total number of points played.
getFormat
getFormat(): string
Get the matchUpFormat code string.
engine.getFormat(); // 'SET3-S:6/TB7'
Format Introspection
isNoAd
isNoAd(): boolean
Whether the format uses No-Advantage scoring (deciding point at deuce).
getSetsToWin
getSetsToWin(): number
Number of sets needed to win the match.
const engine = new ScoringEngine({ matchUpFormat: 'SET5-S:6/TB7' });
engine.getSetsToWin(); // 3
getTiebreakAt
getTiebreakAt(): number | null
Game count at which a tiebreak is played, or null if no tiebreak. Returns null for tiebreak-only formats (pickleball, etc.) since the entire set IS the tiebreak.
hasFinalSetTiebreak
hasFinalSetTiebreak(): boolean
Whether a tiebreak is played in the final/deciding set.
getFormatStructure
getFormatStructure(): FormatStructure | undefined
Get the parsed format structure for advanced consumers. Returns undefined if the format string is invalid.
getInputMode
getInputMode(): 'points' | 'games' | 'sets' | 'mixed' | 'none'
Get the input mode based on what types of entries have been recorded.
engine.addPoint({ winner: 0 });
engine.getInputMode(); // 'points'
engine.addGame({ winner: 1 });
engine.getInputMode(); // 'mixed'
Statistics & Analysis
getStatistics
getStatistics(options?: StatisticsOptions): MatchStatistics
Get match statistics calculated from point history. Returns counters, calculated stats, and summary.
const stats = engine.getStatistics();
const set1Stats = engine.getStatistics({ setFilter: 1 });
getEpisodes
getEpisodes(): Episode[]
Get the point history enriched with game/set/match context. Each episode contains point data plus game boundaries, set boundaries, and next server information. Suitable for timeline visualization and detailed analysis.
const episodes = engine.getEpisodes();
// [{ point, gameScore, setScore, matchScore, gameNumber, setNumber, ... }, ...]
getNextServer
getNextServer(): 0 | 1
Get who serves the next point. Uses format-driven server alternation by default, or WINNER_SERVES if set in the competition format.
Point Decoration & Editing
decoratePoint
decoratePoint(pointIndex: number, metadata: Record<string, any>): void
Attach additional metadata to a point in history.
engine.decoratePoint(0, { courtPosition: 'deuce', shotType: 'forehand' });
editPoint
editPoint(
pointIndex: number,
newData: Partial<AddPointOptions>,
options?: { recalculate?: boolean }
): void
Edit a point in history. By default, recalculates the score from the edited point forward.
// Fix a wrong winner call
engine.editPoint(5, { winner: 1 });
// Update metadata without recalculating
engine.editPoint(5, { result: 'Ace' }, { recalculate: false });
markHardBoundary
markHardBoundary(options: { setIndex: number; gameIndex: number }): void
Mark a game boundary as "hard". Edits before this boundary won't cascade past it during recalculation.
engine.markHardBoundary({ setIndex: 0, gameIndex: 4 });
Persistence
getSupplementaryState
getSupplementaryState(): ScoringEngineSupplementaryState
Get engine-private state for persistence. Returns the redo stack and initial lineup snapshots — data that isn't part of getState() but is needed to fully restore the engine.
const matchUp = engine.getState();
const supplementary = engine.getSupplementaryState();
// Save both
localStorage.setItem('matchUp', JSON.stringify(matchUp));
localStorage.setItem('supplementary', JSON.stringify(supplementary));
loadSupplementaryState
loadSupplementaryState(state: ScoringEngineSupplementaryState): void
Restore engine-private state from persistence. Call after setState() to fully restore engine state.
const matchUp = JSON.parse(localStorage.getItem('matchUp'));
const supplementary = JSON.parse(localStorage.getItem('supplementary'));
engine.setState(matchUp);
engine.loadSupplementaryState(supplementary);
// Redo stack and lineups are now restored
engine.canRedo(); // true (if there were undone actions)
Related Documentation
- Overview — Introduction and architecture
- Event Handlers & Integration — Event system and competitionFormat profiles
- Multi-Sport Format Support — Format strings for different sports
- Visualization Applications — Building visualizations with ScoringEngine data