@johnmorrisdotca/kumimoji 1.2.2 → 1.3.1

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/CONTRIBUTING.md +11 -2
  3. package/README.md +80 -39
  4. package/dist/check.d.ts +26 -1
  5. package/dist/check.js +30 -3
  6. package/dist/computer.types.d.ts +24 -2
  7. package/dist/computerPlay.d.ts +45 -2
  8. package/dist/computerPlay.js +56 -6
  9. package/dist/computerTurn.d.ts +42 -3
  10. package/dist/computerTurn.js +47 -5
  11. package/dist/daily.d.ts +22 -2
  12. package/dist/daily.js +23 -3
  13. package/dist/element-define.d.ts +1 -1
  14. package/dist/element-define.js +3 -13
  15. package/dist/element.d.ts +85 -4
  16. package/dist/element.js +359 -77
  17. package/dist/embed-day.d.ts +4 -0
  18. package/dist/embed-day.js +23 -0
  19. package/dist/export.d.ts +123 -6
  20. package/dist/export.js +109 -9
  21. package/dist/generate.d.ts +14 -0
  22. package/dist/generate.js +32 -5
  23. package/dist/grid.d.ts +181 -10
  24. package/dist/grid.js +148 -11
  25. package/dist/help.d.ts +36 -3
  26. package/dist/help.js +36 -3
  27. package/dist/index.d.ts +22 -22
  28. package/dist/index.js +22 -22
  29. package/dist/judge.d.ts +14 -0
  30. package/dist/judge.js +14 -0
  31. package/dist/kana.d.ts +23 -1
  32. package/dist/kana.js +62 -7
  33. package/dist/kumimoji.types.d.ts +70 -5
  34. package/dist/party.d.ts +268 -14
  35. package/dist/party.js +296 -19
  36. package/dist/party.types.d.ts +50 -2
  37. package/dist/partyKept.d.ts +74 -4
  38. package/dist/partyKept.js +127 -12
  39. package/dist/partySeats.d.ts +92 -6
  40. package/dist/partySeats.js +83 -8
  41. package/dist/partyTurns.d.ts +171 -5
  42. package/dist/partyTurns.js +187 -8
  43. package/dist/placement.d.ts +50 -2
  44. package/dist/placement.js +35 -2
  45. package/dist/play.d.ts +247 -15
  46. package/dist/play.js +251 -18
  47. package/dist/random.d.ts +34 -3
  48. package/dist/random.js +23 -2
  49. package/dist/react.d.ts +42 -4
  50. package/dist/react.js +54 -7
  51. package/dist/showcase.d.ts +83 -7
  52. package/dist/showcase.js +61 -7
  53. package/dist/strings.d.ts +54 -5
  54. package/dist/strings.js +34 -3
  55. package/dist/tableView.d.ts +151 -8
  56. package/dist/tableView.js +129 -6
  57. package/dist/tileFace.d.ts +59 -5
  58. package/dist/tileFace.js +48 -4
  59. package/dist/tileFamily.d.ts +9 -0
  60. package/dist/tileFamily.js +9 -0
  61. package/dist/tileWords.d.ts +93 -6
  62. package/dist/tileWords.js +94 -12
  63. package/dist/tileWordsModule.d.ts +12 -2
  64. package/dist/tileWordsModule.js +14 -2
  65. package/dist/tiles.constants.d.ts +99 -5
  66. package/dist/tiles.constants.js +171 -13
  67. package/dist/turn.d.ts +82 -4
  68. package/dist/turn.js +88 -5
  69. package/dist/ui/board.d.ts +60 -5
  70. package/dist/ui/board.js +44 -4
  71. package/dist/ui/draw.d.ts +62 -0
  72. package/dist/ui/draw.js +229 -0
  73. package/dist/ui/mount.d.ts +41 -2
  74. package/dist/ui/mount.js +190 -265
  75. package/dist/ui/style.d.ts +6 -1
  76. package/dist/ui/style.js +6 -0
  77. package/dist/ui.d.ts +3 -3
  78. package/dist/ui.js +2 -2
  79. package/dist/version.d.ts +9 -2
  80. package/dist/version.js +9 -2
  81. package/dist/wilds.d.ts +22 -1
  82. package/dist/wilds.js +25 -3
  83. package/package.json +68 -61
