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, or both when they name the same side (two spellings that disagree are refused). 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): ScoringActionResult
Add a point to the match. This is the primary input method for point-by-point scoring.
A point added after the match is COMPLETED is not refused, and it does not change the score
(since 7.5.0). Since 7.7.0 nothing records it either: no history entry (so undo cannot pop it), no
onPoint event, and its penaltyType is not stamped on the last point played. The same holds for a
point that names no winner. To add a penalty or a note to a point already played, use
decoratePoint or editPoint.
The timeline records every input the call supplied, including fields beyond AddPointOptions that a
host passes as decorations, so a rebuild (undo, redo, editPoint, removePoint) replays the
point exactly as recorded: its scoreValue, its wrongSide/wrongServer/penaltyPoint flags and
its decorations survive. A server the engine calculated is not an input and is not recorded; a
rebuild derives it again. Nor is any other field the scorer calculates (serveSide, score,
pointNumber, the points-to fields): passed in, it is overwritten on the point and left out of the
timeline.
An input the scorer cannot take is refused with an error before anything is written (no point, no
timeline entry, no event): a winner other than 0 or 1, a winningSide other than 1 or 2, the two
spellings of a side naming different sides (likewise server/serverSideNumber), or a scoreValue
that is negative or not finite. A zero scoreValue is allowed. A point that names no winner is
ignored, as before. A scoreValue other than 1 is refused where a point is not a unit of the score:
in a set whose points build games (a standard set, a tennis-family timed set, the tiebreak a level
timed set plays at time) and in a combined total. A configured point multiplier is not consulted
there either.
The result. addPoint, addGame, addSet and endSegment return { recorded: true }, or
{ recorded: false, ignored } for an input the engine accepted and recorded nothing for: NO_WINNER
(a point naming no winner), MATCH_COMPLETED (any input after the match is COMPLETED),
NO_SET_BEYOND_N (input past the N sets of an exactly format) and TIEBREAK_IN_PLAY (a segment end
during the tiebreak at time). A refusal throws and also writes nothing. Ignored input keeps the redo
stack; every recorded action clears it.
What the engine records about service. The server is side-level. serverParticipantId is host
metadata, recorded as given and never validated or derived. serveSide is the expected court half,
inferred from the score (a No-Ad deciding point, where the receiver chooses, is still reported as
deuce). A caller that saw the court half supplies it as observedServeSide ('deuce' or 'ad'),
recorded beside the expected serveSide. wrongSide and wrongServer are host decorations that do
not affect scoring. A server
correction (setServer) flips the side due to serve from that point on. An individual doubles
rotation, receiver identity and rule-based corrections (ITF Rule 27) are not modelled.
// 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
// Host-owned service metadata
serverParticipantId?: string; // Recorded without identity/rotation validation
observedServeSide?: 'deuce' | 'ad'; // Observed half, beside derived serveSide
// 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, or both when they name the same side; spellings that disagree are refused. 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): ScoringActionResult
Add a complete game result. Used for game-by-game tracking without point detail. winner must be 0
or 1 and a tiebreakScore two non-negative integers. A game after a closed set (won, or a timed
segment ended level) opens the next set, as a point does. In a tiebreak-only set (SET3-S:TB11, or
a match tiebreak in lieu of the final set) a game is the set won 1-0, its tiebreakScore the points,
which must win the race to the set's target for winner (11-9 in SET3-S:TB11; 11-10 is refused).
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): ScoringActionResult
Add a set total. Used by scoring modals where users enter set scores directly. The total is judged by the rule of the set it is entered for:
- a total the rule calls finished is recorded with the rule's winner (6-4, 7-6, 7-5; a tiebreak-only set read from its points; a timed total is a finished segment, the side with more winning it);
- an explicit
winningSidenaming the loser of a finished total, or a side that is not ahead, is refused; - an explicit
winningSidefor the side ahead on an unfinished total is accepted: a set-level mercy or manual end; - an unfinished total with no winner (3-1, 6-5 in a set to six) is recorded as the set in play.
A total entered while a set is in play (points or games already recorded in it) closes that set with the declared total; its points stay on the history and scoring continues in the next set.
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 }); // 3-6, won by side 2
engine.addSet({ side1Score: 4, side2Score: 1, winningSide: 1 }); // a manual end at 4-1
endSegment
endSegment(options?: EndSegmentOptions): ScoringActionResult
End a timed segment. For timed formats (S:T10P, etc.), this finalizes the segment's score and checks
match completion. It ends timed segments only: it is refused on any other kind of set, on a segment
already closed, and when there is no timed segment to end. A set-level mercy or manual end is addSet
with an explicit winningSide; a match-level stop is a matchUpStatus set outside scoring. During
the tiebreak a level segment plays at time it is ignored. reason is a free host string, recorded and
never interpreted.
engine.endSegment(); // End current segment
engine.endSegment({ setNumber: 2 }); // End specific segment
engine.endSegment({ reason: 'bolt_expired' });
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 },
],
currentSetScore: { side1Score: 2, side2Score: 1 },
currentGameScore: { side1Points: 2, side2Points: 1 }, // 30-15
});
The initial score is an action on the timeline. A rebuild replays it where it was set, so it
survives setState without a loadSupplementaryState, and undo removes it like any other action.
Its sets are judged by their set's rule, as addSet judges a total: 6-5 is not won, the deciding set
is judged by the final-set format, and a tiebreak-only set is read from its points. A baseline that
decides the match completes it. An unfinished last set is the set in play. A supplied server is
recorded as a server correction at the baseline, so it survives undo and rebuild. A second baseline
is refused once the timeline holds scoring; a correction uses editPoint, removePoint or undo.
A matchUpStatus or winningSide passed beside the sets is checked against them and refused only
when they disagree; a status set outside scoring (RETIRED, DEFAULTED) is not checked.
State Management
setState
setState(matchUp: MatchUp): void
Load matchUp state from a CODES MatchUp JSON object. Replaces all internal state, clears redo stack.
The engine keeps a copy, so changing the object afterwards does not change the engine; setLineUp
and setPointMultipliers copy their arguments the same way, and getPointMultipliers and
getSupplementaryState return copies.
The initial lineups a rebuild starts from are the matchUp's lineups with its substitutions undone
(since 7.7.0); loadSupplementaryState replaces them with the saved snapshot.
const savedMatchUp = JSON.parse(localStorage.getItem('matchUp'));
engine.setState(savedMatchUp);
getState
getState(): MatchUp
Get current matchUp state as a CODES MatchUp object. Returns a deep copy: changing it does not change the engine.
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; false when there is nothing to undo or count is below 1.
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; false when there is nothing to redo or count is below 1.
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. Returns a copy: changing it does not change the engine.
const score = engine.getScore();
// { sets: [{ side1Score: 6, side2Score: 4, winningSide: 1 }, ...], ... }
decisionRequired is true when the match is COMPLETED without a winner: a timed, aggregate or
exactly-N match that ended level with no declared decider, or a combined-total tie. The situation
of such a match has every flag false and decisionRequired: true; a host running an elimination draw
decides the match outside scoring.
getScoreboard
getScoreboard(options?: GetScoreboardOptions): string
Get the score as a display string. The game in play is read by the format governing the set in play: tennis points in a game, numeric points in a tiebreak-only set, a match tiebreak, a set tiebreak, the tiebreak a level timed segment plays at time, and a consecutive-points game.
engine.getScoreboard(); // '6-4, 3-6, 2-1 (30-15)'
// SET3-S:TB11 at 5-3 in the first game
engine.getScoreboard(); // '0-0 (5-3)'
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. A match that ended level with no declared decider is complete with no
winner (getWinner() is undefined), and takes no further segment.
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.
getSetInPlayFormat
getSetInPlayFormat(): SetFormatStructure | undefined
The format governing the set in play: the final-set format (-F:) in the live deciding set, the ordinary set
format otherwise. It is the format the engine judges the next point, game or set total by, so a scorer that
offers inputs (hiding a game control in a tiebreak-only segment, posting a winning set total) reads it here
rather than re-deriving which set is the decider. The rule covers best-of formats (the deciding set is reached
when both sides are one set from winning), exactly formats (set N) and aggregate formats with a declared decider
(set N + 1). Returns undefined if the format string is invalid. Once the match is complete it is the format a
further set would take; the engine records no further input.
const engine = new ScoringEngine({ matchUpFormat: 'SET3-S:6/TB7-F:TB10' });
engine.getSetInPlayFormat(); // { setTo: 6, tiebreakAt: 6, tiebreakFormat: { tiebreakTo: 7 } }
engine.addSet({ side1Score: 6, side2Score: 3 });
engine.addSet({ side1Score: 3, side2Score: 6 });
engine.getSetInPlayFormat(); // { tiebreakSet: { tiebreakTo: 10 } }: the match tiebreak
getInputLevels
getInputLevels(): { point: boolean; game: boolean; set: boolean }
The inputs the set in play can be scored with: points, games, or set totals (addSet). addPoint and
addGame refuse an input this reports as unavailable, and throw before recording anything. A segment
of outs takes set totals only (see Input Levels).
const engine = new ScoringEngine({ matchUpFormat: 'INN4XA-S:O3' });
engine.getInputLevels(); // { point: false, game: false, set: true }
engine.addSet({ side1Score: 2, side2Score: 1 }); // an inning's runs
getInputMode
getInputMode(): 'points' | 'games' | 'sets' | 'mixed' | 'none'
Get the input mode based on what types of scoring inputs have been recorded: points, games and set totals. Baselines, segment ends, server corrections, substitutions, challenges and annotations are not inputs.
engine.setServer(1);
engine.getInputMode(); // 'none'
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: 0 });
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();
// [{ action, point, game, set, needed, nextService, result, complete }, ...]
// point: { index, number, winner, server, score, result }
// game: { index, isTiebreak, complete, winner }; set: { index, complete, winner }
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.
A point added without a server is recorded with the server this method returns, so the displayed, recorded and replayed servers agree under either rule. A server the caller supplies is recorded as played, and the rule resumes after it.
The format's rule: game by game in standard sets, with Rule 5(b) in a tiebreak (a tennis match
tiebreak included); unit by unit in timed segments, continuously across segments, with Rule 5(b) in a
tiebreak at time; and under @RALLY the winner of the point before. A combined total (P{n}) and a
tiebreak-only format (S:TB{n}) derive no server: this method still returns 0 or 1 there, a
placeholder (the side the last setServer named, or side 1), and points record a server only when
the caller supplies one. isServerDerived() says which it is.
isServerDerived
isServerDerived(): boolean
Whether getNextServer() is the engine's derivation (true) or only a placeholder (false: a
combined total, a tiebreak-only format until each sport's own service rule is added, and a completed
match, which has no next point). The score
situation carries the same answer as serverDerived; where it is false, no game point or break
point is reported and no server-based statistic is produced.
const engine = new ScoringEngine({ matchUpFormat: 'SET5-S:TB11' });
engine.isServerDerived(); // false
Point Decoration & Editing
decoratePoint
decoratePoint(pointIndex: number, metadata: Record<string, any>): void
Attach additional metadata to a point in history. Any point can be decorated, including after the
match is COMPLETED: a penaltyType or an annotation is a correction to the record, not a point
played. Since 7.7.0 the metadata is kept through every later rebuild of the score (undo, redo,
editPoint, removePoint).
engine.decoratePoint(0, { courtPosition: 'deuce', shotType: 'forehand' });
engine.decoratePoint(12, { penaltyType: 'BALL_ABUSE', annotation: 'racquet thrown' });
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. Any
point can be edited, including after the match is COMPLETED. Since 7.7.0 every field given is kept
through later rebuilds (before, a recalculating edit kept only winner and server, and dropped
decorations on other points), penaltyType can be set, and a winner given as winningSide takes
effect as one given as winner does. With recalculate: false the score is left as it was; the
next rebuild applies the edit.
// Record a penalty against a point after the match
engine.editPoint(5, { penaltyType: 'COACHING' });
// 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
Deprecated, removed in the next major. Records the game index on its set's hardBoundaries as an
annotation only. Recalculation does not respect it: an edit before it still changes what follows, and a
rebuild (undo, redo, editPoint, removePoint) discards it.
engine.markHardBoundary({ setIndex: 0, gameIndex: 4 });
removePoint
removePoint(pointIndex: number, options?: { recalculate?: boolean }): void
Remove a point from history at any position — not just the most recent (which undo covers). The corresponding history entry is removed and pointIndex values on later entries are renormalised so they continue to match their new positions in points. Out-of-range indices are silently ignored.
By default, the score is rebuilt from history after removal and the redo stack is cleared. Pass { recalculate: false } to splice the point out without re-deriving state — useful when you'll immediately follow with another mutation that triggers its own rebuild.
// Remove a wrongly-recorded point in the middle of a game
engine.removePoint(5);
// Drop without recalculating (caller will rebuild)
engine.removePoint(5, { recalculate: false });
Use removePoint when the user intends to delete a specific historical point (e.g. a misclicked penalty in a long rally sequence); use undo when the user simply wants to step back the last action.
Persistence
getSupplementaryState
getSupplementaryState(): ScoringEngineSupplementaryState
Get engine-private state for persistence: data that isn't part of getState() but is needed to fully
restore the engine. The envelope carries a version (1), the redo stack, the initial lineup
snapshots, and the scoring configuration the history was scored under: the competitionFormat and
the pointMultipliers.
const matchUp = engine.getState();
const supplementary = engine.getSupplementaryState();
// Save both
localStorage.setItem('matchUp', JSON.stringify(matchUp));
localStorage.setItem('supplementary', JSON.stringify(supplementary));
getLegacyShapes
getLegacyShapes(): string[]
The legacy shapes the last rebuild met. A stored timeline that scored past a level close, as earlier
versions did, replays as it was scored and reports scoringPastLevelClose; the
rule applies to new input only. Likewise a stored entry the engine now refuses (invalid facts, a
segment end on a set that is not timed or is closed, a scoreValue where a point is not a unit, a
second baseline) reports refusedInput, and one it now ignores (input after COMPLETED or past N)
reports ignoredInput; each replays as it was recorded.
loadSupplementaryState
loadSupplementaryState(state: ScoringEngineSupplementaryState): void
Restore engine-private state from persistence. Call after setState() to fully restore engine state.
A saved competitionFormat and pointMultipliers replace the ones the engine was constructed
with: they are what the history was scored under, so a rebuild scores it the same way. An envelope
saved before the version field carries neither, and the engine keeps its own.
const matchUp = JSON.parse(localStorage.getItem('matchUp'));
const supplementary = JSON.parse(localStorage.getItem('supplementary'));
engine.setState(matchUp);
engine.loadSupplementaryState(supplementary);
// Redo stack, lineups and scoring configuration are now restored
engine.canRedo(); // true (if there were undone actions)
Service correction
setServer
setServer(side: 0 | 1, options?: { recordEntry?: boolean }): void
Correct the side serving the next point (zero-based). Earlier recorded servers stay unchanged. By
default this records a timeline action, clears redo and survives rebuilding/undo/redo. Internal
rule-driven corrections can use recordEntry: false: the correction is then attached to the entry
before it (the point just played), replayed with it and undone with it, so undo takes back the
point and its correction together and a rebuild keeps the server. With nothing on the timeline before
it, it is the match's initial server: kept on the record as history.initialServer (not a timeline
entry), so every rebuild and reload starts from it. Under serverRule: 'WINNER_SERVES' a correction
names the next point's server only, and the winner of that point serves after it. A correction that changes nothing records nothing. A side other
than 0 or 1 is refused, and so is any call on a format that does not parse. It corrects sides, not
individual doubles rotation.
Tennis tiebreak derivation uses one point for the first server, then alternating two-point turns.
The applicable final-set format is resolved independently; do not apply the opening-set service
pattern to a different final-set tiebreak. setServer(1) means side 2 should serve next.
Challenges and annotations
addChallenge
addChallenge(options: { sideNumber: 1 | 2; timestamp?: string; [key: string]: any }): void
Record a challenge: the side that challenged and any host fields, as a timeline entry
{ type: 'challenge', data: { sideNumber, ... } }. It changes no score, is kept through every rebuild,
clears redo, and undo takes it back when it was recorded last. A challenge entry a host wrote onto the
timeline itself in that shape is read the same way. A sideNumber other than 1 or 2 is refused.
addAnnotation
addAnnotation(data: Record<string, any>, options?: { timestamp?: string }): void
Record host data on the timeline ({ type: 'annotation', data }). It changes no score and is kept and
undone like a challenge.
Additional statistics
getStatObjects
getStatObjects(options?: StatisticsOptions): StatObject[]
Returns display-ready objects converted from getStatistics(options), with the same zero-based
setFilter. It calculates a result without adding an undo action. Import the published StatObject
type rather than a local display shape.
Lineups and substitution
setLineUp
setLineUp(sideNumber: 1 | 2, lineUp: TeamCompetitor[]): void
Sets a side's starting lineup and its rebuild snapshot. The side number is one-based. This is setup, not a timeline action; establish it before recording substitutions/points. Unknown sides do not gain an on-court lineup. The input's competitor objects are shallow-copied.
substitute
substitute(options: {
sideNumber: 1 | 2; outParticipantId: string; inParticipantId: string; timestamp?: string;
}): SubstitutionResult
Returns { success: true } after replacing an on-court participant and recording a pre-point timeline
action, or { success: false, error } with SAME_PARTICIPANT, NO_LINEUP, OUT_NOT_ON_SIDE or
IN_ALREADY_ON_COURT. Refusal does not record an action or clear redo. Accepted substitutions clear
redo and are restored by timeline replay. See substitution integration.
evaluateSubstitution
evaluateSubstitution(): { violations: SubstitutionViolation[] }
Advisory evaluation of configured permission, match/segment limits, between-game timing and matchUp
type. Possible values are NOT_ALLOWED, MAX_PER_MATCHUP, MAX_PER_SEGMENT, TIMING, MATCHUP_TYPE.
It neither writes history nor vetoes substitute; hosts enforce roster/time rules the scorer cannot
observe. No configured rules yields an empty list.
getActivePlayers
getActivePlayers(): { side1: string[]; side2: string[] }
Returns current lineup participant IDs (empty arrays without lineups). No history action. See event handlers and integration for tracking active players on points.
Multipliers and profiles
setPointMultipliers
setPointMultipliers(multipliers: PointMultiplier[]): void
Replaces scoring configuration with a deep copy. No timeline action and no immediate rescore: the new multipliers value the points recorded from then on. A point keeps the value it was scored at, which its entry records, so an undo, a redo or a reload does not re-value earlier points. Persist configuration with supplementary state.
getPointMultipliers
getPointMultipliers(): PointMultiplier[]
Returns a deep copy; editing it does not configure the scorer. Neither getter nor setter is an undo step.
getPenaltyProfile
getPenaltyProfile(): PenaltyProfile | undefined
getPointProfile
getPointProfile(): PointProfile | undefined
getTimerProfile
getTimerProfile(): TimerProfile | undefined
getTimeoutRules
getTimeoutRules(): TimeoutRules | undefined
getPenaltyBoxProfile
getPenaltyBoxProfile(): PenaltyBoxProfile | undefined
getSubstitutionRules
getSubstitutionRules(): SubstitutionRules | undefined
getPlayerRules
getPlayerRules(): PlayerRules | undefined
These seven accessors return the corresponding configured competition-format object, or undefined
if absent. They do not validate or execute its rules, throw on absence, or create undo actions. They
currently expose those configuration references: treat them as read-only. Host clocks, timeouts,
penalty-box timing and roster enforcement remain host responsibilities. See
format support and event integration.
Runtime handlers
setEventHandlers
setEventHandlers(handlers: ScoringEventHandlers | undefined): void
Replaces all handlers; pass undefined to clear them. Callback registration is not part of undo/redo
or serializable scoring state.
getEventHandlers
getEventHandlers(): ScoringEventHandlers | undefined
Returns the currently installed handler object (a reference). Neither method emits a scoring event. See the dispatch matrix for when callbacks run.
StatisticsOptions.setFilter, episode point/game/set indices and stored point indices are zero-based. The first set is setFilter: 0; CODES winningSide remains one-based.
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