@johnmorrisdotca/taisen-brackets 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,34 @@ All notable changes to this project are written here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.7.0] - 2026-10-20
10
+
11
+ Judo, step 1: results in the bracket boxes, and the bracket of a weight category with the repechage and two bronze medals. Nothing changes for a bracket that has no judo result (every existing snapshot is unchanged).
12
+
13
+ ### Added
14
+
15
+ - **`Matchup.judo`**: a judo contest's result, `{ sides: [white, blue], method?, hansokuMake?, goldenScore?, time? }`, each side `{ ippon, wazaAri, yuko, shido }`. The winner, the method and the score come from it under the IJF Sport and Organisation Rules, Refereeing Rules of 21 January 2026: ippon, two waza-ari are ippon (Article 16), yuko are counted and never add up to a waza-ari (Article 15; yuko is back since 2025, 5 to 9 seconds of osaekomi, Article 7), shido never decide a contest and the third is hansoku-make (Article 18), a direct hansoku-make (`hansokuMake`), golden score (Article 13), fusen-gachi and kiken-gachi (Article 19, the matchup names the `winner`). `Tie.result.score` is the scoreboard's 100, 10 and 1. [docs/judo.md](docs/judo.md) quotes each rule.
16
+ - **`judoOutcome(result, winner?)`** checks a result and works out the winner, the method, the scoreboard points and whether both sides were disqualified; a count out of range, two ippon, a method or winner the scores contradict, and a default with no winner are a `RangeError` that names the field. `judoSeconds`, `judoClock`.
17
+ - **The boxes**: the score in marks (`I`, `W`, `W2`, `Y`, `Y3`, `0`), a white or blue bar at the edge of each row, the shido as yellow and red cards beside the score, and under the rows how it was won, `GS` for golden score and the time. The tooltip, the description and `bracketTable` say the same in a sentence, with each side's shido. Works in the tree, `rows`, `shields`, `update`, `columns` and `series`, both looks, every size. CSS custom properties `--tb-judo-white`, `--tb-judo-blue`, `--tb-shido`, `--tb-hansoku`; `bracketStyle({ judo: true })`.
18
+ - **`judoBracket(athletes, { system, prefix })`**: a weight category for any number of athletes, with the IJF's quarter-final repechage (Sport and Organisation Rules 2.5.2: two repechage contests, two bronze medal contests) for eight athletes or more, or a direct knockout whose losing semi-finalists are the bronze medallists. `Matchup.stage` takes `"repechage"` and `"bronze"` (fed by the losers of the tree's ties, drawn together under the tree with their own heading); `Tie.stage`. `judoPodium(input)` gives gold, silver and the bronze medallists; `judoCategoryLabel({ sex, weight }, locale)` the heading ("Men -66 kg"); `JUDO_WEIGHT_CLASSES`.
19
+ - Words for judo in the five languages (the marks, the methods, golden score, the shido, the headings, the weight class). The French and Japanese were read by native-language reviewers; the Spanish and German need one (docs/languages.md).
20
+ - The demo has a Judo sport: a made-up men's -66 kg category of 16 athletes with every kind of ending, in the tree and in rows, with Hata's flags (Georgia, Uzbekistan, Cuba, Mongolia, Azerbaijan, Hungary, Italy added).
21
+
22
+ ### Changed
23
+
24
+ - The note under a box is cut to fit with the same width estimate the names use, so that a long note never reaches past its box.
25
+ - The ties kept apart under the tree are 66 high in the road look when one of them has a note.
26
+
27
+ ## [0.6.1] - 2026-10-19
28
+
29
+ No change to the package's code. The README's pictures were checked against 0.6.0's features and remade: they already show every layout, so none is added.
30
+
31
+ ### Changed
32
+
33
+ - The demo's road to the final shows three real tournaments (the World Cups of 2022 and 2026 with Hata's round flags, and the 2024 Stanley Cup playoffs with each team's initials on its colour) in place of made-up nations.
34
+ - `CONTRIBUTING.md` has a release-checklist line for visual features: new visual feature? update the README images and add a showcase section if the examples do not show it well.
35
+ - Docs pages are single-language (`CONTRIBUTING.md`), held by a new test, `docs-language.test`, which lists each exception with its reason; two changelog entries that had Japanese in English prose are now in English.
36
+
9
37
  ## [0.6.0] - 2026-10-14
10
38
 
11
39
  ### Added
@@ -111,7 +139,7 @@ All notable changes to this project are written here. The format follows
111
139
  - **Team names in a language.** `Team.names` (by locale code), the `teamName(team, locale)` option, and `countryName(isoCode, locale)`, which asks `Intl.DisplayNames` (the home nations too), so a World Cup shows every country in the chosen language with no list written. `Team.region` carries the ISO code.
112
140
  - A tie can be a `walkover`; a side that had a bye says so beside its score in the language.
113
141
  - **A real example for every sport**, read from spxn.com like the others: the 2024 NBA playoffs, the playoffs of the 2024 NFL season (byes, the Super Bowl at the centre), the 2024 CFL playoffs (byes, the Grey Cup), and the 1984 Stanley Cup playoffs (the Campbell and Wales conferences, a best-of-five first round), beside the NHL 2024, MLB 2022 and the World Cups. Each has its source in NOTICE.md and the demo's footer, and a test that pins its champion and final score.
114
- - **The demo** has a sport picker with its editions, a language switcher (EN, FR, 日本語, ES, DE) that drives every example, the poster included, remembered in `localStorage`, and the shared family header and footer (the family template of 2026-10-15b, `scripts/page.mjs`, the family block of the README).
142
+ - **The demo** has a sport picker with its editions, a language switcher (EN, FR, JA, ES, DE) that drives every example, the poster included, remembered in `localStorage`, and the shared family header and footer (the family template of 2026-10-15b, `scripts/page.mjs`, the family block of the README).
115
143
  - docs/languages.md: every term of every language, what it was checked against, and what still needs a native reader. The README has a "Languages" section and a gallery of the sports.
116
144
 
117
145
  ### Changed
@@ -121,7 +149,7 @@ All notable changes to this project are written here. The format follows
121
149
 
122
150
  ## [0.1.1] - 2026-10-14
123
151
 
124
- - The title is now "Taisen Brackets 対戦" in the README, the demo and the package description, matching the package name.
152
+ - The title now carries the package name's Japanese form beside "Taisen Brackets" in the README, the demo and the package description, matching the package name.
125
153
 