package/CHANGELOG.md CHANGED
@@ -6,6 +6,37 @@ All notable changes to this project are written down here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.3.1] - 2026-10-31
10
+
11
+ ### Changed
12
+
13
+ - Biome replaces ESLint as the formatter and linter, with the family's shared settings in `scripts/biome-family.json`; `pnpm lint` checks formatting too, and `pnpm format` applies it. The code is formatted once to 120 columns. Nothing a user of the package sees has changed.
14
+ - TypeScript targets ES2022, treats an indexed read as possibly missing, and refuses switch fallthrough and unmarked overrides.
15
+ - `pnpm check` also holds every source file to at most 500 lines of code (`scripts/check-size.mjs`; blank lines, comments, tests and data files are not counted).
16
+
17
+ ### Fixed
18
+
19
+ - The issue chooser's security link shows again: an unquoted colon had made `.github/ISSUE_TEMPLATE/config.yml` invalid YAML.
20
+
21
+ ## [1.3.0] - 2026-10-14
22
+
23
+ The table is a custom element with the family's standard embed attributes and a shared builder, and the package has an English and Japanese documentation site with a checked example for every export. The rules, generation, seeds and saved games are unchanged.
24
+
25
+ ### Added
26
+
27
+ - Add the bilingual documentation site, checked public examples, paired guides, local search and machine-readable pages.
28
+ - Add the shared crossword-table builder and validated iframe route with reflected settings and display options. Preserve native saved/table access and gameplay methods; presentation updates now retain play.
29
+ - Add native ready/set, optional initial saved state, control visibility and localized load failures, forwarded by the React wrapper. Preserve event shapes with composed JSON-safe snapshots and add load/error events.
30
+ - Share a package-independent embed builder for option controls, preview adapters, code tabs and shareable links.
31
+
32
+ ### Changed
33
+
34
+ - Synchronize documentation tooling for repository layouts, generated type references, Node entry points and repository-specific README links.
35
+ - Keep large documentation resource catalogues on the entry index and the active resource beside core navigation, preserving every reference page; render valid default imports in HTML and Markdown.
36
+ - The props table of `KumimojiBoard` moved from the README to `docs/react.md`, so that the README, which now lists the sports packages too, stays under npm's 65,536 characters; the README keeps a summary and a link.
37
+ - Group the package family by kind, alphabetize each group, and include the geography packages and REST in Pieces in the shared footer and README.
38
+ - Update the shared family description for addresses in all seven supported countries, including France and Germany.
39
+
9
40
  ## [1.2.2] - 2026-10-06
10
41
 
11
42
  Nothing that was exported has changed.
@@ -157,7 +188,8 @@ The first release.
157
188
  `@johnmorrisdotca/kumimoji/react`.
158
189
  - A static demo for GitHub Pages.
159
190
 
160
- [Unreleased]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.2.1...HEAD
191
+ [Unreleased]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.3.0...HEAD
192
+ [1.3.0]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.2.2...v1.3.0
161
193
  [1.2.1]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.2.0...v1.2.1
162
194
  [1.1.1]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.1.0...v1.1.1
163
195
  [1.1.0]: https://github.com/johnmorrisdotca/kumimoji/releases/tag/v1.1.0
package/CONTRIBUTING.md CHANGED
@@ -21,7 +21,8 @@ shape before you spend time on it. Taking part follows the
21
21
 
22
22
  ```sh
23
23
  pnpm install
24
- pnpm check # lint, types and tests: the same as CI
24
+ pnpm check # format, lint, types, file sizes and tests: the same as CI
25
+ pnpm format # format the code and apply the safe lint fixes
25
26
  pnpm test:package # pack it as npm does, install it in an empty project, import every entry
26
27
  pnpm site # build the demo into ./site, as GitHub Pages publishes it
27
28
  ```
@@ -31,12 +32,18 @@ data scripts) are listed under its own heading below.
31
32
 
32
33
  ## House rules, shared by every package of the family
33
34
 
35
+ How the code is written, formatted and checked, and why, is in
36
+ [CODE-STYLE.md](CODE-STYLE.md), the same text in every package.
37
+
34
38
  - **No runtime dependencies.** Development dependencies are for tests, builds and
35
39
  documentation only.
36
40
  - **The core is pure.** Every function in it returns new values and never
37
41
  changes what it was given.
38
42
  - **Test what you change.** Tests sit beside the code they test. A rule you
39
43
  change has a test that would have caught it.
44
+ - **At most 500 lines of code a file** under `src/`, not counting blank lines,
45
+ comments, tests or `*.data.ts` data. A file that reaches it is split by what
46
+ it does, never squeezed.
40
47
  - **Words a person reads come in English and Japanese.** If you cannot write the
41
48
  Japanese, say so in the pull request and someone will.
42
49
  - **Option values and names are kebab case.**
@@ -48,7 +55,9 @@ data scripts) are listed under its own heading below.
48
55
  together with the code.
49
56
  - **The family's own files are the same in every package**: `demo/family.css`,
50
57
  `scripts/family-template.mjs`, `scripts/family-readme.mjs`,
51
- `scripts/release-notes.mjs`, the files in `scripts/community/` and
58
+ `scripts/release-notes.mjs`, the files in `scripts/community/`,
59
+ `CODE-STYLE.md`, `scripts/biome-family.json`, `scripts/check-size.mjs`,
60
+ `src/embed-attributes.ts` where the package has an element, and
52
61
  `family.test.js` (in `src/`, or in `test/`). Do not edit one here. To change
53
62
  one, change it in every repository at once, bump `FAMILY_TEMPLATE_VERSION` for
54
63
  the template, and record the new hash in `family.test.js`. What is the
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- <h1 align="center">Kumimoji <sub>組み文字</sub></h1>
1
+ <h1 align="center">Kumimoji <sub>組み文字</sub> — Crossword tile game in English and Japanese</h1>
2
2
 
3
3
  <p align="center"><strong>The crossword tile race: a word game where you build one crossword from a bag of letter tiles.</strong><br>
4
4
  In English and in Japanese kana. The rules as pure, seeded TypeScript, two word lists, a computer player, and a table to play on in React, Vue, Svelte, Angular or plain HTML.</p>
