@johnmorrisdotca/kumimoji 1.3.1 → 1.5.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 +26 -1
- package/NOTICE.md +1 -1
- package/README.md +22 -29
- package/dist/check-reasons.d.ts +71 -0
- package/dist/check-reasons.js +47 -0
- package/dist/check.js +10 -9
- package/dist/element.d.ts +3 -0
- package/dist/element.js +20 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/kumimoji.types.d.ts +5 -2
- package/dist/ui/cloth.d.ts +72 -0
- package/dist/ui/cloth.js +49 -0
- package/dist/ui/mount.d.ts +4 -1
- package/dist/ui/mount.js +14 -5
- package/dist/ui/style.d.ts +1 -1
- package/dist/ui/style.js +1 -0
- package/dist/ui.d.ts +1 -0
- package/dist/ui.js +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/words.ja.data.d.ts +3 -2
- package/dist/words.ja.data.js +18 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,29 @@ All notable changes to this project are written down here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.5.0] - 2026-11-04
|
|
10
|
+
|
|
11
|
+
A minor version: nothing that 1.4.0 gave is taken away except what children should not have been handed. The English list, the rules and the saved-game format are unchanged.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- **The Japanese word list leaves out what is not for children** (MODULES-390). Children play this game, and the list was every kana reading of every JMdict entry. It now has the bar of Kotoba's kana lists (`scripts/children-bar.mjs` in Kotoba, which writes the list with `scripts/word-lists-ja.mjs`): a reading is out when every JMdict entry that spells it has a vulgar, X-rated, sensitive or derogatory mark on its first sense, or a first sense whose gloss names sex or the sex trade. A reading that an ordinary entry is also spelt like stays, and so does a line that another word plays: the tiles play ば and ぱ as は, so れいぷ is the line れいふ, which is also 霊符 (a charm). Of the 163,585 lines JMdict's release of 2026-11-04 gives, 1,347 are barred (110 of 3 kana, 347 of 4, 264 of 5, 626 longer), among them せっくす, ばいしゅん, ぽるの, えろい, おなに, ちくび, ふぇらちお, せいどれい, いんらん, ちんぽ and らぶほてる; the 5 words earlier lists held that JMdict no longer lists are kept, so a game kept half way still checks. The list holds **162,243** readings (163,461 in 1.4.0). A test (`src/childrenBar.test.ts`) holds the known bad readings out. The guide "Load and judge with word lists" says what is left out, in English and in Japanese.
|
|
16
|
+
- **A seeded Japanese game is dealt differently**, as the list has fewer words to choose among: the same seed gives another bag. English games are unchanged, and so is the saved-game format; a kept Japanese game checks as before unless it spells one of the barred lines.
|
|
17
|
+
- The Japanese list is made again from JMdict's release of 2026-11-04 (it was 2026-09-28): 129 readings join, 5 are kept that JMdict dropped.
|
|
18
|
+
- The package is a little smaller: the Japanese list is 715 kB (394 kB gzipped), and the sizes in `docs/bundle-size.json` are measured again.
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- The README opens with the package's thumbnail, and the demo carries it as its link preview (og:image) and publishes the card, a dark card and an icon on Pages at `thumbnail.png`, `thumbnail-dark.png` and `icon.png`.
|
|
23
|
+
|
|
24
|
+
## [1.4.0] - 2026-11-02
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- A table can be laid in the family's five cloths: `<kumimoji-table cloth="blue">`, `mountKumimoji(el, { cloth })` and `set({ cloth })`, the `cloth` embed parameter and Builder option, and `KUMIMOJI_CLOTHS`, `isCloth` and `clothVars` from `/ui`. The demo's header patches lay it through the page's theme.
|
|
29
|
+
|
|
30
|
+
- **`checkKumimoji` gives its reason as a code and values as well as in English.** A refusal is now `{ ok: false, reason, code, values }`: `reason` is the sentence it has always been, word for word, and `code` (`"no-hand"`, `"givens-not-bag"`, `"wild-count-wrong"`, `"bag-exceeds-set"`, `"answer-not-grid"`, `"grid-not-the-bag"`, `"tiles-apart"`, `"not-a-word"` and `"grid-not-finished"`) and `values` (`{ size }` for `no-hand`, `{ word }` for `not-a-word`) let a site say it in its own language. `KUMIMOJI_CHECK_REASONS` lists every code with its English clause, `kumimojiRefusal(code, values)` makes a refusal, and the types are `KumimojiCheckCode` and `KumimojiCheckValues`. A test holds every refusal's `reason` to its code's clause filled with its values.
|
|
31
|
+
|
|
9
32
|
## [1.3.1] - 2026-10-31
|
|
10
33
|
|
|
11
34
|
### Changed
|
|
@@ -188,7 +211,9 @@ The first release.
|
|
|
188
211
|
`@johnmorrisdotca/kumimoji/react`.
|
|
189
212
|
- A static demo for GitHub Pages.
|
|
190
213
|
|
|
191
|
-
[Unreleased]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.
|
|
214
|
+
[Unreleased]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.5.0...HEAD
|
|
215
|
+
[1.5.0]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.4.0...v1.5.0
|
|
216
|
+
[1.4.0]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.3.1...v1.4.0
|
|
192
217
|
[1.3.0]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.2.2...v1.3.0
|
|
193
218
|
[1.2.1]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.2.0...v1.2.1
|
|
194
219
|
[1.1.1]: https://github.com/johnmorrisdotca/kumimoji/compare/v1.1.0...v1.1.1
|
package/NOTICE.md
CHANGED
|
@@ -31,7 +31,7 @@ SCOWL's own distribution.
|
|
|
31
31
|
|
|
32
32
|
`src/words.ja.data.ts` is derived from JMdict, the Japanese-Multilingual
|
|
33
33
|
Dictionary of the Electronic Dictionary Research and Development Group
|
|
34
|
-
(https://www.edrdg.org/), release 2026-
|
|
34
|
+
(https://www.edrdg.org/), release 2026-11-04: 162,243 hiragana readings of 2 to
|
|
35
35
|
15 kana, each spelt in the 45 base kana the tiles show. JMdict is the property of
|
|
36
36
|
the EDRDG and is used under the Creative Commons Attribution-ShareAlike 4.0
|
|
37
37
|
licence, with the group's conditions: https://www.edrdg.org/edrdg/licence.html.
|
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<p align="center"><a href="https://johnmorrisdotca.github.io/kumimoji/"><picture><source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/thumbnail-dark.png"><img src="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/thumbnail.png" alt="Kumimoji: letter tiles laid in a crossword by the package's own computer player." width="1200"></picture></a></p>
|
|
2
|
+
|
|
1
3
|
<h1 align="center">Kumimoji <sub>組み文字</sub> — Crossword tile game in English and Japanese</h1>
|
|
2
4
|
|
|
3
5
|
<p align="center"><strong>The crossword tile race: a word game where you build one crossword from a bag of letter tiles.</strong><br>
|
|
@@ -10,7 +12,7 @@ In English and in Japanese kana. The rules as pure, seeded TypeScript, two word
|
|
|
10
12
|
<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
13
|
<img alt="No dependencies" src="https://img.shields.io/badge/dependencies-0-2f5d4a">
|
|
12
14
|
<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="
|
|
15
|
+
<a href="https://johnmorrisdotca.github.io/kumimoji/docs/api/main/index.html"><img alt="677.1 kB minified and gzipped" src="https://img.shields.io/badge/min%2Bgzip-677.1%20kB-2f5d4a"></a>
|
|
14
16
|
<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>
|
|
15
17
|
</p>
|
|
16
18
|
|
|
@@ -103,7 +105,7 @@ Or with nothing to install, [play a game in the demo](https://johnmorrisdotca.gi
|
|
|
103
105
|
- **Races and daily puzzles.** A seed deals one bag, the same for everybody,
|
|
104
106
|
and `checkKumimoji` checks a finished grid against it without searching.
|
|
105
107
|
- **Japanese.** A word game that plays in kana, with が on the か tile and ゃ
|
|
106
|
-
on the や tile, and a list of
|
|
108
|
+
on the や tile, and a list of 162,243 readings.
|
|
107
109
|
- **Learners and teachers** of English or Japanese spelling.
|
|
108
110
|
|
|
109
111
|
## Features
|
|
@@ -112,7 +114,7 @@ Or with nothing to install, [play a game in the demo](https://johnmorrisdotca.gi
|
|
|
112
114
|
its tiles, so every bag can be finished.
|
|
113
115
|
- **English and Japanese.** 144 tiles in each: the English letters, or 45 base
|
|
114
116
|
kana where が plays as か, ゃ as や and を as お. 110,316 English words and
|
|
115
|
-
|
|
117
|
+
162,243 Japanese readings, of 2 to 15 tiles.
|
|
116
118
|
- **Wild tiles** that take any letter or kana, and are read as that in every
|
|
117
119
|
word they are part of.
|
|
118
120
|
- **Trades.** A tile you cannot use goes back into the bag for three.
|
|
@@ -155,7 +157,7 @@ Each picture is the table the package draws, taken from [the demo](https://johnm
|
|
|
155
157
|
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/kana-desk-dark.webp">
|
|
156
158
|
<img src="https://raw.githubusercontent.com/johnmorrisdotca/kumimoji/main/docs/images/kana-desk-light.webp" alt="A Japanese board of kana tiles on the green table, a crossword of hiragana laid across and down on the grid of empty squares." width="400">
|
|
157
159
|
</picture>
|
|
158
|
-
<br><em><strong>Japanese kana.</strong> 45 base kana tiles,
|
|
160
|
+
<br><em><strong>Japanese kana.</strong> 45 base kana tiles, 162,243 readings.</em>
|
|
159
161
|
</td>
|
|
160
162
|
</tr>
|
|
161
163
|
<tr>
|
|
@@ -242,7 +244,7 @@ const wrong = decodeGrid("cat/2x")!;
|
|
|
242
244
|
judgeWithWords(wrong, words).notWords; // → ["tx"]
|
|
243
245
|
|
|
244
246
|
checkKumimoji(3, "nwosy", "snowy", { level: "hard" }); // → { ok: true }
|
|
245
|
-
checkKumimoji(3, "nwosy", "snows", { level: "hard" }); // → { ok: false, reason: "the grid does not use exactly the tiles of the bag" }
|
|
247
|
+
checkKumimoji(3, "nwosy", "snows", { level: "hard" }); // → { ok: false, reason: "the grid does not use exactly the tiles of the bag", code: "grid-not-the-bag" }
|
|
246
248
|
encodeGrid(tiles); // → "cat/2o/2w"
|
|
247
249
|
```
|
|
248
250
|
|
|
@@ -390,8 +392,8 @@ fetches when a game is first dealt.
|
|
|
390
392
|
names only the files that define the tag and register the word lists.
|
|
391
393
|
- **Sizes.** The rules, the computer player and the table of players are
|
|
392
394
|
about 44 kB minified (16 kB gzipped); the table for a page is 34 kB (13 kB).
|
|
393
|
-
The English list is 606 kB (277 kB gzipped) and the Japanese
|
|
394
|
-
(
|
|
395
|
+
The English list is 606 kB (277 kB gzipped) and the Japanese 715 kB
|
|
396
|
+
(394 kB), each a file of its own that is fetched only when a game in that
|
|
395
397
|
language is first dealt.
|
|
396
398
|
- **Where it runs.** Current Chrome, Edge, Firefox and Safari, on a desk or a
|
|
397
399
|
phone. The rules have no DOM in them and run in Node 22 and later, Deno,
|
|
@@ -541,7 +543,7 @@ import { checkKumimoji, generateKumimoji, loadTileWords } from "@johnmorrisdotca
|
|
|
541
543
|
await loadTileWords("english");
|
|
542
544
|
const dealt = generateKumimoji(3, "hard", 44);
|
|
543
545
|
checkKumimoji(3, dealt.givens, "snowy", { level: "hard" }); // → { ok: true }
|
|
544
|
-
checkKumimoji(3, dealt.givens, "snows", { level: "hard" }); // → { ok: false, reason: "the grid does not use exactly the tiles of the bag" }
|
|
546
|
+
checkKumimoji(3, dealt.givens, "snows", { level: "hard" }); // → { ok: false, reason: "the grid does not use exactly the tiles of the bag", code: "grid-not-the-bag" }
|
|
545
547
|
```
|
|
546
548
|
|
|
547
549
|
### Mount the table with options
|
|
@@ -566,6 +568,8 @@ table.destroy();
|
|
|
566
568
|
|
|
567
569
|
### A look of your own
|
|
568
570
|
|
|
571
|
+
`<kumimoji-table cloth="blue">`, or `cloth: "blue"` when you mount, lays the table in one of the family's five cloths, `green`, `blue`, `red`, `black` or `wood`, the same set as the boards on itsutsu.com; `set({ cloth })` changes it in place, keeping the game, and `theme` is laid over it. `KUMIMOJI_CLOTHS` and `clothVars` hold the colours.
|
|
572
|
+
|
|
569
573
|
Every colour is a CSS variable on `.km-root`, so a stylesheet or the `theme` option can restyle the table:
|
|
570
574
|
|
|
571
575
|
```ts no-run
|
|
@@ -670,11 +674,11 @@ import { BASE_KANA, deal, generateKumimoji, loadTileWords, tileKana } from "@joh
|
|
|
670
674
|
const words = await loadTileWords("japanese");
|
|
671
675
|
[...BASE_KANA].length; // → 45
|
|
672
676
|
[tileKana("が"), tileKana("ゃ"), tileKana("を"), tileKana("っ")]; // → ["か", "や", "お", "つ"]
|
|
673
|
-
words.allowed.size; // →
|
|
677
|
+
words.allowed.size; // → 162243
|
|
674
678
|
words.allowed.has("ことは"); // → true
|
|
675
679
|
|
|
676
680
|
const dealt = generateKumimoji(7, "medium", 2026, { language: "japanese" });
|
|
677
|
-
deal(dealt.givens, 7).hand.map(words.glyphOf).join(""); // → "
|
|
681
|
+
deal(dealt.givens, 7).hand.map(words.glyphOf).join(""); // → "つかてしんきし"
|
|
678
682
|
```
|
|
679
683
|
|
|
680
684
|
The tiles are the 45 base hiragana. A word is spelt with the base of each of
|
|
@@ -768,7 +772,7 @@ The JSON, as `kumimojiToJSON` writes it:
|
|
|
768
772
|
{
|
|
769
773
|
"format": 1,
|
|
770
774
|
"game": "kumimoji",
|
|
771
|
-
"generator": "kumimoji 1.
|
|
775
|
+
"generator": "kumimoji 1.5.0",
|
|
772
776
|
"size": 3,
|
|
773
777
|
"level": "hard",
|
|
774
778
|
"seed": 44,
|
|
@@ -813,7 +817,7 @@ with its notice too.
|
|
|
813
817
|
| List | What it is | Where it comes from | Terms |
|
|
814
818
|
| --- | --- | --- | --- |
|
|
815
819
|
| English, `dist/words.en.data.js` | 110,316 words of 2 to 15 letters, English and American spellings | [SCOWL](http://wordlist.aspell.net/) 2020.12.07, sizes 10 to 70 | © 2000–2018 Kevin Atkinson and the authors of the lists SCOWL is built from. Permission to use, copy, modify, distribute and sell for any purpose, **provided the copyright notice appears in all copies, and both it and the permission notice in supporting documentation**. No share-alike. |
|
|
816
|
-
| Japanese, `dist/words.ja.data.js` |
|
|
820
|
+
| Japanese, `dist/words.ja.data.js` | 162,243 hiragana readings of 2 to 15 kana, each spelt in the 45 base kana of the tiles | [JMdict](https://www.edrdg.org/jmdict/j_jmdict.html), release 2026-11-04, of the Electronic Dictionary Research and Development Group | **[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)**, with the [EDRDG's conditions](https://www.edrdg.org/edrdg/licence.html). **Attribution is required, and the list, or anything made from it, may be passed on only under the same licence.** |
|
|
817
821
|
|
|
818
822
|
What that means for a project that uses this package:
|
|
819
823
|
|
|
@@ -886,23 +890,14 @@ as you type. The entries:
|
|
|
886
890
|
| `deal(bag, handSize)` and `placeFromHand`, `moveOnTable`, `liftToHand`, `draw`, `trade` | The opening hand, and each move, every one returning a new game |
|
|
887
891
|
| `judgeWithWords(tiles, words)` | What is wrong with a grid, or that it is sound |
|
|
888
892
|
| `isFinished(play, verdict)` | Whether the bag is empty, the hand used and every run a word in one piece |
|
|
889
|
-
| `checkKumimoji(size, givens, answer, options?)` | The one check a server runs on a finished grid |
|
|
893
|
+
| `checkKumimoji(size, givens, answer, options?)` | The one check a server runs on a finished grid; a refusal has `reason`, `code`, `values` (`KUMIMOJI_CHECK_REASONS`, `kumimojiRefusal`) |
|
|
890
894
|
| `loadTileWords(language)` | A word list, fetched when first needed |
|
|
891
895
|
| `mountKumimoji(element, options?)` and `<kumimoji-table>` | The table, in one call or one tag |
|
|
892
896
|
| `kumimojiToJSON`, `kumimojiFromJSON` | A game kept, and read back without trusting it |
|
|
893
897
|
|
|
894
898
|
### The long tables
|
|
895
899
|
|
|
896
|
-
The tables of every export for dealing and judging, tiles and grids, playing and keeping a game, a table of players and looking at the table are in [docs/API.md](docs/API.md), and every export of every entry point, with its signature and doc comment, is in the [API reference](https://johnmorrisdotca.github.io/kumimoji/api.html). The types the package exports are listed there too.
|
|
897
|
-
|
|
898
|
-
The types are `TilePlay`, `Tiles`, `GridVerdict`, `GridRules`, `Run`,
|
|
899
|
-
`RunLine`, `Bounds`, `Square`, `CrossingFit`, `KumimojiDeal`,
|
|
900
|
-
`KumimojiOptions`, `KumimojiCheck`, `KumimojiLevel`, `KumimojiLength`,
|
|
901
|
-
`KumimojiLanguage`, `TileWords`, `TileFaceOf`, `PartyGame`, `PartySettings`,
|
|
902
|
-
`PartyPlayer`, `PartySeat`, `PartyEnding`, `JoinRefusal`, `LeaveRefusal`,
|
|
903
|
-
`ComputerStep`, `ComputerSaid`, `KumimojiSaved`, `KumimojiExported`, `KumimojiStrings`,
|
|
904
|
-
`KumimojiLocale`, `Random`, `Turn`, `Area`, `View`, `MixTile`, `MixShown` and
|
|
905
|
-
`LengthRow`.
|
|
900
|
+
The types, and the tables of every export for dealing and judging, tiles and grids, playing and keeping a game, a table of players and looking at the table are in [docs/API.md](docs/API.md), and every export of every entry point, with its signature and doc comment, is in the [API reference](https://johnmorrisdotca.github.io/kumimoji/api.html). The types the package exports are listed there too.
|
|
906
901
|
|
|
907
902
|
### The table
|
|
908
903
|
|
|
@@ -1069,8 +1064,6 @@ kept as text made to be sent, and Itsutsu sends it; the sending is yours); a
|
|
|
1069
1064
|
command line; CSV; and any name, art or wording of a published game. Kumimoji
|
|
1070
1065
|
runs from a static page and costs nothing to host.
|
|
1071
1066
|
|
|
1072
|
-
Ideas and pull requests are welcome.
|
|
1073
|
-
|
|
1074
1067
|
## Architecture
|
|
1075
1068
|
|
|
1076
1069
|
The rules are plain functions over plain data with no DOM: a game is a value,
|
|
@@ -1180,16 +1173,16 @@ Kumimoji is one of thirty-two packages, each made for the same site, each at
|
|
|
1180
1173
|
|
|
1181
1174
|
```sh
|
|
1182
1175
|
pnpm install --frozen-lockfile
|
|
1183
|
-
pnpm check # lint, types and tests,
|
|
1176
|
+
pnpm check # lint, types and tests, the README's own checks among them
|
|
1184
1177
|
pnpm test:table # the demo in real browsers, by taps
|
|
1185
1178
|
pnpm test:package # pack it as npm does, install it and import every entry
|
|
1186
1179
|
pnpm test:frameworks # the six framework examples, built from the packed tarball and played
|
|
1187
1180
|
pnpm test:readme # every TypeScript and JavaScript example in this README, type-checked and run
|
|
1188
1181
|
pnpm site # build the demo into ./site
|
|
1189
|
-
pnpm screenshots:readme # retake the README's pictures
|
|
1182
|
+
pnpm screenshots:readme # retake the README's pictures (builds the demo first)
|
|
1190
1183
|
```
|
|
1191
1184
|
|
|
1192
|
-
The
|
|
1185
|
+
The README's pictures are in `docs/images`, which the npm package leaves out.
|
|
1193
1186
|
|
|
1194
1187
|
## Contributing
|
|
1195
1188
|
|
|
@@ -1201,7 +1194,7 @@ Please follow the [code of conduct](./CODE_OF_CONDUCT.md). A way to make a check
|
|
|
1201
1194
|
|
|
1202
1195
|
See [CHANGELOG.md](./CHANGELOG.md).
|
|
1203
1196
|
|
|
1204
|
-
The latest release, 1.
|
|
1197
|
+
The latest release, 1.5.0, leaves words that are not for children out of the Japanese list.
|
|
1205
1198
|
|
|
1206
1199
|
## Licence
|
|
1207
1200
|
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { KumimojiCheck } from "./kumimoji.types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Why `checkKumimoji` will not take a grid, as a stable code and the English clause it says: `{name}` is a gap the
|
|
4
|
+
* check's `values` fill. The clause is what `reason` has always said, word for word; a site that speaks another
|
|
5
|
+
* language says the code in its own words and puts the values in the gaps.
|
|
6
|
+
*
|
|
7
|
+
* | Code | Values |
|
|
8
|
+
* | --- | --- |
|
|
9
|
+
* | `no-hand` | `size`: the hand that has no game |
|
|
10
|
+
* | `not-a-word` | `word`: the first word that is not in the word list |
|
|
11
|
+
* | `givens-not-bag`, `wild-count-wrong`, `bag-exceeds-set`, `answer-not-grid`, `grid-not-the-bag`, `tiles-apart`, `grid-not-finished` | none |
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { KUMIMOJI_CHECK_REASONS } from "@johnmorrisdotca/kumimoji";
|
|
16
|
+
*
|
|
17
|
+
* console.log(KUMIMOJI_CHECK_REASONS["not-a-word"]);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export declare const KUMIMOJI_CHECK_REASONS: {
|
|
21
|
+
readonly "no-hand": "no Kumimoji with a hand of {size}";
|
|
22
|
+
readonly "givens-not-bag": "the givens are not a bag of tiles";
|
|
23
|
+
readonly "wild-count-wrong": "the bag has the wrong number of wild tiles";
|
|
24
|
+
readonly "bag-exceeds-set": "the bag exceeds the tile set";
|
|
25
|
+
readonly "answer-not-grid": "the answer is not a grid";
|
|
26
|
+
readonly "grid-not-the-bag": "the grid does not use exactly the tiles of the bag";
|
|
27
|
+
readonly "tiles-apart": "the tiles are not all joined";
|
|
28
|
+
readonly "not-a-word": "{word} is not in the word list";
|
|
29
|
+
readonly "grid-not-finished": "the grid is not finished";
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A reason `checkKumimoji` can give, by its stable code.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import type { KumimojiCheckCode } from "@johnmorrisdotca/kumimoji";
|
|
37
|
+
*
|
|
38
|
+
* const code: KumimojiCheckCode = "tiles-apart";
|
|
39
|
+
* console.log(code);
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export type KumimojiCheckCode = keyof typeof KUMIMOJI_CHECK_REASONS;
|
|
43
|
+
/**
|
|
44
|
+
* The numbers and words that fill a reason's gaps, by the gap's name (see `KUMIMOJI_CHECK_REASONS` for each code's).
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import type { KumimojiCheckValues } from "@johnmorrisdotca/kumimoji";
|
|
49
|
+
*
|
|
50
|
+
* const values: KumimojiCheckValues = { word: "aow" };
|
|
51
|
+
* console.log(values);
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export type KumimojiCheckValues = Readonly<Record<string, string | number>>;
|
|
55
|
+
/**
|
|
56
|
+
* A refusal: `{ ok: false }`, the English `reason` (the clause of the code with the values in its gaps), the code
|
|
57
|
+
* and the values.
|
|
58
|
+
*
|
|
59
|
+
* @param code - Why the grid is not taken.
|
|
60
|
+
* @param values - What fills the clause's gaps, where it has any.
|
|
61
|
+
* @returns The refusal `checkKumimoji` gives.
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { kumimojiRefusal } from "@johnmorrisdotca/kumimoji";
|
|
65
|
+
*
|
|
66
|
+
* console.log(kumimojiRefusal("not-a-word", { word: "aow" }));
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare function kumimojiRefusal(code: KumimojiCheckCode, values?: KumimojiCheckValues): Extract<KumimojiCheck, {
|
|
70
|
+
ok: false;
|
|
71
|
+
}>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why `checkKumimoji` will not take a grid, as a stable code and the English clause it says: `{name}` is a gap the
|
|
3
|
+
* check's `values` fill. The clause is what `reason` has always said, word for word; a site that speaks another
|
|
4
|
+
* language says the code in its own words and puts the values in the gaps.
|
|
5
|
+
*
|
|
6
|
+
* | Code | Values |
|
|
7
|
+
* | --- | --- |
|
|
8
|
+
* | `no-hand` | `size`: the hand that has no game |
|
|
9
|
+
* | `not-a-word` | `word`: the first word that is not in the word list |
|
|
10
|
+
* | `givens-not-bag`, `wild-count-wrong`, `bag-exceeds-set`, `answer-not-grid`, `grid-not-the-bag`, `tiles-apart`, `grid-not-finished` | none |
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { KUMIMOJI_CHECK_REASONS } from "@johnmorrisdotca/kumimoji";
|
|
15
|
+
*
|
|
16
|
+
* console.log(KUMIMOJI_CHECK_REASONS["not-a-word"]);
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export const KUMIMOJI_CHECK_REASONS = {
|
|
20
|
+
"no-hand": "no Kumimoji with a hand of {size}",
|
|
21
|
+
"givens-not-bag": "the givens are not a bag of tiles",
|
|
22
|
+
"wild-count-wrong": "the bag has the wrong number of wild tiles",
|
|
23
|
+
"bag-exceeds-set": "the bag exceeds the tile set",
|
|
24
|
+
"answer-not-grid": "the answer is not a grid",
|
|
25
|
+
"grid-not-the-bag": "the grid does not use exactly the tiles of the bag",
|
|
26
|
+
"tiles-apart": "the tiles are not all joined",
|
|
27
|
+
"not-a-word": "{word} is not in the word list",
|
|
28
|
+
"grid-not-finished": "the grid is not finished",
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* A refusal: `{ ok: false }`, the English `reason` (the clause of the code with the values in its gaps), the code
|
|
32
|
+
* and the values.
|
|
33
|
+
*
|
|
34
|
+
* @param code - Why the grid is not taken.
|
|
35
|
+
* @param values - What fills the clause's gaps, where it has any.
|
|
36
|
+
* @returns The refusal `checkKumimoji` gives.
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { kumimojiRefusal } from "@johnmorrisdotca/kumimoji";
|
|
40
|
+
*
|
|
41
|
+
* console.log(kumimojiRefusal("not-a-word", { word: "aow" }));
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function kumimojiRefusal(code, values) {
|
|
45
|
+
const reason = KUMIMOJI_CHECK_REASONS[code].replace(/\{(\w+)\}/g, (_gap, name) => String(values?.[name]));
|
|
46
|
+
return values === undefined ? { ok: false, reason, code } : { ok: false, reason, code, values };
|
|
47
|
+
}
|
package/dist/check.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { kumimojiRefusal } from "./check-reasons.js";
|
|
1
2
|
import { decodeGrid, lettersOf, sameLetters } from "./grid.js";
|
|
2
3
|
import { judgeWithWords } from "./judge.js";
|
|
3
4
|
import { KUMIMOJI_SCORE, kumimojiTileCount, kumimojiWildCount, TILE_MIX_TOTAL } from "./tiles.constants.js";
|
|
@@ -43,37 +44,37 @@ export function checkKumimoji(size, givens, answer, options = {}) {
|
|
|
43
44
|
inBag = kumimojiTileCount(size, gameLength, language === "english" ? TILE_MIX_TOTAL : setSize, doubleSet);
|
|
44
45
|
}
|
|
45
46
|
catch {
|
|
46
|
-
return
|
|
47
|
+
return kumimojiRefusal("no-hand", { size });
|
|
47
48
|
}
|
|
48
49
|
const bag = [...givens];
|
|
49
50
|
if (typeof givens !== "string" || bag.length !== inBag)
|
|
50
|
-
return
|
|
51
|
+
return kumimojiRefusal("givens-not-bag");
|
|
51
52
|
const expectedWilds = kumimojiWildCount(size, level, inBag);
|
|
52
53
|
if (bag.filter(words.isWild).length !== expectedWilds)
|
|
53
|
-
return
|
|
54
|
+
return kumimojiRefusal("wild-count-wrong");
|
|
54
55
|
for (const [tile, count] of lettersOf(bag.filter((each) => !words.isWild(each)))) {
|
|
55
56
|
const face = words.inventoryKey(tile);
|
|
56
57
|
const code = face === null ? null : words.codeOf(face);
|
|
57
58
|
if (code === null || count > (words.mix.get(code) ?? 0) * multiplier)
|
|
58
|
-
return
|
|
59
|
+
return kumimojiRefusal("bag-exceeds-set");
|
|
59
60
|
}
|
|
60
61
|
const tiles = decodeGrid(answer);
|
|
61
62
|
if (tiles === null)
|
|
62
|
-
return
|
|
63
|
+
return kumimojiRefusal("answer-not-grid");
|
|
63
64
|
const bagIdentities = bag.map(words.familyKey);
|
|
64
65
|
const tileIdentities = [...tiles.values()].map(words.familyKey);
|
|
65
66
|
if (bagIdentities.some((tile) => tile === null) ||
|
|
66
67
|
tileIdentities.some((tile) => tile === null) ||
|
|
67
68
|
!sameLetters(lettersOf(tileIdentities), lettersOf(bagIdentities))) {
|
|
68
|
-
return
|
|
69
|
+
return kumimojiRefusal("grid-not-the-bag");
|
|
69
70
|
}
|
|
70
71
|
const verdict = judgeWithWords(tiles, words, { diagonals: options.diagonals === true });
|
|
71
72
|
if (verdict.apart.size > 0)
|
|
72
|
-
return
|
|
73
|
+
return kumimojiRefusal("tiles-apart");
|
|
73
74
|
if (verdict.notWords.length > 0)
|
|
74
|
-
return {
|
|
75
|
+
return kumimojiRefusal("not-a-word", { word: verdict.notWords[0] });
|
|
75
76
|
if (!verdict.sound)
|
|
76
|
-
return
|
|
77
|
+
return kumimojiRefusal("grid-not-finished");
|
|
77
78
|
return { ok: true };
|
|
78
79
|
}
|
|
79
80
|
/** A finished game's leaderboard points (`KUMIMOJI_SCORE`): ten a tile, and up to as much again for speed.
|
package/dist/element.d.ts
CHANGED
|
@@ -97,6 +97,9 @@ export declare class KumimojiTable extends ElementBase {
|
|
|
97
97
|
/** The `controls` attribute, reflected. */
|
|
98
98
|
get controls(): string | null;
|
|
99
99
|
set controls(value: string | number | boolean | null | undefined);
|
|
100
|
+
/** The `cloth` attribute, reflected: green, blue, red, black or wood. */
|
|
101
|
+
get cloth(): string | null;
|
|
102
|
+
set cloth(value: string | number | boolean | null | undefined);
|
|
100
103
|
/** The `theme` attribute, reflected. */
|
|
101
104
|
get theme(): string | null;
|
|
102
105
|
set theme(value: string | number | boolean | null | undefined);
|
package/dist/element.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { kumimojiDailySeed } from "./daily.js";
|
|
2
2
|
import { embedDay } from "./embed-day.js";
|
|
3
3
|
import { KUMIMOJI_HANDS } from "./tiles.constants.js";
|
|
4
|
+
import { isCloth } from "./ui/cloth.js";
|
|
4
5
|
import { mountKumimoji } from "./ui/mount.js";
|
|
5
6
|
import { KUMIMOJI_STYLE } from "./ui/style.js";
|
|
6
7
|
const ElementBase = typeof HTMLElement === "undefined" ? class {
|
|
@@ -53,6 +54,7 @@ export const KUMIMOJI_EMBED_OPTION_KEYS = Object.keys({
|
|
|
53
54
|
locale: true,
|
|
54
55
|
strings: true,
|
|
55
56
|
theme: true,
|
|
57
|
+
cloth: true,
|
|
56
58
|
keep: true,
|
|
57
59
|
controls: true,
|
|
58
60
|
saved: true,
|
|
@@ -82,6 +84,7 @@ export class KumimojiTable extends ElementBase {
|
|
|
82
84
|
"size",
|
|
83
85
|
"width",
|
|
84
86
|
"label",
|
|
87
|
+
"cloth",
|
|
85
88
|
];
|
|
86
89
|
#table = null;
|
|
87
90
|
#key = "";
|
|
@@ -205,6 +208,13 @@ export class KumimojiTable extends ElementBase {
|
|
|
205
208
|
set controls(value) {
|
|
206
209
|
this.#write("controls", value);
|
|
207
210
|
}
|
|
211
|
+
/** The `cloth` attribute, reflected: green, blue, red, black or wood. */
|
|
212
|
+
get cloth() {
|
|
213
|
+
return this.getAttribute("cloth");
|
|
214
|
+
}
|
|
215
|
+
set cloth(value) {
|
|
216
|
+
this.#write("cloth", value);
|
|
217
|
+
}
|
|
208
218
|
/** The `theme` attribute, reflected. */
|
|
209
219
|
get theme() {
|
|
210
220
|
return this.getAttribute("theme");
|
|
@@ -319,11 +329,19 @@ export class KumimojiTable extends ElementBase {
|
|
|
319
329
|
? lang
|
|
320
330
|
: (this.#options.locale ??
|
|
321
331
|
(this.ownerDocument.documentElement.lang.toLowerCase().startsWith("ja") ? "ja" : "en"));
|
|
332
|
+
const cloth = isCloth(this.getAttribute("cloth")) ? this.getAttribute("cloth") : this.#options.cloth;
|
|
322
333
|
const keep = isOn(this.getAttribute("keep"), this.#options.keep ?? true);
|
|
323
334
|
const controls = isOn(this.getAttribute("controls"), this.#options.controls ?? true);
|
|
324
335
|
const key = JSON.stringify([language, hand, level, gameLength, diagonals, seed, day, this.#options.saved]);
|
|
325
336
|
if (key === this.#key && this.#table !== null) {
|
|
326
|
-
this.#table.set({
|
|
337
|
+
this.#table.set({
|
|
338
|
+
locale,
|
|
339
|
+
strings: this.#options.strings,
|
|
340
|
+
cloth,
|
|
341
|
+
theme: this.#options.theme ?? {},
|
|
342
|
+
keep,
|
|
343
|
+
controls,
|
|
344
|
+
});
|
|
327
345
|
return;
|
|
328
346
|
}
|
|
329
347
|
if (hand !== undefined && !HANDS.includes(hand))
|
|
@@ -342,6 +360,7 @@ export class KumimojiTable extends ElementBase {
|
|
|
342
360
|
diagonals,
|
|
343
361
|
seed,
|
|
344
362
|
locale,
|
|
363
|
+
cloth,
|
|
345
364
|
keep,
|
|
346
365
|
controls,
|
|
347
366
|
onChange: (saved) => {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/kumimoji.types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { KumimojiCheckCode, KumimojiCheckValues } from "./check-reasons.ts";
|
|
1
2
|
/** How much of the set a game's bag holds: a short game, half the set, or all of it.
|
|
2
3
|
*
|
|
3
4
|
* @example
|
|
@@ -68,13 +69,13 @@ export type KumimojiOptions = {
|
|
|
68
69
|
*/
|
|
69
70
|
diagonals?: boolean;
|
|
70
71
|
};
|
|
71
|
-
/** The answer to "does this grid finish the game?": yes, or no and why.
|
|
72
|
+
/** The answer to "does this grid finish the game?": yes, or no and why, in English (`reason`) and as a stable `code` with `values` a site can say in its own language (`KUMIMOJI_CHECK_REASONS`).
|
|
72
73
|
*
|
|
73
74
|
* @example
|
|
74
75
|
* ```ts
|
|
75
76
|
* import type { KumimojiCheck } from "@johnmorrisdotca/kumimoji";
|
|
76
77
|
*
|
|
77
|
-
* const value: KumimojiCheck = {ok:false,reason:"
|
|
78
|
+
* const value: KumimojiCheck = {ok:false,reason:"the grid is not finished",code:"grid-not-finished"};
|
|
78
79
|
* console.log(value);
|
|
79
80
|
* ```
|
|
80
81
|
*/
|
|
@@ -83,6 +84,8 @@ export type KumimojiCheck = {
|
|
|
83
84
|
} | {
|
|
84
85
|
ok: false;
|
|
85
86
|
reason: string;
|
|
87
|
+
code: KumimojiCheckCode;
|
|
88
|
+
values?: KumimojiCheckValues;
|
|
86
89
|
};
|
|
87
90
|
/**
|
|
88
91
|
* A GAME DEALT (`generateKumimoji`): the bag, in the order its tiles come out,
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE CLOTHS A TABLE MAY BE LAID IN: the same five the whole family offers, and itsutsu.com's boards:
|
|
3
|
+
* green, blue, red, black and wood. Each is the felt's colour, its deep edge and the ink written on it.
|
|
4
|
+
* Given to a table as its `--km-felt` and `--km-accent` variables; `theme` is laid over it.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { KUMIMOJI_CLOTHS } from "@johnmorrisdotca/kumimoji/ui";
|
|
9
|
+
* console.log(Object.keys(KUMIMOJI_CLOTHS));
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export declare const KUMIMOJI_CLOTHS: {
|
|
13
|
+
readonly green: {
|
|
14
|
+
readonly felt: "#2f5d4a";
|
|
15
|
+
readonly deep: "#1f4135";
|
|
16
|
+
readonly ink: "#f3efe4";
|
|
17
|
+
};
|
|
18
|
+
readonly blue: {
|
|
19
|
+
readonly felt: "#2865a6";
|
|
20
|
+
readonly deep: "#1a4677";
|
|
21
|
+
readonly ink: "#f3efe4";
|
|
22
|
+
};
|
|
23
|
+
readonly red: {
|
|
24
|
+
readonly felt: "#a3342e";
|
|
25
|
+
readonly deep: "#7a231f";
|
|
26
|
+
readonly ink: "#f3efe4";
|
|
27
|
+
};
|
|
28
|
+
readonly black: {
|
|
29
|
+
readonly felt: "#2f3236";
|
|
30
|
+
readonly deep: "#1b1d20";
|
|
31
|
+
readonly ink: "#ece8dc";
|
|
32
|
+
};
|
|
33
|
+
readonly wood: {
|
|
34
|
+
readonly felt: "#e2ba7a";
|
|
35
|
+
readonly deep: "#c4954f";
|
|
36
|
+
readonly ink: "#2b1d0e";
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
/** A cloth's name.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* import type { Cloth } from "@johnmorrisdotca/kumimoji/ui";
|
|
44
|
+
* const value: Cloth = "green";
|
|
45
|
+
* console.log(value);
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export type Cloth = keyof typeof KUMIMOJI_CLOTHS;
|
|
49
|
+
/** Whether a text names a cloth.
|
|
50
|
+
*
|
|
51
|
+
* @param text - Text to parse, validate or format.
|
|
52
|
+
* @returns Whether a text names a cloth.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* import { isCloth } from "@johnmorrisdotca/kumimoji/ui";
|
|
57
|
+
* console.log(isCloth("green"), isCloth("unknown"));
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare function isCloth(text: unknown): text is Cloth;
|
|
61
|
+
/** A cloth as CSS variables; nothing for a name that is not a cloth, so the defaults stay.
|
|
62
|
+
*
|
|
63
|
+
* @param cloth - A built-in cloth name; an unknown name leaves the defaults.
|
|
64
|
+
* @returns The CSS variables to set on the element, or an empty object.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { clothVars } from "@johnmorrisdotca/kumimoji/ui";
|
|
69
|
+
* console.log(clothVars("green"));
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function clothVars(cloth: string | undefined | null): Record<`--km-${string}`, string>;
|
package/dist/ui/cloth.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE CLOTHS A TABLE MAY BE LAID IN: the same five the whole family offers, and itsutsu.com's boards:
|
|
3
|
+
* green, blue, red, black and wood. Each is the felt's colour, its deep edge and the ink written on it.
|
|
4
|
+
* Given to a table as its `--km-felt` and `--km-accent` variables; `theme` is laid over it.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { KUMIMOJI_CLOTHS } from "@johnmorrisdotca/kumimoji/ui";
|
|
9
|
+
* console.log(Object.keys(KUMIMOJI_CLOTHS));
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export const KUMIMOJI_CLOTHS = {
|
|
13
|
+
green: { felt: "#2f5d4a", deep: "#1f4135", ink: "#f3efe4" },
|
|
14
|
+
blue: { felt: "#2865a6", deep: "#1a4677", ink: "#f3efe4" },
|
|
15
|
+
red: { felt: "#a3342e", deep: "#7a231f", ink: "#f3efe4" },
|
|
16
|
+
black: { felt: "#2f3236", deep: "#1b1d20", ink: "#ece8dc" },
|
|
17
|
+
wood: { felt: "#e2ba7a", deep: "#c4954f", ink: "#2b1d0e" },
|
|
18
|
+
};
|
|
19
|
+
/** Whether a text names a cloth.
|
|
20
|
+
*
|
|
21
|
+
* @param text - Text to parse, validate or format.
|
|
22
|
+
* @returns Whether a text names a cloth.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { isCloth } from "@johnmorrisdotca/kumimoji/ui";
|
|
27
|
+
* console.log(isCloth("green"), isCloth("unknown"));
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function isCloth(text) {
|
|
31
|
+
return typeof text === "string" && Object.hasOwn(KUMIMOJI_CLOTHS, text);
|
|
32
|
+
}
|
|
33
|
+
/** A cloth as CSS variables; nothing for a name that is not a cloth, so the defaults stay.
|
|
34
|
+
*
|
|
35
|
+
* @param cloth - A built-in cloth name; an unknown name leaves the defaults.
|
|
36
|
+
* @returns The CSS variables to set on the element, or an empty object.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { clothVars } from "@johnmorrisdotca/kumimoji/ui";
|
|
41
|
+
* console.log(clothVars("green"));
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function clothVars(cloth) {
|
|
45
|
+
if (!isCloth(cloth))
|
|
46
|
+
return {};
|
|
47
|
+
const { felt, ink } = KUMIMOJI_CLOTHS[cloth];
|
|
48
|
+
return { "--km-felt": felt, "--km-accent": felt, "--km-accent-ink": ink };
|
|
49
|
+
}
|