126
154
  ## [0.1.0] - 2026-10-14
127
155
 
package/NOTICE.md CHANGED
@@ -17,14 +17,16 @@ The code of this package is under the MIT licence (see [LICENSE](LICENSE)). The
17
17
  | The demo's MLB results (`demo/data/mlb-2022.js`) | Every game of the 2022 MLB postseason, read through spxn.com. The information used here was obtained free of charge from and is copyrighted by Retrosheet. Interested parties may contact Retrosheet at www.retrosheet.org | Retrosheet's notice, kept as it asks |
18
18
  | How the results were read | spxn.com's public API (a PostgREST service whose `api` schema is readable with a publishable key; the key is not kept in this repository), by `scripts/fetch-demo-data.mjs`, one request at a time, a second apart, retrieved 2026-10-14 | spxn.com is the same owner's site |
19
19
  | The demo's two-legged ties and its 32-team generic bracket | Made up for the demo: the clubs do not exist and the teams are "Team 1" to "Team 32" | Not real |
20
- | The demo's road to the final (`demo/road-data.js`) | Made up for the demo and the tests: sixteen nations that do not exist, each with a flag of a few plain shapes drawn in code (no real national flag, emblem or colour scheme is copied), and two made-up hockey clubs; the results are invented | Not real; MIT |
20
+ | The demo's road to the final (`demo/road-data.js`) | Real results, no new source: the finalists and the knockout matches on their way, read from the men's World Cup files above (2022 and 2026) and from the 2024 Stanley Cup playoffs file, with the nations' flags from Hata (below) | As the files they are read from: CC BY-SA 4.0 (2022), CC0 (2026), credited to the NHL (2024); MIT for the code |
21
21
  | The demo's national flags (in `demo/flags-data.js`), drawn through the `flags` option | [Hata](https://github.com/johnmorrisdotca/hata) 1.5.0 (MIT code), a development dependency used by `scripts/make-flags.mjs` and the tests, and an optional peer that the package never imports. Each flag is a Wikimedia Commons file that Hata ships only when Commons states it public domain, CC0 or CC BY; the file, author and licence of each are in `FLAG_CREDITS` in that file and in Hata's manifest and NOTICE | Each flag's own licence; none of the flags used asks for a credit. Commons marks flags as official insignia, whose use some countries restrict independently of copyright |
22
22
  | The demo's trophies (`demo/emblems.js`, and the `dougu-sports/` files the Pages build copies beside the page) | [Dougu Sports](https://github.com/johnmorrisdotca/dougu-sports) 0.1.0, a development dependency and an optional peer: generic emblems drawn for that package, which carry no league's or federation's name or mark | MIT, same owner; its NOTICE is in the package |
23
23
  | The words of the languages (`src/locales.ts`) | The football terms (the rounds of the World Cup, extra time, the shoot-out, a replay, a walkover) were read on 2026-10-14 in the French, Spanish, German and Japanese Wikipedia articles on the World Cups of 2022, 2026 and 1938; the articles are cited in [docs/languages.md](docs/languages.md). The other words, including those of the road to the final, are written for this package; the French and Japanese were read by native-language reviewers, the Spanish and German were not | Wikipedia's text is CC BY-SA 4.0; only terms (single words and short phrases) are taken, no sentence or table, and the articles are credited in docs/languages.md |