@@ -10,9 +10,11 @@ In English and in Japanese kana. The rules as pure, seeded TypeScript, two word
10
10
  <a href="./NOTICE.md"><img alt="Japanese word list: CC BY-SA 4.0" src="https://img.shields.io/badge/Japanese%20words-CC%20BY--SA%204.0-b5452c"></a>
11
11
  <img alt="No dependencies" src="https://img.shields.io/badge/dependencies-0-2f5d4a">
12
12
  <img alt="TypeScript" src="https://img.shields.io/badge/types-TypeScript-3178c6">
13
+ <a href="https://johnmorrisdotca.github.io/kumimoji/docs/api/main/index.html"><img alt="679.1 kB minified and gzipped" src="https://img.shields.io/badge/min%2Bgzip-679.1%20kB-2f5d4a"></a>
14
+ <a href="https://www.npmjs.com/package/@johnmorrisdotca/kumimoji#provenance"><img alt="Published with npm provenance" src="https://img.shields.io/badge/npm-provenance-2f5d4a"></a>
13
15
  </p>
14
16
 
15
- <p align="center"><a href="https://johnmorrisdotca.github.io/kumimoji/"><strong>Play a game →</strong></a> · <a href="https://johnmorrisdotca.github.io/kumimoji/api.html">API reference</a> · <a href="docs/API.md">The API in full</a></p>
17
+ <p align="center"><a href="https://johnmorrisdotca.github.io/kumimoji/"><strong>Play a game →</strong></a> · <a href="https://johnmorrisdotca.github.io/kumimoji/docs/">Documentation</a> · <a href="https://johnmorrisdotca.github.io/kumimoji/docs/guides/getting-started.html">Getting started</a> · <a href="https://johnmorrisdotca.github.io/kumimoji/api.html">API reference</a> · <a href="docs/API.md">The API in full</a></p>
16
18
 
17
19
  <table align="center">
18
20
  <tr>
