{"id":51319239,"url":"https://github.com/echecsjs/swiss","last_synced_at":"2026-07-01T11:02:10.746Z","repository":{"id":345997907,"uuid":"1184212801","full_name":"echecsjs/swiss","owner":"echecsjs","description":"Swiss tournament pairing and standings algorithms following FIDE rules.","archived":false,"fork":false,"pushed_at":"2026-06-29T14:11:05.000Z","size":1113,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-29T16:04:22.086Z","etag":null,"topics":["chess","fide","pairing","swiss","typescript"],"latest_commit_sha":null,"homepage":"https://swiss.echecs.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/echecsjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-17T11:14:51.000Z","updated_at":"2026-06-29T14:05:05.000Z","dependencies_parsed_at":"2026-06-29T16:01:06.137Z","dependency_job_id":null,"html_url":"https://github.com/echecsjs/swiss","commit_stats":null,"previous_names":["mormubis/swiss","echecsjs/swiss"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/echecsjs/swiss","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Fswiss","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Fswiss/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Fswiss/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Fswiss/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/echecsjs","download_url":"https://codeload.github.com/echecsjs/swiss/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Fswiss/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35003464,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chess","fide","pairing","swiss","typescript"],"created_at":"2026-07-01T11:02:09.845Z","updated_at":"2026-07-01T11:02:10.731Z","avatar_url":"https://github.com/echecsjs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Swiss\n\n[![npm](https://img.shields.io/npm/v/@echecs/swiss)](https://www.npmjs.com/package/@echecs/swiss)\n[![Coverage](https://codecov.io/gh/echecsjs/swiss/branch/main/graph/badge.svg)](https://codecov.io/gh/echecsjs/swiss)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Spec](https://img.shields.io/badge/Spec-FIDE-green.svg)](SPEC.md)\n\n**Swiss** is a TypeScript library for Swiss chess tournament pairing, following\n[FIDE rules](https://handbook.fide.com/chapter/C0401202507).\n\nSix FIDE-approved pairing systems are supported via subpath exports: Dutch\n(C.04.3), Dubov (C.04.4.1), Burstein (C.04.4.2), Lim (C.04.4.3), Double-Swiss\n(C.04.5), and Swiss Team (C.04.6).\n\n## Installation\n\n```bash\nnpm install @echecs/swiss\n```\n\n## Quick Start\n\n```typescript\nimport { pair } from '@echecs/swiss'; // Dutch system (default)\nimport type { Game, Player } from '@echecs/swiss';\n\nconst players: Player[] = [\n  { id: 'alice', rating: 2100 },\n  { id: 'bob', rating: 1950 },\n  { id: 'carol', rating: 1870 },\n  { id: 'dave', rating: 1820 },\n];\n\n// Pair round 1 (no games played yet)\nconst round1 = pair(players, []);\nconsole.log(round1.pairings);\n// [{ white: 'alice', black: 'carol' }, { white: 'bob', black: 'dave' }]\n\n// Submit results — games[n] = round n+1, no `round` field on Game\nconst games: Game[][] = [\n  [\n    { white: 'alice', black: 'carol', result: 1 },\n    { white: 'bob', black: 'dave', result: 0.5 },\n  ],\n];\n\n// Pair round 2 — next round inferred from games.length + 1\nconst round2 = pair(players, games);\n```\n\n## API\n\n### `pair(players, games)`\n\nAll pairing systems export a single `pair` function:\n\n```typescript\npair(players: Player[], games: Game[][]): PairingResult;\n```\n\n- `players` — all registered players in the tournament\n- `games` — completed games grouped by round: `games[0]` = round 1, `games[1]` =\n  round 2, … The round to pair is `games.length + 1`\n\nThe `Game` type has no `round` field — round is encoded by array position.\n\nThrows `RangeError` for fewer than 2 players or unknown player ids in games.\n\n```typescript\ninterface PairingResult {\n  byes: Bye[]; // players with no opponent this round\n  pairings: Pairing[]; // white/black assignments\n}\n\ninterface Pairing {\n  black: string;\n  white: string;\n}\n\ninterface Bye {\n  player: string;\n}\n```\n\n### Subpath exports\n\nEach pairing system is available at its own subpath:\n\n```typescript\nimport { pair } from '@echecs/swiss'; // Dutch (default)\nimport { pair } from '@echecs/swiss/dutch'; // Dutch (explicit)\nimport { pair } from '@echecs/swiss/dubov'; // Dubov\nimport { pair } from '@echecs/swiss/burstein'; // Burstein\nimport { pair } from '@echecs/swiss/lim'; // Lim\nimport { pair } from '@echecs/swiss/double'; // Double-Swiss\nimport { pair } from '@echecs/swiss/team'; // Swiss Team\n```\n\n| Import path              | FIDE rule | Description                                                 |\n| ------------------------ | --------- | ----------------------------------------------------------- |\n| `@echecs/swiss`          | C.04.3    | Default import — Dutch system                               |\n| `@echecs/swiss/dutch`    | C.04.3    | Top half vs bottom half within each score group             |\n| `@echecs/swiss/dubov`    | C.04.4.1  | Adjacent pairing — rank 1 vs rank 2, rank 3 vs rank 4, etc. |\n| `@echecs/swiss/burstein` | C.04.4.2  | Rank 1 vs rank last, rank 2 vs rank second-to-last, etc.    |\n| `@echecs/swiss/lim`      | C.04.4.3  | Bi-directional pairing with strict colour rules             |\n| `@echecs/swiss/double`   | C.04.5    | Two-game match Swiss                                        |\n| `@echecs/swiss/team`     | C.04.6    | Team Swiss — teams as players, Type A colour preferences    |\n\n### Double-Swiss matches\n\nIn Double-Swiss, each pairing is a two-game match. Both games appear in the same\nround slot:\n\n```typescript\nimport { pair } from '@echecs/swiss/double';\n\nconst round1 = pair(players, []);\n// round1.pairings[0] = { white: 'alice', black: 'bob' }\n\n// Record both games of the match in games[0] (round 1)\nconst games: Game[][] = [\n  [\n    { white: 'alice', black: 'bob', result: 1 }, // game 1\n    { white: 'bob', black: 'alice', result: 0.5 }, // game 2\n  ],\n];\n// Alice scored 1 + 0.5 = 1.5 points for this match\n```\n\nA Double-Swiss bye awards 1.5 points (one win + one draw), recorded as two\nentries in the same round slot:\n\n```typescript\nconst games: Game[][] = [\n  [\n    { white: 'carol', black: '', result: 1 },\n    { white: 'carol', black: '', result: 0.5 },\n  ],\n];\n```\n\n### Byes\n\nA bye is represented as a `Game` with `black: ''` (empty string). The player in\n`white` receives the bye point:\n\n```typescript\nconst games: Game[][] = [\n  [\n    { white: 'alice', black: 'carol', result: 1 },\n    { white: 'bob', black: '', result: 1 }, // bye for bob\n  ],\n];\n```\n\n### Using with `@echecs/trf`\n\n```typescript\nimport parse from '@echecs/trf';\nimport { pair } from '@echecs/swiss';\nimport type { Tournament } from '@echecs/trf';\nimport type { Game, Player } from '@echecs/swiss';\n\nfunction toPlayers(t: Tournament): Player[] {\n  return t.players.map((p) =\u003e ({\n    id: String(p.pairingNumber),\n    rating: p.rating,\n  }));\n}\n\nfunction toGames(t: Tournament): Game[][] {\n  const byRound: Game[][] = [];\n  for (const player of t.players) {\n    for (const r of player.results) {\n      if (r.color !== 'w' || r.opponentId === null) continue;\n      let result: 0 | 0.5 | 1;\n      if (r.result === '1' || r.result === '+') result = 1;\n      else if (r.result === '0' || r.result === '-') result = 0;\n      else if (r.result === '=') result = 0.5;\n      else continue;\n      const idx = r.round - 1;\n      byRound[idx] ??= [];\n      byRound[idx].push({\n        black: String(r.opponentId),\n        result,\n        white: String(player.pairingNumber),\n      });\n    }\n  }\n  return byRound;\n}\n\nconst tournament = parse(trfString)!;\nconst pairings = pair(toPlayers(tournament), toGames(tournament));\n```\n\n## Types\n\n```typescript\ninterface Player {\n  id: string;\n  rating?: number; // used for seeding in round 1\n}\n\ninterface Game {\n  black: string; // '' for a bye\n  kind?: GameKind; // optional: classifies unplayed rounds\n  result: Result; // from white's perspective\n  white: string;\n  // No `round` field — round is encoded by position in Game[][]\n}\n\ntype GameKind =\n  | 'forfeit-loss'\n  | 'forfeit-win'\n  | 'full-bye'\n  | 'half-bye'\n  | 'pairing-bye'\n  | 'zero-bye';\n\ntype Result = 0 | 0.5 | 1;\n```\n\n## FIDE References\n\n- [C.04.1 Basic rules](https://handbook.fide.com/chapter/C0401202507)\n- [C.04.3 Dutch system](https://handbook.fide.com/chapter/C0403202602)\n- [C.04.4.1 Dubov system](https://handbook.fide.com/chapter/C040401202602)\n- [C.04.4.2 Burstein system](https://handbook.fide.com/chapter/C040402202602)\n- [C.04.4.3 Lim system](https://handbook.fide.com/chapter/C040403202602)\n- [C.04.5 Double-Swiss system](https://handbook.fide.com/chapter/DoubleSwissSystem202602)\n- [C.04.6 Swiss Team system](https://handbook.fide.com/chapter/SwissTeamPairingSystem202602)\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fechecsjs%2Fswiss","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fechecsjs%2Fswiss","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fechecsjs%2Fswiss/lists"}