24
24
  | The names of countries in each language | Not in the package: `countryName` asks `Intl.DisplayNames`, which holds Unicode CLDR's names; the demo passes it each team's ISO code. The home nations and the two states that no longer exist are written in `src/locales.ts` and `demo/data.js` | CLDR is under the Unicode licence (https://www.unicode.org/license.txt) |
25
+ | The judo scoring (`src/judo-model.ts`, [docs/judo.md](docs/judo.md)) | The facts of the IJF Sport and Organisation Rules, Refereeing Rules, version 21 January 2026 (Articles 6, 7, 13 to 19), and of its competition systems (2.5, March 2023 version) and weight classes (2.2.1), as published by the International Judo Federation on ijf.org; and the IJF news items of 9 December 2016 and December 2024. Read on 2026-10-20. Only facts and a few short quotations, cited by article, are used; no text, drawing or mark of the IJF is copied | The rules are the IJF's; the package states what they say and names the article. No IJF mark is used |
26
+ | The demo's made-up judo tournament (`demo/data/judo-sample.js`) | Made up: the 16 athletes and their results are invented, and the countries are only there for the flags (the same Hata flags as above, with Azerbaijan, Cuba, Georgia, Hungary, Italy, Mongolia and Uzbekistan added) | Not real |
25
27
  | The demo's page | Written for this package, with the family stylesheet `demo/family.css` | MIT |
26
28
 
27
- The flags in the demo are for the nations of the World Cups of 2022 and 2026: `AR AT AU BA BE BR CA CD CH CI CO CV DE DZ EC EG ES FR GB-ENG GH HR JP KR MA MX NL NO PL PT PY SE SN US ZA`. The package itself ships no flag, no logo and no data of the leagues.
29
+ The flags in the demo are for the nations of the World Cups of 2022 and 2026 and of the made-up judo tournament: `AR AT AU AZ BA BE BR CA CD CH CI CO CU CV DE DZ EC EG ES FR GB-ENG GE GH HR HU IT JP KR MA MN MX NL NO PL PT PY SE SN US UZ ZA`. The package itself ships no flag, no logo and no data of the leagues.
28
30
 
29
31
  No code, text or picture was taken from the bracket libraries compared in [docs/compare.md](docs/compare.md); that page records what each says of itself.
30
32
 
package/README.md CHANGED
@@ -11,7 +11,7 @@ Give it the games, series, two-legged ties or replays of a tournament and it wor
11
11
  <img alt="TypeScript" src="https://img.shields.io/badge/types-TypeScript-3178c6">
12
12
  </p>
13
13
 
14
- <p align="center"><a href="https://johnmorrisdotca.github.io/taisen-brackets/"><strong>Open the demo →</strong></a> · <a href="./docs/layout.md">Layout</a> · <a href="./docs/layouts.md">Layouts</a> · <a href="./docs/drawing.md">Drawing</a> · <a href="./docs/road.md">Road to the final</a> · <a href="./docs/backgrounds.md">Backgrounds</a> · <a href="./docs/formats.md">Formats</a> · <a href="./docs/compare.md">How it compares</a></p>
14
+ <p align="center"><a href="https://johnmorrisdotca.github.io/taisen-brackets/"><strong>Open the demo →</strong></a> · <a href="./docs/layout.md">Layout</a> · <a href="./docs/layouts.md">Layouts</a> · <a href="./docs/drawing.md">Drawing</a> · <a href="./docs/road.md">Road to the final</a> · <a href="./docs/backgrounds.md">Backgrounds</a> · <a href="./docs/judo.md">Judo</a> · <a href="./docs/formats.md">Formats</a> · <a href="./docs/compare.md">How it compares</a></p>
15
15
 
16
16
  <p align="center">
17
17
  <picture>
@@ -238,6 +238,15 @@ console.log(ice.includes("tb-bgd"), mine.includes("#ff7ac8"), photo.includes('<i
238
238
 
239
239
  In the road look, `seeds: true` writes a small tag (`P1`, `WC2`) beside each team's mark, `codes: true` its three letters under the mark in a box of marks, `vs: true` a "v" between the two marks, and a team with no flag or logo is a tile with its letters. [docs/backgrounds.md](./docs/backgrounds.md) has the options, the contrast rules and the module the other packages of the family share.
240
240
 
241
+ ## Judo
242
+
243
+ A judo contest is a tie with a `judo` result: ippon, waza-ari, yuko and shido as the IJF's current Sport and Organisation Rules score them (yuko is back since 2025), golden score, the contest time and how it was won. The box writes the marks (`I`, `W`, `Y`), the shido as yellow and red cards and a note under the two rows, in the five languages. `judoBracket` makes a weight category's bracket with the IJF's repechage and its two bronze medal contests. [docs/judo.md](./docs/judo.md) quotes the rules it follows.
244
+
245
+ <p align="center"><picture>
246
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/judo-desk-dark.webp">
247
+ <img alt="A made-up men's minus 66 kilogram judo tournament of 16 athletes with round flags, score marks, shido cards, how each contest was won and its time, and the repechage and bronze medal contests under the tree." src="https://raw.githubusercontent.com/johnmorrisdotca/taisen-brackets/main/docs/images/judo-desk-light.webp" width="720">
248
+ </picture><br><em>Men's -66 kg, with the quarter-final repechage (invented names).</em></p>
249
+
241
250
  ## In 30 seconds
242
251
 
243
252
  ```sh
@@ -283,6 +292,7 @@ Put the string in a page (`element.innerHTML = svg`), write it to a file, or sen
283
292
  - **Team marks you supply**: a logo address or inline SVG, drawn round or square; a team with none is its initials on a colour. Round flags from [Hata](https://github.com/johnmorrisdotca/hata) go in as they are.
284
293
  - **The champion in the centre**, with a slot for your trophy: your own SVG or image, or an emblem from [Dougu Sports](https://github.com/johnmorrisdotca/dougu-sports) (a hockey cup, a basketball trophy, a globe) through an optional plug-in, with no dependency added.
285
294
  - **Accessible**: `role="img"` with a title and a description, a tooltip sentence on every tie, and `bracketTable`.
295
+ - **Judo scoring**: `Matchup.judo` takes ippon, waza-ari, yuko, shido, hansoku-make, golden score and the contest time, checked against the IJF rules (`judoOutcome`); a box shows the marks, the shido cards and how the contest was won, and `judoBracket` adds the repechage and the two bronze medal contests ([docs/judo.md](./docs/judo.md)).
286
296
  - **Formats by era**: NHL, NBA, MLB, NFL, CFL, MLS and FIFA World Cup round names, series lengths and byes.
287
297
  - **Five languages**: English, French, Japanese, Spanish and German, for every word the drawing, the tooltips, the description and the table write, with the football terms of each language; countries named by `Intl` from their ISO code; numbers and dates by `Intl`. Add a language of your own.
288
298
  - **Deterministic**: no clock, no random number, no counter; the same input gives the same string, and ids come from a hash of the input.
@@ -642,6 +652,7 @@ Everything is exported from `@johnmorrisdotca/taisen-brackets`; the layout part
642
652
  - `singleElimination(teams, options?)`, `advanceWinners(matchups)`, `standardSeeds(size)`: brackets for any number of teams.
643
653
  - `matchupsFromGames(games, options)`: matchups from a flat list of games.
644
654
  - `resolveMatchup`, `collectTeams`, `describeTie`, `noteParts`, `noteLong`: one tie's result, the teams of a bracket, a tie in a sentence.
655
+ - `judoOutcome(result, winner?)`, `judoBracket(athletes, options?)`, `judoPodium(input)`, `judoCategoryLabel(category, locale?)`, `JUDO_WEIGHT_CLASSES`, `judoSeconds(time)`, `judoClock(seconds)`: judo results checked against the IJF rules, the bracket with its repechage and two bronze medal contests, the medallists, a weight category as a heading ([docs/judo.md](./docs/judo.md)). `Matchup.judo` and `Matchup.stage` (`"repechage"`, `"bronze"`) carry them into `drawBracket`.
645
656
  - `playoffFormat(league, year)`, `playoffEras(league)`, `LEAGUES`: the formats by era (`nhl`, `nba`, `mlb`, `nfl`, `cfl`, `mls`, `worldcup`). `LEAGUE_EMBLEMS`: each league's default emblem id.
646
657
  - `isEmblemId(value)`, and the types `EmblemRenderer`, `EmblemRequest`, `EmblemStyle`: the trophy emblems (see "Emblems for the trophy").
647
658
  - `roundName(remaining)`, `roundNamesFor(count)`, `roundLabel(layout, round, names, words)`: round names.
@@ -700,6 +711,7 @@ console.log(svg.includes('lang="nl"')); // true
700
711
  | CFL | 1996 on | Division Semi-Final, Division Final, Grey Cup |
701
712
  | MLS | 2023 on | Wild Card Round, Round One (best of three), Conference Semifinals, Conference Finals, MLS Cup |
702
713
  | FIFA World Cup | every edition with a knockout, 1930 to 2026 (1950 had none) | Round of 32 (2026), Round of 16, Quarter-finals, Semi-finals, Final |
714
+ | Judo (IJF rules of 21 January 2026) | any weight category: `judoBracket` with the quarter-final repechage or a direct knockout | Round of N … Final, repechage, two bronze medal contests |
703
715
  | Any other | single elimination for any number of teams | Round of N … Final |
704
716
 
705
717
  Each era was checked against English Wikipedia (facts only; the articles are cited in [docs/formats.md](./docs/formats.md)). The year is the year the playoffs are played in, except for the NFL, whose year is its season's, so 2024 is the season whose playoffs were played in January and February 2025. A season did something of its own (a strike, a pandemic) has its own entry, and a later one is not covered until it is checked.
@@ -732,6 +744,7 @@ Any current browser, and Node 22 or later. The package makes no network request
732
744
 
733
745
  ## Roadmap
734
746
 
747
+ - Judo: a contest scoreboard drawing, a contest card and a mat queue board, the road to the final of a judoka, double and full repechage ([docs/judo.md](./docs/judo.md)).
735
748
  - Double elimination, and the group tables on one page with the knockout they lead to.
736
749
  - More languages (Portuguese, Italian, Korean, Chinese), and native review of the Spanish and German words.
737
750
  - A custom element and a React helper, as in the other packages of the family.
@@ -753,39 +766,67 @@ The layout began as the playoff bracket of [spxn.com](https://spxn.com), the his
753
766
  ### The family
754
767
 
755
768
  <!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
756
- Taisen Brackets is one of twenty-eight packages, each made for the same site, each at
757
- [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
769
+ Taisen Brackets is one of thirty-two packages, each made for the same site, each at
770
+ [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca) or [github.com/spxis](https://github.com/spxis). The code of every one is MIT.
758
771
 
759
- - [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
760
- - [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ): a turning cube for the browser, 2×2 to 7×7, with record solves to replay. [Demo](https://johnmorrisdotca.github.io/kyuubu/).
761
- - [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ): a colour-card shedding game for two to eight, with the house rules people play. [Demo](https://johnmorrisdotca.github.io/hitotsu/).
762
- - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
763
- - [Tane](https://github.com/johnmorrisdotca/tane) (種): seeded random numbers and daily seeds, the same in every browser and on every server. [Demo](https://johnmorrisdotca.github.io/tane/).
772
+ **Games — board**
773
+
774
+ - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
775
+ - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
764
776
  - [Narabe](https://github.com/johnmorrisdotca/narabe) (並べ): one rules engine for abstract board games, from gomoku and Reversi to Go and checkers. [Demo](https://johnmorrisdotca.github.io/narabe/).
777
+ - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
765
778
  - [Tenka](https://github.com/johnmorrisdotca/tenka) (天下): world conquest for two to six, on a map of the real world. [Demo](https://johnmorrisdotca.github.io/tenka/).
766
- - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
767
- - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
779
+
780
+ **Games — cards & tiles**
781
+
782
+ - [Hitotsu](https://github.com/johnmorrisdotca/hitotsu) (一つ): a colour-card shedding game for two to eight, with the house rules people play. [Demo](https://johnmorrisdotca.github.io/hitotsu/).
768
783
  - [Jarajara](https://github.com/johnmorrisdotca/jarajara) (ジャラジャラ): mahjong tiles drawn as SVG, stacked layouts, and the matching solitaire Awase. [Demo](https://johnmorrisdotca.github.io/jarajara/).
769
- - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
770
- - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
771
- - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
772
- - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
784
+ - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
785
+ - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
786
+
787
+ **Games — puzzles**
788
+
789
+ - [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
790
+ - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
791
+ - [Karakuri](https://github.com/johnmorrisdotca/karakuri) (からくり): eight hyper-casual puzzle games, some of them physics: draw a shield, pull pins, cut ropes, slide blocks, pour tubes. [Demo](https://johnmorrisdotca.github.io/karakuri/).
773
792
  - [Kazu](https://github.com/johnmorrisdotca/kazu) (数): grid number puzzles: Sudoku and its variants, Futoshiki and Skyscrapers. [Demo](https://johnmorrisdotca.github.io/kazu/).
793
+ - [Kyuubu](https://github.com/johnmorrisdotca/kyuubu) (キューブ): a turning cube for the browser, 2×2 to 7×7, with record solves to replay. [Demo](https://johnmorrisdotca.github.io/kyuubu/).
774
794
  - [Meikyuu](https://github.com/johnmorrisdotca/meikyuu) (迷宮): mazes on squares, hexagons, triangles and circles, made from a seed and drawn through with a finger or the mouse. [Demo](https://johnmorrisdotca.github.io/meikyuu/).
795
+ - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
796
+ - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
797
+ - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
798
+
799
+ **Games — dice & random**
800
+
801
+ - [Korokoro](https://github.com/johnmorrisdotca/korokoro) (コロコロ): dice, with notation, exact odds, real sounds and the dice of many games. [Demo](https://johnmorrisdotca.github.io/korokoro/).
802
+ - [Tane](https://github.com/johnmorrisdotca/tane) (種): seeded random numbers and daily seeds, the same in every browser and on every server. [Demo](https://johnmorrisdotca.github.io/tane/).
803
+
804
+ **Language**
805
+
806
+ - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
775
807
  - [Hikidashi](https://github.com/johnmorrisdotca/hikidashi) (引き出し): a drawer of small Japanese text tools: era dates, kanji numerals, readings and sentence difficulty. [Demo](https://johnmorrisdotca.github.io/hikidashi/).
808
+ - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
809
+
810
+ **Geography & data**
811
+
812
+ - [address-plus](https://github.com/johnmorrisdotca/address-plus) (アドレスプラス): parse and format addresses in the United States, Canada, Japan, Australia, France, Germany and the United Kingdom. [Demo](https://johnmorrisdotca.github.io/address-plus/).
776
813
  - [Chizu](https://github.com/johnmorrisdotca/chizu) (地図): maps of the world and of countries' regions, in English and Japanese, with a quiz and callouts. [Demo](https://johnmorrisdotca.github.io/chizu/).
777
- - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
778
- - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
779
- - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
780
- - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
781
- - [Karakuri](https://github.com/johnmorrisdotca/karakuri) (からくり): eight hyper-casual puzzle games, some of them physics: draw a shield, pull pins, cut ropes, slide blocks, pour tubes. [Demo](https://johnmorrisdotca.github.io/karakuri/).
782
- - [Houseki](https://github.com/johnmorrisdotca/houseki) (宝石): gem and stone matching puzzles: falling triplets, stone collapse, colour chains and gem swap. [Demo](https://johnmorrisdotca.github.io/houseki/).
783
- - [Kyogijo Stadiums](https://github.com/johnmorrisdotca/kyogijo-stadiums) (競技場): sports fields, rinks, courts and ballparks drawn to scale as SVG, with each team and the score on them. [Demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/).
784
- - [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets) (対戦): a sport-agnostic tournament bracket, drawn as SVG, from a list of ties. [Demo](https://johnmorrisdotca.github.io/taisen-brackets/).
814
+ - [Hata](https://github.com/johnmorrisdotca/hata) (旗): country and regional flags as SVG, at their true proportions and keyed by ISO code. [Demo](https://johnmorrisdotca.github.io/hata/).
815
+ - [Kuni](https://github.com/johnmorrisdotca/kuni) (国): countries and their regions, with ISO codes and names in English and Japanese. [Demo](https://johnmorrisdotca.github.io/kuni/).
816
+
817
+ **Sports**
818
+
785
819
  - [Dougu Sports](https://github.com/johnmorrisdotca/dougu-sports) (道具): sports emblems as SVG: trophies, medals, crests, and the ball and gear of every sport, with optional 3D turning. [Demo](https://johnmorrisdotca.github.io/dougu-sports/).
820
+ - [Kyogijo Stadiums](https://github.com/johnmorrisdotca/kyogijo-stadiums) (競技場): sports fields, rinks, courts and ballparks drawn to scale as SVG, with each team and the score on them. [Demo](https://johnmorrisdotca.github.io/kyogijo-stadiums/).
786
821
  - [Senshu Jerseys](https://github.com/johnmorrisdotca/senshu-jerseys) (選手): sports jerseys, sweaters, kits, caps, helmets and retired-number banners as SVG, front and back, with optional 2.5D turning. [Demo](https://johnmorrisdotca.github.io/senshu-jerseys/).
822
+ - [Taisen Brackets](https://github.com/johnmorrisdotca/taisen-brackets) (対戦): a sport-agnostic tournament bracket, drawn as SVG, from a list of ties. [Demo](https://johnmorrisdotca.github.io/taisen-brackets/).
823
+
824
+ **Developer tools**
825
+
826
+ - [REST in Pieces](https://github.com/spxis/rest-in-pieces) (レスト・イン・ピーシーズ): seeded test data and REST APIs for frontend development, with latency and error drills. [Demo](https://spxis.github.io/rest-in-pieces/).
827
+
787
828
 
788
- **This package is Taisen Brackets.** The demos of all twenty-eight share one header and footer, so each links the rest.
829
+ **This package is Taisen Brackets.** The demos of all thirty-two share one header and footer, so each links the rest.
789
830
  <!-- family:end -->
790
831
 
791
832
  ## Development
package/dist/index.d.ts CHANGED
@@ -17,3 +17,5 @@ export type { ResolvedBackground } from "./background.ts";
17
17
  export { VERSION } from "./version.ts";
18
18
  export { playoffFormat, playoffEras, LEAGUES, LEAGUE_EMBLEMS } from "./presets.ts";
19
19
  export type { League, PlayoffFormat, PlayoffRound } from "./presets.ts";
20
+ export { judoBracket, judoPodium, judoCategoryLabel, judoOutcome, judoSeconds, judoClock, JUDO_WEIGHT_CLASSES } from "./judo.ts";
21
+ export type { JudoBracketOptions, JudoCategory, JudoMethod, JudoOutcome, JudoPodium, JudoResult, JudoSide, JudoSystem } from "./judo.ts";
package/dist/index.js CHANGED
@@ -10,3 +10,4 @@ export { BACKGROUND_PRESETS, FAMILY_BACKGROUNDS_VERSION, backgroundSvg, contrast
10
10
  export { resolveBackground } from "./background.js";
11
11
  export { VERSION } from "./version.js";
12
12
  export { playoffFormat, playoffEras, LEAGUES, LEAGUE_EMBLEMS } from "./presets.js";
13
+ export { judoBracket, judoPodium, judoCategoryLabel, judoOutcome, judoSeconds, judoClock, JUDO_WEIGHT_CLASSES } from "./judo.js";
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Judo results: the scores of one contest and what they come to under the IJF Sport and Organisation Rules (Refereeing Rules, version
3
+ * 21 January 2026, for the Olympic cycle to Los Angeles 2028). Pure and sport-specific; nothing here draws. The rules this follows, by
4
+ * article, are in docs/judo.md.
5
+ *
6
+ * - Article 7 and 15: osaekomi of 20 seconds is ippon, 10 to 19 seconds waza-ari, 5 to 9 seconds yuko.
7
+ * - Article 15: "Yuko scores are counted (1, 2, 3, etc.) but they do not add up to waza-ari."
8
+ * - Article 16: a second waza-ari is waza-ari-awasete-ippon, ippon.
9
+ * - Article 18: "Two shido can be awarded and the third will be hansoku-make"; "A contest cannot be decided by single or accumulated shido, until hansoku-make is reached."
10
+ * - Article 13: a level contest at the end of regular time (4 minutes) goes to golden score, which has no time limit and ends at the first score or hansoku-make.
11
+ */
12
+ /** How a contest was won. `"score"` is a decision on the technical scores (the waza-ari, then the yuko, counted). */
13
+ export type JudoMethod = "ippon" | "waza-ari-awasete-ippon" | "score" | "hansoku-make" | "fusen-gachi" | "kiken-gachi";
14
+ /** What one side scored in a contest. Anything left out is none. */
15
+ export interface JudoSide {
16
+ /** Ippon: the contest ends. */
17
+ ippon?: boolean;
18
+ /** Waza-ari, 0 to 2; a second one is waza-ari-awasete-ippon. */
19
+ wazaAri?: number;
20
+ /** Yuko: counted, never added up to a waza-ari. */
21
+ yuko?: number;
22
+ /** Shido (the penalties), 0 to 3; the third is hansoku-make (indirect). */
23
+ shido?: number;
24
+ }
25
+ /** The result of one judo contest, the sides in the order of the matchup's `sides`: the first is white, the second blue. */
26
+ export interface JudoResult {
27
+ sides: readonly [JudoSide, JudoSide];
28
+ /** How it was won. Left out, it is worked out from the scores; fusen-gachi and kiken-gachi (no score can say so) need the matchup's `winner`. */
29
+ method?: JudoMethod;
30
+ /** The side (0 or 1) that was given a direct hansoku-make (a grave infringement): the other side wins by ippon. */
31
+ hansokuMake?: 0 | 1;
32
+ /** The contest went on to golden score, and the scores are the ones at its end. */
33
+ goldenScore?: boolean;
34
+ /** The contest time: seconds (`222`) or `"3:42"`. Regular time is 4 minutes; golden score runs past it. */
35
+ time?: number | string;
36
+ }
37
+ /** A result checked and worked out. */
38
+ export interface JudoOutcome {
39
+ /** 0 or 1, the side that won; `null` while nothing has decided it (level scores: golden score is next). */
40
+ winner: 0 | 1 | null;
41
+ /** How it was won, or `null` while undecided. */
42
+ method: JudoMethod | null;
43
+ goldenScore: boolean;
44
+ /** The contest time in seconds, or `null`. */
45
+ seconds: number | null;
46
+ /** The two sides with every count filled in. */
47
+ sides: readonly [Required<JudoSide>, Required<JudoSide>];
48
+ /** The value on the scoreboard (Articles 4 and 15: ippon 100, waza-ari 10, yuko 1; two waza-ari are ippon). */
49
+ points: readonly [number, number];
50
+ /** Both sides were given the third shido: the contest is recorded 0-0 and nobody wins (Article 18.3.1). */
51
+ doubleHansokuMake: boolean;
52
+ }
53
+ /** The IJF's senior weight classes in kilograms, the lightest first: a number is "up to and including", a text starting `+` is "over". */
54
+ export declare const JUDO_WEIGHT_CLASSES: Readonly<Record<"men" | "women", readonly string[]>>;
55
+ /** A time as seconds: a number, or `"m:ss"`. */
56
+ export declare function judoSeconds(time: number | string | undefined): number | null;
57
+ /** Seconds as `m:ss`. */
58
+ export declare const judoClock: (seconds: number) => string;
59
+ /**
60
+ * Checks a result against the rules and works out who won and how. `winner` is the winner the matchup names (needed for fusen-gachi and
61
+ * kiken-gachi, which no score can show). Throws a `RangeError` for a count out of range, two ippon, a method the scores contradict, or a
62
+ * default with no winner.
63
+ */
64
+ export declare function judoOutcome(result: JudoResult, winner?: 0 | 1 | null): JudoOutcome;
65
+ /** Whether a result has any score or penalty at all (a contest still to be fought has none). */
66
+ export declare const judoHasScore: (o: JudoOutcome) => boolean;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Judo results: the scores of one contest and what they come to under the IJF Sport and Organisation Rules (Refereeing Rules, version
3
+ * 21 January 2026, for the Olympic cycle to Los Angeles 2028). Pure and sport-specific; nothing here draws. The rules this follows, by
4
+ * article, are in docs/judo.md.
5
+ *
6
+ * - Article 7 and 15: osaekomi of 20 seconds is ippon, 10 to 19 seconds waza-ari, 5 to 9 seconds yuko.
7
+ * - Article 15: "Yuko scores are counted (1, 2, 3, etc.) but they do not add up to waza-ari."
8
+ * - Article 16: a second waza-ari is waza-ari-awasete-ippon, ippon.
9
+ * - Article 18: "Two shido can be awarded and the third will be hansoku-make"; "A contest cannot be decided by single or accumulated shido, until hansoku-make is reached."
10
+ * - Article 13: a level contest at the end of regular time (4 minutes) goes to golden score, which has no time limit and ends at the first score or hansoku-make.
11
+ */
12
+ /** The IJF's senior weight classes in kilograms, the lightest first: a number is "up to and including", a text starting `+` is "over". */
13
+ export const JUDO_WEIGHT_CLASSES = Object.freeze({
14
+ men: Object.freeze(["-60", "-66", "-73", "-81", "-90", "-100", "+100"]),
15
+ women: Object.freeze(["-48", "-52", "-57", "-63", "-70", "-78", "+78"]),
16
+ });
17
+ const whole = (what, n, max) => {
18
+ const v = n === undefined ? 0 : n;
19
+ if (typeof v !== "number" || !Number.isInteger(v) || v < 0 || v > max)
20
+ throw new RangeError(`taisen-brackets: ${what} is a whole number from 0 to ${max}, not ${JSON.stringify(v)}`);
21
+ return v;
22
+ };
23
+ function filled(side, k) {
24
+ const who = k === 0 ? "the white side's" : "the blue side's";
25
+ return {
26
+ ippon: side.ippon === true,
27
+ wazaAri: whole(`${who} waza-ari`, side.wazaAri, 2),
28
+ yuko: whole(`${who} yuko`, side.yuko, 99),
29
+ shido: whole(`${who} shido`, side.shido, 3),
30
+ };
31
+ }
32
+ /** A time as seconds: a number, or `"m:ss"`. */
33
+ export function judoSeconds(time) {
34
+ if (time === undefined)
35
+ return null;
36
+ if (typeof time === "number") {
37
+ if (!Number.isFinite(time) || time < 0)
38
+ throw new RangeError(`taisen-brackets: a judo contest time is seconds from 0, not ${time}`);
39
+ return Math.round(time);
40
+ }
41
+ const m = /^(\d{1,3}):([0-5]\d)$/.exec(time.trim());
42
+ if (!m)
43
+ throw new RangeError(`taisen-brackets: a judo contest time is seconds or "m:ss" ("3:42"), not ${JSON.stringify(time)}`);
44
+ return Number(m[1]) * 60 + Number(m[2]);
45
+ }
46
+ /** Seconds as `m:ss`. */
47
+ export const judoClock = (seconds) => `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, "0")}`;
48
+ const scoreboard = (s) => (s.ippon || s.wazaAri >= 2 ? 100 : s.wazaAri * 10 + s.yuko);
49
+ /**
50
+ * Checks a result against the rules and works out who won and how. `winner` is the winner the matchup names (needed for fusen-gachi and
51
+ * kiken-gachi, which no score can show). Throws a `RangeError` for a count out of range, two ippon, a method the scores contradict, or a
52
+ * default with no winner.
53
+ */
54
+ export function judoOutcome(result, winner = null) {
55
+ const sides = [filled(result.sides[0], 0), filled(result.sides[1], 1)];
56
+ const seconds = judoSeconds(result.time);
57
+ const points = [scoreboard(sides[0]), scoreboard(sides[1])];
58
+ const golden = result.goldenScore === true;
59
+ const out = (w, method, double = false) => ({ winner: w, method, goldenScore: golden, seconds, sides, points, doubleHansokuMake: double });
60
+ const other = (k) => (k === 0 ? 1 : 0);
61
+ if (sides[0].ippon && sides[1].ippon)
62
+ throw new RangeError("taisen-brackets: both sides of a judo contest cannot score ippon");
63
+ if (sides[0].wazaAri === 2 && sides[1].wazaAri === 2)
64
+ throw new RangeError("taisen-brackets: both sides of a judo contest cannot score waza-ari-awasete-ippon");
65
+ const given = result.method;
66
+ const agree = (derived) => {
67
+ if (given !== undefined && derived.method !== given)
68
+ throw new RangeError(`taisen-brackets: the judo method ${JSON.stringify(given)} does not fit the scores, which make ${JSON.stringify(derived.method)}`);
69
+ return derived;
70
+ };
71
+ if (given === "fusen-gachi" || given === "kiken-gachi") {
72
+ if (winner === null)
73
+ throw new RangeError(`taisen-brackets: a judo contest won by ${given} needs the matchup's winner`);
74
+ return out(winner, given);
75
+ }
76
+ if (result.hansokuMake !== undefined) {
77
+ if (result.hansokuMake !== 0 && result.hansokuMake !== 1)
78
+ throw new RangeError("taisen-brackets: hansokuMake is 0 or 1, the side that was penalised");
79
+ return agree(out(other(result.hansokuMake), "hansoku-make"));
80
+ }
81
+ if (sides[0].shido >= 3 && sides[1].shido >= 3)
82
+ return agree(out(null, "hansoku-make", true));
83
+ for (const k of [0, 1])
84
+ if (sides[k].shido >= 3)
85
+ return agree(out(other(k), "hansoku-make"));
86
+ for (const k of [0, 1]) {
87
+ if (sides[k].ippon)
88
+ return agree(out(k, "ippon"));
89
+ if (sides[k].wazaAri >= 2)
90
+ return agree(out(k, "waza-ari-awasete-ippon"));
91
+ }
92
+ const by = (a, b) => (a > b ? 0 : b > a ? 1 : null);
93
+ const decided = by(sides[0].wazaAri, sides[1].wazaAri) ?? by(sides[0].yuko, sides[1].yuko);
94
+ if (decided !== null)
95
+ return agree(out(decided, "score"));
96
+ if (given !== undefined)
97
+ throw new RangeError(`taisen-brackets: the judo method ${JSON.stringify(given)} does not fit scores that are level`);
98
+ // Level: nothing has decided it, though the matchup may name a winner (a contest recorded with no scores).
99
+ return out(null, null);
100
+ }
101
+ /** Whether a result has any score or penalty at all (a contest still to be fought has none). */
102
+ export const judoHasScore = (o) => o.sides.some((s) => s.ippon || s.wazaAri > 0 || s.yuko > 0 || s.shido > 0);
@@ -0,0 +1,19 @@
1
+ import type { JudoMethod, JudoSide } from "./judo-model.ts";
2
+ import type { Tie } from "./types.ts";
3
+ import { type Voice } from "./locales.ts";
4
+ type Filled = Required<JudoSide>;
5
+ /** A side's score as the box writes it: `I` for ippon, `W` a waza-ari (`W2` two), `Y` a yuko (`Y3` three), "0" for none. The marks are the language's (`judoIppon`, `judoWazaAri`, `judoYuko`). */
6
+ export declare function judoMark(side: Filled, v: Voice): string;
7
+ /** A side's score in words, for a sentence: "ippon", "waza-ari yuko", "2 waza-ari 3 yuko", "0". */
8
+ export declare function judoSideLong(side: Filled, v: Voice): string;
9
+ /** How a contest was won, in the language. */
10
+ export declare function judoMethodWord(method: JudoMethod, v: Voice): string;
11
+ /** The pieces of a judo contest's note, short or in a sentence: how it was won, golden score, the contest time, and (long only) each side's shido. */
12
+ export declare function judoNotes(tie: Tie, v: Voice, long: boolean): string[];
13
+ /** What a row of a box writes for a side's score: a judo contest's marks, else the number. `""` when nothing was played. */
14
+ export declare function sideScore(tie: Tie, k: 0 | 1, v: Voice): string;
15
+ /** A side's score in words for a sentence (a judo contest's, else the number). */
16
+ export declare function sideScoreLong(tie: Tie, k: 0 | 1, v: Voice): string;
17
+ /** The cards beside a side's score: a yellow one for each shido, and a red one for the third (hansoku-make) or for a direct hansoku-make: `["y", "y", "r"]`. */
18
+ export declare function shidoCards(tie: Tie, k: 0 | 1): ("y" | "r")[];
19
+ export {};
@@ -0,0 +1,84 @@
1
+ import { judoClock } from "./judo-model.js";
2
+ import { fill } from "./locales.js";
3
+ /** A side's score as the box writes it: `I` for ippon, `W` a waza-ari (`W2` two), `Y` a yuko (`Y3` three), "0" for none. The marks are the language's (`judoIppon`, `judoWazaAri`, `judoYuko`). */
4
+ export function judoMark(side, v) {
5
+ const w = v.words;
6
+ if (side.ippon)
7
+ return w.judoIppon;
8
+ const parts = [];
9
+ if (side.wazaAri > 0)
10
+ parts.push(w.judoWazaAri + (side.wazaAri > 1 ? v.number(side.wazaAri) : ""));
11
+ if (side.yuko > 0)
12
+ parts.push(w.judoYuko + (side.yuko > 1 ? v.number(side.yuko) : ""));
13
+ return parts.length ? parts.join(" ") : v.number(0);
14
+ }
15
+ /** A side's score in words, for a sentence: "ippon", "waza-ari yuko", "2 waza-ari 3 yuko", "0". */
16
+ export function judoSideLong(side, v) {
17
+ const w = v.words;
18
+ if (side.ippon)
19
+ return w.judoIpponLong;
20
+ const parts = [];
21
+ if (side.wazaAri > 0)
22
+ parts.push(side.wazaAri > 1 ? `${v.number(side.wazaAri)} ${w.judoWazaAriLong}` : w.judoWazaAriLong);
23
+ if (side.yuko > 0)
24
+ parts.push(side.yuko > 1 ? `${v.number(side.yuko)} ${w.judoYukoLong}` : w.judoYukoLong);
25
+ return parts.length ? parts.join(" ") : v.number(0);
26
+ }
27
+ /** How a contest was won, in the language. */
28
+ export function judoMethodWord(method, v) {
29
+ const w = v.words;
30
+ switch (method) {
31
+ case "ippon": return w.judoMethodIppon;
32
+ case "waza-ari-awasete-ippon": return w.judoMethodAwasete;
33
+ case "score": return w.judoMethodScore;
34
+ case "hansoku-make": return w.judoMethodHansoku;
35
+ case "fusen-gachi": return w.judoMethodFusen;
36
+ case "kiken-gachi": return w.judoMethodKiken;
37
+ }
38
+ }
39
+ /** The pieces of a judo contest's note, short or in a sentence: how it was won, golden score, the contest time, and (long only) each side's shido. */
40
+ export function judoNotes(tie, v, long) {
41
+ const j = tie.judo;
42
+ if (!j)
43
+ return [];
44
+ const w = v.words;
45
+ const out = [];
46
+ if (j.method !== null)
47
+ out.push(judoMethodWord(j.method, v));
48
+ if (j.goldenScore)
49
+ out.push(long ? w.judoGoldenLong : w.judoGolden);
50
+ if (j.seconds !== null)
51
+ out.push(fill(w.judoTime, { t: judoClock(j.seconds) }));
52
+ if (long) {
53
+ [0, 1].forEach((k) => {
54
+ const team = tie.sides[k];
55
+ if (j.sides[k].shido > 0 && team)
56
+ out.push(fill(w.judoShidoOf, { name: v.team(team), n: v.number(j.sides[k].shido) }));
57
+ });
58
+ }
59
+ return out;
60
+ }
61
+ /** What a row of a box writes for a side's score: a judo contest's marks, else the number. `""` when nothing was played. */
62
+ export function sideScore(tie, k, v) {
63
+ if (tie.judo)
64
+ return tie.result.score ? judoMark(tie.judo.sides[k], v) : "";
65
+ return tie.result.score ? v.number(tie.result.score[k]) : "";
66
+ }
67
+ /** A side's score in words for a sentence (a judo contest's, else the number). */
68
+ export function sideScoreLong(tie, k, v) {
69
+ if (tie.judo)
70
+ return judoSideLong(tie.judo.sides[k], v);
71
+ return tie.result.score ? v.number(tie.result.score[k]) : "";
72
+ }
73
+ /** The cards beside a side's score: a yellow one for each shido, and a red one for the third (hansoku-make) or for a direct hansoku-make: `["y", "y", "r"]`. */
74
+ export function shidoCards(tie, k) {
75
+ const j = tie.judo;
76
+ if (!j)
77
+ return [];
78
+ const n = j.sides[k].shido;
79
+ if (n >= 3)
80
+ return ["y", "y", "r"];
81
+ // A direct hansoku-make is the loser's red card, when it came with no third shido.
82
+ const penalised = j.method === "hansoku-make" && j.winner !== null && j.winner !== k;
83
+ return penalised ? [...Array.from({ length: n }, () => "y"), "r"] : Array.from({ length: n }, () => "y");
84
+ }
package/dist/judo.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ import type { BracketInput, BracketLayout, Matchup, Team } from "./types.ts";
2
+ export { JUDO_WEIGHT_CLASSES, judoOutcome, judoSeconds, judoClock } from "./judo-model.ts";
3
+ export type { JudoMethod, JudoOutcome, JudoResult, JudoSide } from "./judo-model.ts";
4
+ /** How a judo tournament is drawn: `repechage` (the IJF's quarter-final repechage, with its two bronze medal contests), or `direct` (a direct knockout in which both semi-final losers are bronze medallists). */
5
+ export type JudoSystem = "repechage" | "direct";
6
+ export interface JudoBracketOptions {
7
+ /** Default `repechage` for eight athletes or more, else `direct`. */
8
+ system?: JudoSystem;
9
+ /** The ids of the contests are `${prefix}1-1`, `${prefix}1-2`… (round, then place in the round), `${prefix}rep-1`, `${prefix}bronze-1`. Default "J". */
10
+ prefix?: string;
11
+ }
12
+ /**
13
+ * The contests of one judo weight category for any number of athletes, seeded in the standard way (the top seeds have the byes when the number is
14
+ * not a power of two). The athlete drawn first is white and the second blue (`Matchup.sides`: first white).
15
+ *
16
+ * With `repechage` (IJF Sport and Organisation Rules, 2.5.2 "Quarter-final Repechage", the system of the Olympic Games): the athletes are in two tables
17
+ * and an elimination produces two finalists. "The athletes defeated in the quarter-final will compete in two (2) repechage contests. The winners of each of
18
+ * these two (2) repechage contests will compete in bronze medal contests against the loser of the semi-final contest of the respective opposite table.
19
+ * The winners (2) of those contests are placed third." The repechage and bronze contests are `stage: "repechage"` and `stage: "bronze"` matchups, fed by
20
+ * the losers of the tree's, and are drawn under it. Needs eight athletes or more. With `direct`, there are no such contests: the two losing semi-finalists are
21
+ * the bronze medallists (`judoPodium`).
22
+ *
23
+ * Double repechage and full repechage (2.5.3 and 2.5.4) are not drawn: their pools are laid out in the rules' Appendix G charts.
24
+ */
25
+ export declare function judoBracket(athletes: readonly Team[], options?: JudoBracketOptions): {
26
+ teams: Team[];
27
+ matchups: Matchup[];
28
+ };
29
+ /** The medallists of a judo tournament, as far as it has been fought. */
30
+ export interface JudoPodium {
31
+ gold: Team | null;
32
+ silver: Team | null;
33
+ /** The two bronze medallists: the winners of the bronze medal contests, or the losing semi-finalists of a direct knockout. Only those decided. */
34
+ bronze: Team[];
35
+ }
36
+ /** The gold, silver and two bronze medals of a bracket (matchups, input or layout), from the final, the bronze medal contests or the semi-finals. */
37
+ export declare function judoPodium(input: BracketLayout | readonly Matchup[] | BracketInput): JudoPodium;
38
+ export interface JudoCategory {
39
+ sex: "men" | "women";
40
+ /** `"-66"` (up to and including 66 kg), `"+100"` (over 100 kg): see `JUDO_WEIGHT_CLASSES`. */
41
+ weight: string;
42
+ }
43
+ /** A weight category as a heading in a language: "Men -66 kg", "Women +78 kg". */
44
+ export declare function judoCategoryLabel(category: JudoCategory, locale?: string): string;