@@ -184,6 +186,19 @@ Each picture is the table the package draws, taken from [the demo](https://johnm
184
186
 
185
187
  ## Use it in your project
186
188
 
189
+ ### Install under another name
190
+
191
+ ```sh
192
+ npm install word-game@npm:@johnmorrisdotca/kumimoji
193
+ ```
194
+
195
+ ```ts no-check
196
+ import "word-game/words";
197
+ import { loadTileWords, generateKumimoji } from "word-game";
198
+ await loadTileWords("english");
199
+ console.log(generateKumimoji(7,"easy",42).seed);
200
+ ```
201
+
187
202
  ### Install
188
203
 
189
204
  ```sh
@@ -384,6 +399,12 @@ fetches when a game is first dealt.
384
399
 
385
400
  The cookbook, with the output of each example, is under [Examples](#examples).
386
401
 
402
+ ### Embed builder and reflected settings
403
+
404
+ [Build a crossword-table embed](https://johnmorrisdotca.github.io/kumimoji/builder.html), preview English letters or Japanese kana and copy element, iframe, module, React 19, Vue, Svelte, Angular, HTML or URL code. It saves no play automatically. The builder/frame defaults `keep` off; the existing element default remains on for compatibility.
405
+
406
+ Every attribute reflects to a property. New `daily` accepts today or an explicit UTC date (the `seed="daily"` alias remains); `controls`, device/light/dark `theme`, `size` (small 320px, medium 480px, large 640px), pixel `width` and spoken `label` are available. `table`, `saved`, `newGame()` and `setGame()` remain. `options` accepts every native table choice, including initial saved state and callbacks. Readiness is available as `table.ready`; `table.set(...)` updates presentation without redealing. Load errors are visible and reported through `onError`/`kumimoji-error`. `defineTable` accepts a custom tag and is server-safe. See [the embed guide](docs/embeds.md).
407
+
387
408
  ## Examples
388
409
 
389
410
  Every TypeScript and JavaScript block that can run is type-checked against the built package and run by `pnpm test:readme`, so the output after `// →` is what the code prints. The word lists are read from the package's files in Node, so nothing here uses a network.
@@ -747,7 +768,7 @@ The JSON, as `kumimojiToJSON` writes it:
747
768
  {
748
769
  "format": 1,
749
770
  "game": "kumimoji",
750
- "generator": "kumimoji 1.2.2",
771
+ "generator": "kumimoji 1.3.1",
751
772
  "size": 3,
752
773
  "level": "hard",
753
774
  "seed": 44,
@@ -843,7 +864,7 @@ turn tiles into words: `glyphOf`, `soundOf`, `wordOf`, `wildFor`, `isWild`,
843
864
 
844
865
  ## API
845
866
 
846
- The [API reference](https://johnmorrisdotca.github.io/kumimoji/api.html) lists every export of every entry point with its signature and its doc comment. It is made from the source by `pnpm site`, so it cannot fall behind the code.
867
+ The [documentation](https://johnmorrisdotca.github.io/kumimoji/docs/) includes bilingual guides, local search, signatures, parameters, return values and checked examples, built from source by `pnpm docs:site`. The [API index](https://johnmorrisdotca.github.io/kumimoji/api.html) preserves export anchors.
847
868
 
848
869
  Every function, type and constant has a doc comment, so an editor shows this
849
870
  as you type. The entries:
@@ -885,7 +906,7 @@ The types are `TilePlay`, `Tiles`, `GridVerdict`, `GridRules`, `Run`,
885
906
 
886
907
  ### The table
887
908
 
888
- `mountKumimoji(element, options?)` from `@johnmorrisdotca/kumimoji/ui` deals and plays a whole game alone in the element and returns a handle (`play()`, `saved()`, `newGame(options?)`, `setGame(saved)`, `setLocale(locale, strings?)`, `destroy()`). Its options are `language`, `hand`, `level`, `gameLength`, `diagonals`, `seed`, `onFinish`, `onChange`, `locale`, `strings`, `theme` and `keep`; each, with its default, is in [docs/API.md](docs/API.md#the-table).
909
+ `mountKumimoji(element, options?)` from `@johnmorrisdotca/kumimoji/ui` deals and plays a whole game alone in the element and returns a handle (`play()`, `saved()`, `newGame(options?)`, `setGame(saved)`, `setLocale(locale, strings?)`, `ready`, `set(options)`, `destroy()`). Its options are `language`, `hand`, `level`, `gameLength`, `diagonals`, `seed`, `onFinish`, `onChange`, `locale`, `strings`, `theme`, `keep`, `controls`, `saved` and `onError`; each, with its default, is in [docs/API.md](docs/API.md#the-table).
889
910
 
890
911
  ### The element
891
912
 
@@ -914,19 +935,7 @@ export function Board() {
914
935
  }
915
936
  ```
916
937
 
917
- | `KumimojiBoard` prop | What it does |
918
- | --- | --- |
919
- | `tiles` | The tiles on the table: `play.tiles` |
920
- | `verdict` | From `judgeWithWords`: rings the runs that are not words and fades the tiles apart |
921
- | `glyphOf` | What to print on a tile: `words.glyphOf` for the language played |
922
- | `onSquare` | Called with a square's key when it is pressed, empty or not |
923
- | `held` | A square to ring, such as the one holding a tile picked up |
924
- | `square` | The side of a square in pixels; 36 by default |
925
- | anything else | Passed to the `<div>` |
926
-
927
- `KumimojiTable` takes every option of `mountKumimoji` as a prop, and anything
928
- else for its `<div>`. Options are read when it mounts; give it a new `key` to
929
- start over with different ones.
938
+ The props of `KumimojiBoard` (`tiles`, `verdict`, `glyphOf`, `onSquare`, `held`, `square`, and anything else for its `<div>`) and of `KumimojiTable` are in [docs/react.md](docs/react.md).
930
939
 
931
940
  ## Theming
932
941
 
@@ -1104,35 +1113,67 @@ issue and we will add you.
1104
1113
  ### The family
1105
1114
 
1106
1115
  <!-- family:start (made by scripts/family-readme.mjs from scripts/family-template.mjs; change those, not this) -->
1107
- Kumimoji is one of twenty-four packages, each made for the same site, each at
1108
- [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca). The code of every one is MIT.
1116
+ Kumimoji is one of thirty-two packages, each made for the same site, each at
1117
+ [github.com/johnmorrisdotca](https://github.com/johnmorrisdotca) or [github.com/spxis](https://github.com/spxis). The code of every one is MIT.
1109
1118
 
1110
- - [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/).
1111
- - [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/).
1112
- - [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/).
1113
- - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
1114
- - [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/).
1119
+ **Games — board**
1120
+
1121
+ - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
1122
+ - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
1115
1123
  - [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/).
1124
+ - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
1116
1125
  - [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/).
1117
- - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
1118
- - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
1126
+
1127
+ **Games — cards & tiles**
1128
+
1129
+ - [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/).
1119
1130
  - [Jarajara](https://github.com/johnmorrisdotca/jarajara) (ジャラジャラ): mahjong tiles drawn as SVG, stacked layouts, and the matching solitaire Awase. [Demo](https://johnmorrisdotca.github.io/jarajara/).
1120
- - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
1121
- - [Domino](https://github.com/johnmorrisdotca/domino) (ドミノ): dominoes and Mexican Train. [Demo](https://johnmorrisdotca.github.io/domino/).
1122
- - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
1123
- - [Sugoroku](https://github.com/johnmorrisdotca/sugoroku) (双六): backgammon and its variants, with the doubling cube and match play. [Demo](https://johnmorrisdotca.github.io/sugoroku/).
1131
+ - [Kumimoji](https://github.com/johnmorrisdotca/kumimoji) (組み文字): a crossword tile race, in English and Japanese kana. [Demo](https://johnmorrisdotca.github.io/kumimoji/).
1132
+ - [Toranpu](https://github.com/johnmorrisdotca/toranpu) (トランプ): a deck of playing cards, card games with computer players, and solitaires. [Demo](https://johnmorrisdotca.github.io/toranpu/).
1133
+
1134
+ **Games — puzzles**
1135
+
1136
+ - [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/).
1137
+ - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
1138
+ - [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/).
1124
1139
  - [Kazu](https://github.com/johnmorrisdotca/kazu) (数): grid number puzzles: Sudoku and its variants, Futoshiki and Skyscrapers. [Demo](https://johnmorrisdotca.github.io/kazu/).
1140
+ - [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/).
1125
1141
  - [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/).
1142
+ - [Suido](https://github.com/johnmorrisdotca/suido) (水道): a pipe puzzle: turn the pieces until the water reaches every drain. [Demo](https://johnmorrisdotca.github.io/suido/).
1143
+ - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
1144
+ - [Tsunagi](https://github.com/johnmorrisdotca/tsunagi) (繋ぎ): a line-joining logic puzzle whose every level has exactly one answer. [Demo](https://johnmorrisdotca.github.io/tsunagi/).
1145
+
1146
+ **Games — dice & random**
1147
+
1148
+ - [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/).
1149
+ - [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/).
1150
+
1151
+ **Language**
1152
+
1153
+ - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
1126
1154
  - [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/).
1155
+ - [Kotoba](https://github.com/johnmorrisdotca/kotoba) (言葉): word lists and word-game rules in English, French, German and Japanese. [Demo](https://johnmorrisdotca.github.io/kotoba/).
1156
+
1157
+ **Geography & data**
1158
+
1159
+ - [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/).
1127
1160
  - [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/).
1128
- - [Bushu](https://github.com/johnmorrisdotca/bushu) (部首): find a kanji by the parts it is made of. [Demo](https://johnmorrisdotca.github.io/bushu/).
1129
- - [Tobiishi](https://github.com/johnmorrisdotca/tobiishi) (飛び石): peg solitaire with nine boards and seeded solvable challenges. [Demo](https://johnmorrisdotca.github.io/tobiishi/).
1130
- - [Jirai](https://github.com/johnmorrisdotca/jirai) (地雷): minesweeper on shaped grids with verified no-guess boards. [Demo](https://johnmorrisdotca.github.io/jirai/).
1131
- - [Gunjin](https://github.com/johnmorrisdotca/gunjin) (軍人): five hidden-rank strategy games with pass-the-device play. [Demo](https://johnmorrisdotca.github.io/gunjin/).
1132
- - [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/).
1133
- - [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/).
1161
+ - [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/).
1162
+ - [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/).
1163
+
1164
+ **Sports**
1165
+
1166
+ - [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/).
1167
+ - [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/).
1168
+ - [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/).
1169
+ - [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/).
1170
+
1171
+ **Developer tools**
1172
+
1173
+ - [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/).
1174
+
1134
1175
 
1135
- **This package is Kumimoji.** The demos of all twenty-four share one header and footer, so each links the rest.
1176
+ **This package is Kumimoji.** The demos of all thirty-two share one header and footer, so each links the rest.
1136
1177
  <!-- family:end -->
1137
1178
 
1138
1179
  ## Development
@@ -1160,7 +1201,7 @@ Please follow the [code of conduct](./CODE_OF_CONDUCT.md). A way to make a check
1160
1201
 
1161
1202
  See [CHANGELOG.md](./CHANGELOG.md).
1162
1203
 
1163
- The latest release is 1.2.2: the README takes the family's full layout, with pictures of the table and examples that are run.
1204
+ The latest release, 1.3.1, changes nothing a player sees.
1164
1205
 
1165
1206
  ## Licence
1166
1207
 
package/dist/check.d.ts CHANGED
@@ -11,9 +11,34 @@ import type { KumimojiCheck, KumimojiLevel, KumimojiOptions } from "./kumimoji.t
11
11
  * Written out here rather than asked of the play page, which is what made the
12
12
  * grid; the list must have been loaded (`loadTileWords`), and a check that
13
13
  * cannot read it refuses.
14
+ *
15
+ * @param size - The supported initial hand size.
16
+ * @param givens - The original bag of internal tile codes.
17
+ * @param answer - The encoded completed grid to verify.
18
+ * @param options - Native game, component or presentation options.
19
+ * @returns WHETHER A GRID FINISHES A KUMIMOJI: the one check the server also runs. O(squares) and a lookup a word, nothing searched: the grid uses exactly the tiles of the bag, as many of each, it is all one piece, and every run of two or more across or down is in the list — and, for a game set up with Diagonals, every run of three or more along a diagonal. Which tiles were traded on the way does not matter — a finished game holds the whole bag whatever came out of it when — so the answer is the grid alone.
20
+ * @example
21
+ * ```ts
22
+ * import { checkKumimoji } from "@johnmorrisdotca/kumimoji";
23
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
24
+ *
25
+ * await loadTileWordsFromModule("english");
26
+ * console.log(checkKumimoji(3,"cat","cat",{level:"hard"}));
27
+ * ```
14
28
  */
15
29
  export declare function checkKumimoji(size: number, givens: string, answer: string, options?: KumimojiOptions & {
16
30
  level?: KumimojiLevel;
17
31
  }): KumimojiCheck;
18
- /** A finished game's leaderboard points (`KUMIMOJI_SCORE`): ten a tile, and up to as much again for speed. */
32
+ /** A finished game's leaderboard points (`KUMIMOJI_SCORE`): ten a tile, and up to as much again for speed.
33
+ *
34
+ * @param givens - The original bag of internal tile codes.
35
+ * @param elapsedMs - The elapsed solve time in milliseconds.
36
+ * @returns A finished game's leaderboard points (`KUMIMOJI_SCORE`): ten a tile, and up to as much again for speed.
37
+ * @example
38
+ * ```ts
39
+ * import { kumimojiPoints } from "@johnmorrisdotca/kumimoji";
40
+ *
41
+ * console.log(kumimojiPoints("cat",12000));
42
+ * ```
43
+ */
19
44
  export declare function kumimojiPoints(givens: string, elapsedMs: number): number;
package/dist/check.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { decodeGrid, lettersOf, sameLetters } from "./grid.js";
2
2
  import { judgeWithWords } from "./judge.js";
3
- import { kumimojiTileCount, kumimojiWildCount, KUMIMOJI_SCORE, TILE_MIX_TOTAL } from "./tiles.constants.js";
3
+ import { KUMIMOJI_SCORE, kumimojiTileCount, kumimojiWildCount, TILE_MIX_TOTAL } from "./tiles.constants.js";
4
4
  import { tileWords } from "./tileWords.js";
5
5
  /**
6
6
  * WHETHER A GRID FINISHES A KUMIMOJI: the one check the server also runs.
@@ -14,6 +14,20 @@ import { tileWords } from "./tileWords.js";
14
14
  * Written out here rather than asked of the play page, which is what made the
15
15
  * grid; the list must have been loaded (`loadTileWords`), and a check that
16
16
  * cannot read it refuses.
17
+ *
18
+ * @param size - The supported initial hand size.
19
+ * @param givens - The original bag of internal tile codes.
20
+ * @param answer - The encoded completed grid to verify.
21
+ * @param options - Native game, component or presentation options.
22
+ * @returns WHETHER A GRID FINISHES A KUMIMOJI: the one check the server also runs. O(squares) and a lookup a word, nothing searched: the grid uses exactly the tiles of the bag, as many of each, it is all one piece, and every run of two or more across or down is in the list — and, for a game set up with Diagonals, every run of three or more along a diagonal. Which tiles were traded on the way does not matter — a finished game holds the whole bag whatever came out of it when — so the answer is the grid alone.
23
+ * @example
24
+ * ```ts
25
+ * import { checkKumimoji } from "@johnmorrisdotca/kumimoji";
26
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
27
+ *
28
+ * await loadTileWordsFromModule("english");
29
+ * console.log(checkKumimoji(3,"cat","cat",{level:"hard"}));
30
+ * ```
17
31
  */
18
32
  export function checkKumimoji(size, givens, answer, options = {}) {
19
33
  const gameLength = options.gameLength ?? "short";
@@ -48,7 +62,9 @@ export function checkKumimoji(size, givens, answer, options = {}) {
48
62
  return { ok: false, reason: "the answer is not a grid" };
49
63
  const bagIdentities = bag.map(words.familyKey);
50
64
  const tileIdentities = [...tiles.values()].map(words.familyKey);
51
- if (bagIdentities.some((tile) => tile === null) || tileIdentities.some((tile) => tile === null) || !sameLetters(lettersOf(tileIdentities), lettersOf(bagIdentities))) {
65
+ if (bagIdentities.some((tile) => tile === null) ||
66
+ tileIdentities.some((tile) => tile === null) ||
67
+ !sameLetters(lettersOf(tileIdentities), lettersOf(bagIdentities))) {
52
68
  return { ok: false, reason: "the grid does not use exactly the tiles of the bag" };
53
69
  }
54
70
  const verdict = judgeWithWords(tiles, words, { diagonals: options.diagonals === true });
@@ -60,7 +76,18 @@ export function checkKumimoji(size, givens, answer, options = {}) {
60
76
  return { ok: false, reason: "the grid is not finished" };
61
77
  return { ok: true };
62
78
  }
63
- /** A finished game's leaderboard points (`KUMIMOJI_SCORE`): ten a tile, and up to as much again for speed. */
79
+ /** A finished game's leaderboard points (`KUMIMOJI_SCORE`): ten a tile, and up to as much again for speed.
80
+ *
81
+ * @param givens - The original bag of internal tile codes.
82
+ * @param elapsedMs - The elapsed solve time in milliseconds.
83
+ * @returns A finished game's leaderboard points (`KUMIMOJI_SCORE`): ten a tile, and up to as much again for speed.
84
+ * @example
85
+ * ```ts
86
+ * import { kumimojiPoints } from "@johnmorrisdotca/kumimoji";
87
+ *
88
+ * console.log(kumimojiPoints("cat",12000));
89
+ * ```
90
+ */
64
91
  export function kumimojiPoints(givens, elapsedMs) {
65
92
  const tiles = givens.length;
66
93
  const base = KUMIMOJI_SCORE.tile * tiles;
@@ -1,5 +1,14 @@
1
1
  import type { PartyGame } from "./party.types.ts";
2
- /** What a computer did in one step of its turn, for the line over its table. */
2
+ /** What a computer did in one step of its turn, for the line over its table.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import type { ComputerSaid } from "@johnmorrisdotca/kumimoji";
7
+ *
8
+ * const value: ComputerSaid = {kind:"drew"};
9
+ * console.log(value);
10
+ * ```
11
+ */
3
12
  export type ComputerSaid = {
4
13
  kind: "rebuilt";
5
14
  } | {
@@ -16,7 +25,20 @@ export type ComputerSaid = {
16
25
  } | {
17
26
  kind: "resigned";
18
27
  };
19
- /** One step of a computer's turn: the game after it, and what it did. The last step's game is the one kept. */
28
+ /** One step of a computer's turn: the game after it, and what it did. The last step's game is the one kept.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { startParty } from "@johnmorrisdotca/kumimoji";
33
+ * import type { ComputerStep } from "@johnmorrisdotca/kumimoji";
34
+ *
35
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
36
+ * const bag = "catdogsun";
37
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
38
+ * const value: ComputerStep = {game,said:{kind:"drew"}};
39
+ * console.log(value);
40
+ * ```
41
+ */
20
42
  export type ComputerStep = {
21
43
  game: PartyGame;
22
44
  said: ComputerSaid;
@@ -1,17 +1,60 @@
1
1
  import { type GridRules, type GridVerdict, type Tiles } from "./grid.ts";
2
2
  import { type TilePlay } from "./play.ts";
3
3
  import type { TileWords } from "./tileWords.ts";
4
- /** Whether a grid is sound, read against the loaded word list by the game's rules: the same judgement the desk shows. */
4
+ /** Whether a grid is sound, read against the loaded word list by the game's rules: the same judgement the desk shows.
5
+ *
6
+ * @param tiles - The map from row,column square names to internal tile codes.
7
+ * @param words - The loaded native language word list and tile mappings.
8
+ * @param rules - Whether diagonal runs are also judged.
9
+ * @returns Whether a grid is sound, read against the loaded word list by the game's rules: the same judgement the desk shows.
10
+ * @example
11
+ * ```ts
12
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
13
+ * import { judgeTiles } from "@johnmorrisdotca/kumimoji";
14
+ *
15
+ * const words = await loadTileWordsFromModule("english");
16
+ * const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
17
+ * console.log(judgeTiles(tiles,words));
18
+ * ```
19
+ */
5
20
  export declare function judgeTiles(tiles: Tiles, words: TileWords, rules?: GridRules): GridVerdict;
6
21
  /**
7
22
  * THE COMPUTER'S NEXT WORD: its play after laying the best word its hand can
8
23
  * lay on its table, and the word, or null when there is none. The grid after
9
24
  * it is judged against the list, by the game's rules (`rules`), and is
10
25
  * always sound.
26
+ *
27
+ * @param play - The current immutable solo-game progress.
28
+ * @param words - The loaded native language word list and tile mappings.
29
+ * @param rules - Whether diagonal runs are also judged.
30
+ * @returns THE COMPUTER'S NEXT WORD: its play after laying the best word its hand can lay on its table, and the word, or null when there is none. The grid after it is judged against the list, by the game's rules (`rules`), and is always sound.
31
+ * @example
32
+ * ```ts
33
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
34
+ * import { bestLaying, deal } from "@johnmorrisdotca/kumimoji";
35
+ *
36
+ * const words = await loadTileWordsFromModule("english");
37
+ * const play = deal("catdog",3);
38
+ * console.log(bestLaying(play,words)?.word);
39
+ * ```
11
40
  */
12
41
  export declare function bestLaying(play: TilePlay, words: TileWords, rules?: GridRules): {
13
42
  play: TilePlay;
14
43
  word: string;
15
44
  } | null;
16
- /** The tile a computer trades: its rarest letter in the set, the first of those in its hand; never a wild. Null for a hand of wilds. */
45
+ /** The tile a computer trades: its rarest letter in the set, the first of those in its hand; never a wild. Null for a hand of wilds.
46
+ *
47
+ * @param hand - The hand size or tile codes, as declared by the function signature.
48
+ * @param words - The loaded native language word list and tile mappings.
49
+ * @returns The tile a computer trades: its rarest letter in the set, the first of those in its hand; never a wild. null for a hand of wilds.
50
+ * @example
51
+ * ```ts
52
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
53
+ * import { deal, tradeChoice } from "@johnmorrisdotca/kumimoji";
54
+ *
55
+ * const words = await loadTileWordsFromModule("english");
56
+ * const play = deal("catdog",3);
57
+ * console.log(tradeChoice(play.hand,words));
58
+ * ```
59
+ */
17
60
  export declare function tradeChoice(hand: readonly string[], words: TileWords): number | null;
@@ -28,7 +28,22 @@ function plainOf(tile, words) {
28
28
  const sound = words.soundOf(tile);
29
29
  return sound === null ? "" : (words.codeOf(sound) ?? "");
30
30
  }
31
- /** Whether a grid is sound, read against the loaded word list by the game's rules: the same judgement the desk shows. */
31
+ /** Whether a grid is sound, read against the loaded word list by the game's rules: the same judgement the desk shows.
32
+ *
33
+ * @param tiles - The map from row,column square names to internal tile codes.
34
+ * @param words - The loaded native language word list and tile mappings.
35
+ * @param rules - Whether diagonal runs are also judged.
36
+ * @returns Whether a grid is sound, read against the loaded word list by the game's rules: the same judgement the desk shows.
37
+ * @example
38
+ * ```ts
39
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
40
+ * import { judgeTiles } from "@johnmorrisdotca/kumimoji";
41
+ *
42
+ * const words = await loadTileWordsFromModule("english");
43
+ * const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
44
+ * console.log(judgeTiles(tiles,words));
45
+ * ```
46
+ */
32
47
  export function judgeTiles(tiles, words, rules = {}) {
33
48
  return judgeWithWords(tiles, words, rules);
34
49
  }
@@ -61,7 +76,7 @@ function lacking(word, hand, most) {
61
76
  /** How many tiles already down one word may run through: two lets it bridge a gap, as players do. */
62
77
  const CROSSED_MOST = 2;
63
78
  /** How many placements are tried in one search at most, so a turn's thinking stays a fraction of a second on a phone. */
64
- const FITS_MOST = 20000;
79
+ const FITS_MOST = 20_000;
65
80
  /** A laying's worth: tiles used first, rare letters next, wilds spent last. */
66
81
  function worth(fresh, wilds, words) {
67
82
  let rare = 0;
@@ -103,7 +118,8 @@ function layings(play, words, rules) {
103
118
  }
104
119
  const bounds = boundsOf(play.tiles);
105
120
  const fits = (first, last) => bounds === null ||
106
- (Math.max(bounds.bottom, last.row) - Math.min(bounds.top, first.row) < KUMIMOJI_GRID_MOST && Math.max(bounds.right, last.col) - Math.min(bounds.left, first.col) < KUMIMOJI_GRID_MOST);
121
+ (Math.max(bounds.bottom, last.row) - Math.min(bounds.top, first.row) < KUMIMOJI_GRID_MOST &&
122
+ Math.max(bounds.right, last.col) - Math.min(bounds.left, first.col) < KUMIMOJI_GRID_MOST);
107
123
  const found = [];
108
124
  let tried = 0;
109
125
  let mostFresh = 0;
@@ -135,9 +151,15 @@ function layings(play, words, rules) {
135
151
  if (fit === null)
136
152
  continue;
137
153
  const step = across ? { row: 0, col: 1 } : { row: 1, col: 0 };
138
- if (!fits(fit.first, { row: fit.first.row + (length - 1) * step.row, col: fit.first.col + (length - 1) * step.col }))
154
+ if (!fits(fit.first, {
155
+ row: fit.first.row + (length - 1) * step.row,
156
+ col: fit.first.col + (length - 1) * step.col,
157
+ }))
139
158
  continue;
140
- const fresh = fit.fresh.map((square) => ({ square: squareAt(square.row, square.col), letter: word[(square.row - fit.first.row) + (square.col - fit.first.col)] }));
159
+ const fresh = fit.fresh.map((square) => ({
160
+ square: squareAt(square.row, square.col),
161
+ letter: word[square.row - fit.first.row + (square.col - fit.first.col)],
162
+ }));
141
163
  const wilds = wildsNeeded(fresh, hand);
142
164
  if (wilds !== null)
143
165
  keep({ word, fresh, wilds, score: worth(fresh, wilds, words) });
@@ -173,6 +195,20 @@ const JUDGED_MOST = 12;
173
195
  * lay on its table, and the word, or null when there is none. The grid after
174
196
  * it is judged against the list, by the game's rules (`rules`), and is
175
197
  * always sound.
198
+ *
199
+ * @param play - The current immutable solo-game progress.
200
+ * @param words - The loaded native language word list and tile mappings.
201
+ * @param rules - Whether diagonal runs are also judged.
202
+ * @returns THE COMPUTER'S NEXT WORD: its play after laying the best word its hand can lay on its table, and the word, or null when there is none. The grid after it is judged against the list, by the game's rules (`rules`), and is always sound.
203
+ * @example
204
+ * ```ts
205
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
206
+ * import { bestLaying, deal } from "@johnmorrisdotca/kumimoji";
207
+ *
208
+ * const words = await loadTileWordsFromModule("english");
209
+ * const play = deal("catdog",3);
210
+ * console.log(bestLaying(play,words)?.word);
211
+ * ```
176
212
  */
177
213
  export function bestLaying(play, words, rules = {}) {
178
214
  if (play.hand.length === 0)
@@ -186,7 +222,21 @@ export function bestLaying(play, words, rules = {}) {
186
222
  }
187
223
  return null;
188
224
  }
189
- /** The tile a computer trades: its rarest letter in the set, the first of those in its hand; never a wild. Null for a hand of wilds. */
225
+ /** The tile a computer trades: its rarest letter in the set, the first of those in its hand; never a wild. Null for a hand of wilds.
226
+ *
227
+ * @param hand - The hand size or tile codes, as declared by the function signature.
228
+ * @param words - The loaded native language word list and tile mappings.
229
+ * @returns The tile a computer trades: its rarest letter in the set, the first of those in its hand; never a wild. null for a hand of wilds.
230
+ * @example
231
+ * ```ts
232
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
233
+ * import { deal, tradeChoice } from "@johnmorrisdotca/kumimoji";
234
+ *
235
+ * const words = await loadTileWordsFromModule("english");
236
+ * const play = deal("catdog",3);
237
+ * console.log(tradeChoice(play.hand,words));
238
+ * ```
239
+ */
190
240
  export function tradeChoice(hand, words) {
191
241
  let chosen = null;
192
242
  let fewest = Infinity;