Documentation
¶
Overview ¶
Package chesspairing provides chess tournament pairing, scoring, and tiebreaking engines in pure Go. It implements FIDE-approved Swiss pairing systems (Dutch C.04.3, Burstein C.04.4.2, Dubov C.04.4.1, Lim C.04.4.3, Double-Swiss C.04.5, and Team Swiss C.04.6), Keizer pairing, and round-robin pairing, along with standard, Keizer, and football scoring systems and 25 tiebreaker algorithms.
Engines operate on in-memory data structures (TournamentState, PlayerEntry, RoundData) and have no I/O, database, or network dependencies. They are safe for concurrent use when each goroutine supplies its own TournamentState.
Context: all engine interface methods accept context.Context as their first parameter for API compatibility with service layers. However, since all computation is CPU-bound and in-memory (no I/O, no network), the context is not currently checked for cancellation. Callers should still pass a context for forward compatibility.
Forfeit handling across subsystems ¶
A single FIDE-aligned semantics for forfeits doesn't exist: the rule depends on the question being asked. Subsystems in this module make different choices, all consistent with the FIDE handbook:
Subsystem Single forfeit (1-0f / 0-1f) Double forfeit (0-0f) ----------------- ------------------------------ -------------------------- Scorer Awards PointForfeitWin/Loss Awards 0 to both TieBreaker Excluded from opponent data Excluded from opponent data PlayedPairs Excluded by default Always excluded standings.Build Counts as +1 win or +1 loss 0 across the board
The PlayedPairs default (excluding single forfeits) matches FIDE's position that a forfeit didn't really happen as a chess game and therefore the players may meet again. Setting HistoryOptions.IncludeForfeits to true crosses into house-rule territory.
Bye types and absences ¶
Six ByeType values cover the unplayed-round cases. They differ in scoring weight, in whether they count as a played round for tiebreakers, in pairing impact, and in TRF representation. The matrix below summarises the semantics; specifics for the standard scorer live on its Options fields, and Keizer scoring's bye and absent values are valuation-relative rather than fixed.
ByeType Standard pts (default) Counts as played PAB-tracked TRF code ----------------- ----------------------- ----------------- ------------ -------- ByePAB PointBye (1.0) yes yes F ByeHalf PointDraw (0.5) yes no H ByeZero PointLoss (0.0) yes no Z ByeAbsent PointAbsent (0.0) no no U ByeExcused PointExcused (0.0) no no (directive) ByeClubCommitment PointClubCommitment (0) no no (directive)
"Counts as played" affects rounds-played tiebreakers and Buchholz virtual-opponent calculations. "PAB-tracked" matters for the Swiss pairers' constraint that no player gets the pairing-allocated bye twice. "TRF code" is the round-column letter; ByeExcused and ByeClubCommitment have no TRF round-column representation and are carried in chesspairing directive comments instead (see the trf sub-package).
Pre-assigned byes are configured via TournamentState.PreAssignedByes. The Swiss pairers honour them by partitioning the player pool before the matching step, so a pre-assigned bye of any type passes through to the PairingResult unchanged. The PAB-uniqueness constraint only applies to algorithmically allocated byes.
Player withdrawals use PlayerEntry.WithdrawnAfterRound (a *int). state.IsActiveInRound(id, n) and state.ActivePlayerIDs(n) are the canonical accessors. Tiebreakers consult the active filter contemporaneously per historical round, so a player withdrawn after round 3 still contributes to opponents' Buchholz for rounds 1 and 2.
Index ¶
- func BoolPtr(v bool) *bool
- func DefaultTiebreakers(system PairingSystem) []string
- func Float64Ptr(v float64) *float64
- func GetBool(m map[string]any, key string) (bool, bool)
- func GetFloat64(m map[string]any, key string) (float64, bool)
- func GetInt(m map[string]any, key string) (int, bool)
- func GetString(m map[string]any, key string) (string, bool)
- func IntPtr(v int) *int
- func PlayedPairs(state *TournamentState, opts HistoryOptions) [][]string
- func StringPtr(v string) *string
- type ByeEntry
- type ByeType
- type GameData
- type GamePairing
- type GameResult
- type HistoryOptions
- type NamedValue
- type Pairer
- type PairingConfig
- type PairingResult
- type PairingSystem
- type PlayerEntry
- type PlayerScore
- type ResultContext
- type RoundData
- type Scorer
- type ScoringConfig
- type ScoringSystem
- type Standing
- type TieBreakValue
- type TieBreaker
- type TournamentInfo
- type TournamentState
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DefaultTiebreakers ¶
func DefaultTiebreakers(system PairingSystem) []string
DefaultTiebreakers returns the FIDE-recommended tiebreaker order for the given pairing system.
func Float64Ptr ¶
Float64Ptr returns a pointer to v. Used by options packages to set pointer-nil-pattern fields.
func GetBool ¶
GetBool extracts a bool from a map. Returns (false, false) if the key is missing or has an incompatible type.
func GetFloat64 ¶
GetFloat64 extracts a float64 from a map, handling float64, int, and int64 value types. Returns (0, false) if the key is missing or has an incompatible type.
func GetInt ¶
GetInt extracts an int from a map, handling int, int64, and float64 value types. Returns (0, false) if the key is missing or has an incompatible type.
func GetString ¶
GetString extracts a string from a map. Returns ("", false) if the key is missing or has an incompatible type.
func PlayedPairs ¶ added in v0.2.0
func PlayedPairs(state *TournamentState, opts HistoryOptions) [][]string
PlayedPairs returns the set of player pairs that have already been paired in the tournament, suitable for use as a forbidden-pair constraint when computing the next round.
Each entry in the returned slice is a two-element slice of player IDs sorted lexicographically. The outer slice is sorted lexicographically by first then second element so the output is deterministic.
Pending games (ResultPending) are skipped — they have not been played yet. Bye entries are not pairs and are skipped entirely. Forfeit handling is controlled by opts.IncludeForfeits; double forfeits are never included.
Returns nil for an empty result.
Forfeit handling across this package is documented at the package level — see the package comment in chesspairing.go.
Types ¶
type ByeType ¶
type ByeType int
ByeType classifies how a bye is scored.
const ( ByePAB ByeType = iota // Pairing-Allocated Bye (full point, TRF "F") ByeHalf // Half-point bye (TRF "H") ByeZero // Zero-point bye (TRF "Z") ByeAbsent // Absent/unpaired, unexcused (TRF "U") ByeExcused // Excused absence (notified in advance) ByeClubCommitment // Club commitment (absent for interclub team duty) )
func ParseByeType ¶ added in v0.2.0
ParseByeType parses a string into a ByeType. Matching is case-insensitive and surrounding whitespace is trimmed. Accepted spellings include both the String() forms and the TRF letter codes:
"PAB", "F" -> ByePAB "Half", "H" -> ByeHalf "Zero", "Z" -> ByeZero "Absent", "U" -> ByeAbsent "Excused" -> ByeExcused "ClubCommitment" -> ByeClubCommitment
The TRF letters are accepted because they are the canonical TRF-side spelling and downstream consumers reading TRF data may have them in hand. String() output is unchanged.
Returns an error wrapping the input on unknown values. Empty input is rejected.
type GameData ¶
type GameData struct {
WhiteID string
BlackID string
Result GameResult
IsForfeit bool
}
GameData is a single game result for engine consumption.
type GamePairing ¶
GamePairing is a single pairing assignment for a round.
type GameResult ¶
type GameResult string
GameResult represents the outcome of a chess game.
const ( ResultWhiteWins GameResult = "1-0" ResultBlackWins GameResult = "0-1" ResultDraw GameResult = "0.5-0.5" ResultPending GameResult = "*" ResultForfeitWhiteWins GameResult = "1-0f" ResultForfeitBlackWins GameResult = "0-1f" ResultDoubleForfeit GameResult = "0-0f" )
func ParseGameResult ¶ added in v0.2.0
func ParseGameResult(s string) (GameResult, error)
ParseGameResult parses a string into a GameResult. Matching is case-insensitive and surrounding whitespace is trimmed. Internal spaces around the dash are tolerated ("1 - 0" parses as ResultWhiteWins).
Accepted spellings:
"1-0", "1 - 0" -> ResultWhiteWins "0-1", "0 - 1" -> ResultBlackWins "0.5-0.5", "1/2-1/2", -> ResultDraw "0.5 - 0.5", "1/2 - 1/2" "*" -> ResultPending "1-0f", "1 - 0 f" -> ResultForfeitWhiteWins "0-1f", "0 - 1 f" -> ResultForfeitBlackWins "0-0f", "0 - 0 f" -> ResultDoubleForfeit
Returns an error wrapping the input on unknown values. Empty input is rejected.
func (GameResult) IsDoubleForfeit ¶
func (gr GameResult) IsDoubleForfeit() bool
IsDoubleForfeit returns true if both players forfeited. Double-forfeit games are excluded from both pairing and scoring — the game never happened.
func (GameResult) IsForfeit ¶
func (gr GameResult) IsForfeit() bool
IsForfeit returns true if the result is a forfeit (single or double). Forfeit games are excluded from pairing history — players can be re-paired within the same period.
func (GameResult) IsRecordable ¶
func (gr GameResult) IsRecordable() bool
IsRecordable returns true if the game result is a valid result that can be recorded by a user. ResultPending ("*") is valid but not recordable — it is the initial state set by the system when a game is created.
func (GameResult) IsValid ¶
func (gr GameResult) IsValid() bool
IsValid returns true if the game result is a recognized value.
type HistoryOptions ¶ added in v0.2.0
type HistoryOptions struct {
// IncludeForfeits controls whether single-forfeit games count as
// "played" for the purpose of repeat-pair detection.
//
// false (FIDE / library-canonical, default): a forfeited game is not
// in the pairing history; the two players may be paired again. This
// matches the documented forfeit semantics in this package — a single
// forfeit awards the point but the game did not happen for pairing
// purposes.
//
// true (house-rule territory): forfeited games count as played; the
// two players will not be re-paired. Some local regulations require
// this; pass true only when local rules call for it.
//
// Double-forfeit games are never included regardless of this option,
// matching ResultDoubleForfeit's documented "the game never happened"
// semantics.
IncludeForfeits bool
}
HistoryOptions controls how PlayedPairs interprets tournament history.
The zero value matches the FIDE / library-canonical defaults: forfeited games do not count as played for repeat-pair detection. Add this struct to a function call as HistoryOptions{} when the defaults are correct.
type NamedValue ¶
type NamedValue struct {
ID string `json:"id"`
Name string `json:"name"`
Value float64 `json:"value"`
}
NamedValue pairs a tiebreaker identifier with its computed value.
type Pairer ¶
type Pairer interface {
Pair(ctx context.Context, state *TournamentState) (*PairingResult, error)
}
Pairer generates pairings for a round given tournament state.
type PairingConfig ¶
type PairingConfig struct {
System PairingSystem
Options map[string]any
}
PairingConfig holds per-period pairing settings.
type PairingResult ¶
type PairingResult struct {
Pairings []GamePairing
Byes []ByeEntry
Notes []string
}
PairingResult is returned by a Pairer.
type PairingSystem ¶
type PairingSystem string
PairingSystem identifies which pairing algorithm to use.
const ( PairingDutch PairingSystem = "dutch" PairingBurstein PairingSystem = "burstein" PairingDubov PairingSystem = "dubov" PairingLim PairingSystem = "lim" PairingDoubleSwiss PairingSystem = "doubleswiss" PairingTeam PairingSystem = "team" PairingKeizer PairingSystem = "keizer" PairingRoundRobin PairingSystem = "roundrobin" )
func ParsePairingSystem ¶ added in v0.2.0
func ParsePairingSystem(s string) (PairingSystem, error)
ParsePairingSystem parses a string into a PairingSystem. Matching is case-insensitive and surrounding whitespace is trimmed. Accepted values are the canonical names ("dutch", "burstein", "dubov", "lim", "doubleswiss", "team", "keizer", "roundrobin") plus the bbpPairings / JaVaFo aliases ("fide-dutch", "fide-burstein", "fide-dubov", "fide-lim", "double-swiss", "round-robin", "rr").
Returns an error wrapping the input on unknown values. Empty input is rejected.
func (PairingSystem) IsValid ¶
func (p PairingSystem) IsValid() bool
IsValid returns true if the pairing system is a recognized value.
type PlayerEntry ¶
type PlayerEntry struct {
ID string
DisplayName string
Rating int
Federation string // FIDE federation code (e.g. "NED", "USA", "IND"). Empty if unknown.
FideID string // FIDE player ID number. Empty if unknown.
Title string // FIDE title code (GM, IM, FM, WGM, WIM, WFM, CM, WCM). Empty if untitled.
Sex string // "m" or "w". Empty if unknown.
BirthDate string // Birth date as YYYY/MM/DD. Empty if unknown.
JoinedRound int // Round number the player joined. 0 or 1 means original player (joined from the start).
WithdrawnAfterRound *int // Last round the player participated in; nil means still active.
}
PlayerEntry represents a player for engine purposes.
JoinedRound and WithdrawnAfterRound bracket the player's active window. JoinedRound = 0 or 1 means the player was present from round 1. WithdrawnAfterRound names the last round in which the player participated; from *WithdrawnAfterRound + 1 onward they are inactive. nil means the player has not withdrawn. Use TournamentState.IsActiveInRound rather than reading these fields directly.
type PlayerScore ¶
PlayerScore holds a player's calculated score from the scoring engine.
type ResultContext ¶
type ResultContext struct {
OpponentRank int
OpponentValueNumber int
PlayerRank int
PlayerValueNumber int
ByeType *ByeType
}
ResultContext provides additional information needed by scoring systems when calculating points for a specific game result.
ByeType, when non-nil, indicates that the "result" is actually a bye of the given type rather than a played game; the Result field is then ignored by scorers. Forfeit status is derived from Result.IsForfeit().
type Scorer ¶
type Scorer interface {
Score(ctx context.Context, state *TournamentState) ([]PlayerScore, error)
PointsForResult(result GameResult, rctx ResultContext) float64
}
Scorer calculates standings from game results.
type ScoringConfig ¶
type ScoringConfig struct {
System ScoringSystem
Tiebreakers []string
Options map[string]any
}
ScoringConfig holds tournament-wide scoring settings.
type ScoringSystem ¶
type ScoringSystem string
ScoringSystem identifies which scoring algorithm to use.
const ( ScoringStandard ScoringSystem = "standard" ScoringKeizer ScoringSystem = "keizer" ScoringFootball ScoringSystem = "football" )
func ParseScoringSystem ¶ added in v0.2.0
func ParseScoringSystem(s string) (ScoringSystem, error)
ParseScoringSystem parses a string into a ScoringSystem. Matching is case-insensitive and surrounding whitespace is trimmed. Accepted values are the canonical names "standard", "keizer", "football".
Returns an error wrapping the input on unknown values. Empty input is rejected so the empty string and typos surface at the parse boundary instead of propagating as a default.
func (ScoringSystem) IsValid ¶
func (s ScoringSystem) IsValid() bool
IsValid returns true if the scoring system is a recognized value.
type Standing ¶
type Standing struct {
Rank int `json:"rank"`
PlayerID string `json:"playerId"`
DisplayName string `json:"displayName"`
Score float64 `json:"score"`
TieBreakers []NamedValue `json:"tieBreakers"`
GamesPlayed int `json:"gamesPlayed"`
Wins int `json:"wins"`
Draws int `json:"draws"`
Losses int `json:"losses"`
}
Standing is the final ranked output combining score and tiebreakers.
type TieBreakValue ¶
TieBreakValue is a single tiebreak computation for one player.
type TieBreaker ¶
type TieBreaker interface {
ID() string
Name() string
Compute(ctx context.Context, state *TournamentState, scores []PlayerScore) ([]TieBreakValue, error)
}
TieBreaker computes a single tiebreak value for each player.
type TournamentInfo ¶
type TournamentInfo struct {
Name string
City string
Federation string // Organizing federation code
StartDate string // YYYY/MM/DD
EndDate string // YYYY/MM/DD
ChiefArbiter string
DeputyArbiter string
TimeControl string // Allotted time description
RoundDates []string // YYYY/MM/DD per round
}
TournamentInfo holds tournament metadata for display and TRF round-trip fidelity. Engines ignore this struct; it is populated from TRF header lines and written back when serializing to TRF.
type TournamentState ¶
type TournamentState struct {
Players []PlayerEntry
Rounds []RoundData
CurrentRound int
PreAssignedByes []ByeEntry
PairingConfig PairingConfig
ScoringConfig ScoringConfig
Info TournamentInfo // Tournament metadata. Zero value if not set.
}
TournamentState is the read-only snapshot of a tournament passed to engines. The caller constructs this from their data source before calling any engine method. Engines never perform I/O directly.
Rounds holds completed rounds only (round numbers 1..CurrentRound-1). CurrentRound is the 1-based round about to be paired. PreAssignedByes declares byes locked in for that upcoming round (e.g. a player notified the arbiter in advance that they will skip the round). Pairers exclude these players from the matching pool and echo their bye entries back in PairingResult.Byes. The roundrobin pairer rejects non-empty PreAssignedByes because the Berger schedule is fixed.
func (*TournamentState) ActivePlayerIDs ¶ added in v0.2.0
func (s *TournamentState) ActivePlayerIDs(round int) []string
ActivePlayerIDs returns the IDs of players active in the given round, in the order they appear in s.Players. See IsActiveInRound for the predicate, including the round <= 0 convenience.
func (*TournamentState) IsActiveInRound ¶ added in v0.2.0
func (s *TournamentState) IsActiveInRound(playerID string, round int) bool
IsActiveInRound reports whether the player with the given ID exists in the tournament and is participating in the given 1-indexed round. A player is active in round r when their JoinedRound is at most r (treating 0 as 1) and either WithdrawnAfterRound is nil or *WithdrawnAfterRound >= r. Unknown player IDs return false.
As a convenience for scoring callers that operate over the entire played history without a specific round anchor, round <= 0 means "no round filter": any enrolled player who has not been withdrawn (WithdrawnAfterRound == nil) is considered active. A withdrawn player is excluded regardless of when.
func (*TournamentState) Validate ¶
func (s *TournamentState) Validate() error
Validate checks structural invariants of the tournament state. Returns an error describing the first problem found, or nil if valid.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
algorithm
|
|
|
blossom
Package blossom implements Edmonds' maximum weight matching algorithm for general graphs.
|
Package blossom implements Edmonds' maximum weight matching algorithm for general graphs. |
|
varma
Package varma implements the Varma Tables pre-processing number assignment scheme for round-robin chess tournaments (FIDE C.05 Annex 2).
|
Package varma implements the Varma Tables pre-processing number assignment scheme for round-robin chess tournaments (FIDE C.05 Annex 2). |
|
cmd
|
|
|
chesspairing
command
cmd/chesspairing/check.go
|
cmd/chesspairing/check.go |
|
Package factory constructs chesspairing engines (Pairers, Scorers, TieBreakers) by name.
|
Package factory constructs chesspairing engines (Pairers, Scorers, TieBreakers) by name. |
|
pairing
|
|
|
burstein
Package burstein implements the Burstein Swiss pairing system (C.04.4.2).
|
Package burstein implements the Burstein Swiss pairing system (C.04.4.2). |
|
doubleswiss
Package doubleswiss implements the FIDE Double-Swiss pairing system (C.04.5).
|
Package doubleswiss implements the FIDE Double-Swiss pairing system (C.04.5). |
|
dubov
pairing/dubov/dubov.go
|
pairing/dubov/dubov.go |
|
dutch
Package dutch implements the FIDE Dutch Swiss pairing system (C.04.3).
|
Package dutch implements the FIDE Dutch Swiss pairing system (C.04.3). |
|
keizer
Package keizer implements Keizer-style pairing for chess tournaments.
|
Package keizer implements Keizer-style pairing for chess tournaments. |
|
lexswiss
Package lexswiss provides shared data structures and algorithms for lexicographic Swiss pairing systems.
|
Package lexswiss provides shared data structures and algorithms for lexicographic Swiss pairing systems. |
|
lim
Package lim implements the Lim Swiss pairing system (C.04.4.3).
|
Package lim implements the Lim Swiss pairing system (C.04.4.3). |
|
roundrobin
Package roundrobin implements round-robin pairing for chess tournaments.
|
Package roundrobin implements round-robin pairing for chess tournaments. |
|
swisslib
Package swisslib provides shared data structures and algorithms for the Swiss pairing engines.
|
Package swisslib provides shared data structures and algorithms for the Swiss pairing engines. |
|
team
Package team implements the FIDE Swiss Team Pairing System (C.04.6).
|
Package team implements the FIDE Swiss Team Pairing System (C.04.6). |
|
scoring
|
|
|
football
Package football implements football-style scoring (3-1-0) for chess tournaments.
|
Package football implements football-style scoring (3-1-0) for chess tournaments. |
|
keizer
Package keizer implements Keizer point scoring for chess tournaments.
|
Package keizer implements Keizer point scoring for chess tournaments. |
|
standard
Package standard implements standard chess scoring (1-½-0).
|
Package standard implements standard chess scoring (1-½-0). |
|
Package standings composes a Scorer and a list of TieBreakers over a TournamentState into a presentation-ready standings table.
|
Package standings composes a Scorer and a list of TieBreakers over a TournamentState into a presentation-ready standings table. |
|
Package tiebreaker implements chess tournament tiebreakers.
|
Package tiebreaker implements chess tournament tiebreakers. |
|
Package trf implements reading and writing of TRF16 (FIDE Tournament Report File) documents.
|
Package trf implements reading and writing of TRF16 (FIDE Tournament Report File) documents. |