@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.
- package/CHANGELOG.md +33 -1
- package/CONTRIBUTING.md +11 -2
- package/README.md +80 -39
- package/dist/check.d.ts +26 -1
- package/dist/check.js +30 -3
- package/dist/computer.types.d.ts +24 -2
- package/dist/computerPlay.d.ts +45 -2
- package/dist/computerPlay.js +56 -6
- package/dist/computerTurn.d.ts +42 -3
- package/dist/computerTurn.js +47 -5
- package/dist/daily.d.ts +22 -2
- package/dist/daily.js +23 -3
- package/dist/element-define.d.ts +1 -1
- package/dist/element-define.js +3 -13
- package/dist/element.d.ts +85 -4
- package/dist/element.js +359 -77
- package/dist/embed-day.d.ts +4 -0
- package/dist/embed-day.js +23 -0
- package/dist/export.d.ts +123 -6
- package/dist/export.js +109 -9
- package/dist/generate.d.ts +14 -0
- package/dist/generate.js +32 -5
- package/dist/grid.d.ts +181 -10
- package/dist/grid.js +148 -11
- package/dist/help.d.ts +36 -3
- package/dist/help.js +36 -3
- package/dist/index.d.ts +22 -22
- package/dist/index.js +22 -22
- package/dist/judge.d.ts +14 -0
- package/dist/judge.js +14 -0
- package/dist/kana.d.ts +23 -1
- package/dist/kana.js +62 -7
- package/dist/kumimoji.types.d.ts +70 -5
- package/dist/party.d.ts +268 -14
- package/dist/party.js +296 -19
- package/dist/party.types.d.ts +50 -2
- package/dist/partyKept.d.ts +74 -4
- package/dist/partyKept.js +127 -12
- package/dist/partySeats.d.ts +92 -6
- package/dist/partySeats.js +83 -8
- package/dist/partyTurns.d.ts +171 -5
- package/dist/partyTurns.js +187 -8
- package/dist/placement.d.ts +50 -2
- package/dist/placement.js +35 -2
- package/dist/play.d.ts +247 -15
- package/dist/play.js +251 -18
- package/dist/random.d.ts +34 -3
- package/dist/random.js +23 -2
- package/dist/react.d.ts +42 -4
- package/dist/react.js +54 -7
- package/dist/showcase.d.ts +83 -7
- package/dist/showcase.js +61 -7
- package/dist/strings.d.ts +54 -5
- package/dist/strings.js +34 -3
- package/dist/tableView.d.ts +151 -8
- package/dist/tableView.js +129 -6
- package/dist/tileFace.d.ts +59 -5
- package/dist/tileFace.js +48 -4
- package/dist/tileFamily.d.ts +9 -0
- package/dist/tileFamily.js +9 -0
- package/dist/tileWords.d.ts +93 -6
- package/dist/tileWords.js +94 -12
- package/dist/tileWordsModule.d.ts +12 -2
- package/dist/tileWordsModule.js +14 -2
- package/dist/tiles.constants.d.ts +99 -5
- package/dist/tiles.constants.js +171 -13
- package/dist/turn.d.ts +82 -4
- package/dist/turn.js +88 -5
- package/dist/ui/board.d.ts +60 -5
- package/dist/ui/board.js +44 -4
- package/dist/ui/draw.d.ts +62 -0
- package/dist/ui/draw.js +229 -0
- package/dist/ui/mount.d.ts +41 -2
- package/dist/ui/mount.js +190 -265
- package/dist/ui/style.d.ts +6 -1
- package/dist/ui/style.js +6 -0
- package/dist/ui.d.ts +3 -3
- package/dist/ui.js +2 -2
- package/dist/version.d.ts +9 -2
- package/dist/version.js +9 -2
- package/dist/wilds.d.ts +22 -1
- package/dist/wilds.js +25 -3
- package/package.json +68 -61
package/dist/partyTurns.d.ts
CHANGED
|
@@ -7,17 +7,72 @@ import type { TileWords } from "./tileWords.ts";
|
|
|
7
7
|
* solo game's (`play.ts`), and the deal and the draw are `party.ts`'s. Pure:
|
|
8
8
|
* each returns a new game, or the one it was given where the move is refused.
|
|
9
9
|
*/
|
|
10
|
-
/** Whether the game is over.
|
|
10
|
+
/** Whether the game is over.
|
|
11
|
+
*
|
|
12
|
+
* @param game - The current immutable shared-bag party game.
|
|
13
|
+
* @returns Whether the game is over.
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { isOver, startParty } from "@johnmorrisdotca/kumimoji";
|
|
17
|
+
*
|
|
18
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
19
|
+
* const bag = "catdogsun";
|
|
20
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
21
|
+
* console.log(isOver(game));
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
11
24
|
export declare function isOver(game: PartyGame): boolean;
|
|
12
25
|
/**
|
|
13
26
|
* Whether the player whose turn it is would go out with Done: their hand
|
|
14
27
|
* used, their grid sound, and too few tiles in the bag for everybody still in
|
|
15
28
|
* to draw.
|
|
29
|
+
*
|
|
30
|
+
* @param game - The current immutable shared-bag party game.
|
|
31
|
+
* @param verdict - The native word and connectivity verdict for this grid.
|
|
32
|
+
* @returns Whether the player whose turn it is would go out with Done: their hand used, their grid sound, and too few tiles in the bag for everybody still in to draw.
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
36
|
+
* import { goesOut, judgeWithWords, startParty } from "@johnmorrisdotca/kumimoji";
|
|
37
|
+
*
|
|
38
|
+
* const words = await loadTileWordsFromModule("english");
|
|
39
|
+
* const verdict = judgeWithWords(new Map([["0,0","c"],["0,1","a"],["0,2","t"]]),words);
|
|
40
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
41
|
+
* const bag = "catdogsun";
|
|
42
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
43
|
+
* console.log(goesOut(game,verdict));
|
|
44
|
+
* ```
|
|
16
45
|
*/
|
|
17
46
|
export declare function goesOut(game: PartyGame, verdict: GridVerdict): boolean;
|
|
18
|
-
/** Whether the player whose turn it is has laid a tile on their table this turn: more tiles there than when it began.
|
|
47
|
+
/** Whether the player whose turn it is has laid a tile on their table this turn: more tiles there than when it began.
|
|
48
|
+
*
|
|
49
|
+
* @param game - The current immutable shared-bag party game.
|
|
50
|
+
* @returns Whether the player whose turn it is has laid a tile on their table this turn: more tiles there than when it began.
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* import { laidThisTurn, startParty } from "@johnmorrisdotca/kumimoji";
|
|
54
|
+
*
|
|
55
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
56
|
+
* const bag = "catdogsun";
|
|
57
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
58
|
+
* console.log(laidThisTurn(game));
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
19
61
|
export declare function laidThisTurn(game: PartyGame): boolean;
|
|
20
|
-
/** Whether the player whose turn it is is the only one still in: everybody else has resigned.
|
|
62
|
+
/** Whether the player whose turn it is is the only one still in: everybody else has resigned.
|
|
63
|
+
*
|
|
64
|
+
* @param game - The current immutable shared-bag party game.
|
|
65
|
+
* @returns Whether the player whose turn it is is the only one still in: everybody else has resigned.
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { lastStanding, startParty } from "@johnmorrisdotca/kumimoji";
|
|
69
|
+
*
|
|
70
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
71
|
+
* const bag = "catdogsun";
|
|
72
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
73
|
+
* console.log(lastStanding(game));
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
21
76
|
export declare function lastStanding(game: PartyGame): boolean;
|
|
22
77
|
/**
|
|
23
78
|
* A HAND THAT SPELLS NOTHING MUST BE TRADED. John, 2026-09-28: "If no words
|
|
@@ -26,14 +81,59 @@ export declare function lastStanding(game: PartyGame): boolean;
|
|
|
26
81
|
* spell no word, has laid nothing on (or lifted nothing from) their table this
|
|
27
82
|
* turn, and has not traded — unless the bag cannot give three, when there is
|
|
28
83
|
* nothing to trade for. `handSpells` answers for a hand (`handCanSpell`).
|
|
84
|
+
*
|
|
85
|
+
* @param game - The current immutable shared-bag party game.
|
|
86
|
+
* @param handSpells - Whether a hand can form any accepted word.
|
|
87
|
+
* @returns A HAND THAT SPELLS NOTHING MUST BE TRADED. John, 2026-09-28: "If no words can be formed which is possible, they must dump a tile to get 3 more. Easy rule." So Done waits while the player whose turn it is holds tiles that spell no word, has laid nothing on (or lifted nothing from) their table this turn, and has not traded — unless the bag cannot give three, when there is nothing to trade for. `handSpells` answers for a hand (`handCanSpell`).
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
91
|
+
* import { handCanSpell, mustTradeFirst, startParty } from "@johnmorrisdotca/kumimoji";
|
|
92
|
+
*
|
|
93
|
+
* const words = await loadTileWordsFromModule("english");
|
|
94
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
95
|
+
* const bag = "catdogsun";
|
|
96
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
97
|
+
* console.log(mustTradeFirst(game,hand=>handCanSpell(hand,words)));
|
|
98
|
+
* ```
|
|
29
99
|
*/
|
|
30
100
|
export declare function mustTradeFirst(game: PartyGame, handSpells: (hand: readonly string[]) => boolean): boolean;
|
|
31
|
-
/** Whether a hand can spell a word from the list: any word of two tiles or more, a wild counting as able to be anything.
|
|
101
|
+
/** Whether a hand can spell a word from the list: any word of two tiles or more, a wild counting as able to be anything.
|
|
102
|
+
*
|
|
103
|
+
* @param hand - The hand size or tile codes, as declared by the function signature.
|
|
104
|
+
* @param words - The loaded native language word list and tile mappings.
|
|
105
|
+
* @returns Whether a hand can spell a word from the list: any word of two tiles or more, a wild counting as able to be anything.
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
109
|
+
* import { handCanSpell } from "@johnmorrisdotca/kumimoji";
|
|
110
|
+
*
|
|
111
|
+
* const words = await loadTileWordsFromModule("english");
|
|
112
|
+
* console.log(handCanSpell(["c","a","t"],words));
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
32
115
|
export declare function handCanSpell(hand: readonly string[], words: TileWords): boolean;
|
|
33
116
|
/**
|
|
34
117
|
* Why Done cannot be pressed now, or null when it can: a hand to trade first,
|
|
35
118
|
* or, for the last one standing, no tile laid on a sound grid yet (a hand
|
|
36
119
|
* already used on a sound grid has nothing left to lay, and wins as it is).
|
|
120
|
+
*
|
|
121
|
+
* @param game - The current immutable shared-bag party game.
|
|
122
|
+
* @param verdict - The native word and connectivity verdict for this grid.
|
|
123
|
+
* @param handSpells - Whether a hand can form any accepted word.
|
|
124
|
+
* @returns Why Done cannot be pressed now, or null when it can: a hand to trade first, or, for the last one standing, no tile laid on a sound grid yet (a hand already used on a sound grid has nothing left to lay, and wins as it is).
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
128
|
+
* import { doneRefused, handCanSpell, judgeWithWords, seatPlay, startParty } from "@johnmorrisdotca/kumimoji";
|
|
129
|
+
*
|
|
130
|
+
* const words = await loadTileWordsFromModule("english");
|
|
131
|
+
* const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
|
|
132
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
133
|
+
* const bag = "catdogsun";
|
|
134
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
135
|
+
* console.log(doneRefused(game,judgeWithWords(seatPlay(game).tiles,words),hand=>handCanSpell(hand,words)));
|
|
136
|
+
* ```
|
|
37
137
|
*/
|
|
38
138
|
export declare function doneRefused(game: PartyGame, verdict: GridVerdict, handSpells: (hand: readonly string[]) => boolean): "trade" | "standing" | null;
|
|
39
139
|
/**
|
|
@@ -46,6 +146,23 @@ export declare function doneRefused(game: PartyGame, verdict: GridVerdict, handS
|
|
|
46
146
|
* The last one standing, everybody else resigned, wins by laying a tile on a
|
|
47
147
|
* sound grid and pressing Done. `verdict` is the grid of the player whose turn
|
|
48
148
|
* it is. Refused, the game given back as it was, while `doneRefused` says so.
|
|
149
|
+
*
|
|
150
|
+
* @param game - The current immutable shared-bag party game.
|
|
151
|
+
* @param verdict - The native word and connectivity verdict for this grid.
|
|
152
|
+
* @param handSpells - Whether a hand can form any accepted word.
|
|
153
|
+
* @returns DONE: the end of a turn, and the device passed on. Before anybody is out, to the next player still in. A player who goes out with it starts the last round: everybody else still in, in order from the next, has one last turn, and any of them who goes out on it shares the win (John, 2026-09-28: "Sharing the win is correct"). After the last of those, the game is over.
|
|
154
|
+
* @example
|
|
155
|
+
* ```ts
|
|
156
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
157
|
+
* import { endTurn, handCanSpell, judgeWithWords, seatPlay, startParty } from "@johnmorrisdotca/kumimoji";
|
|
158
|
+
*
|
|
159
|
+
* const words = await loadTileWordsFromModule("english");
|
|
160
|
+
* const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
|
|
161
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
162
|
+
* const bag = "catdogsun";
|
|
163
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
164
|
+
* console.log(endTurn(game,judgeWithWords(seatPlay(game).tiles,words),hand=>handCanSpell(hand,words)).turn);
|
|
165
|
+
* ```
|
|
49
166
|
*/
|
|
50
167
|
export declare function endTurn(game: PartyGame, verdict: GridVerdict, handSpells: (hand: readonly string[]) => boolean): PartyGame;
|
|
51
168
|
/**
|
|
@@ -53,6 +170,18 @@ export declare function endTurn(game: PartyGame, verdict: GridVerdict, handSpell
|
|
|
53
170
|
* give a trade, so nothing more can be got from it. John, 2026-09-28:
|
|
54
171
|
* "Perhaps we offer a resign button only when there are no remaining tiles
|
|
55
172
|
* they can get from the bag."
|
|
173
|
+
*
|
|
174
|
+
* @param game - The current immutable shared-bag party game.
|
|
175
|
+
* @returns Whether the player whose turn it is may resign: only once the bag cannot give a trade, so nothing more can be got from it. John, 2026-09-28: "Perhaps we offer a resign button only when there are no remaining tiles they can get from the bag."
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* import { mayResign, startParty } from "@johnmorrisdotca/kumimoji";
|
|
179
|
+
*
|
|
180
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
181
|
+
* const bag = "catdogsun";
|
|
182
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
183
|
+
* console.log(mayResign(game));
|
|
184
|
+
* ```
|
|
56
185
|
*/
|
|
57
186
|
export declare function mayResign(game: PartyGame): boolean;
|
|
58
187
|
/**
|
|
@@ -63,13 +192,50 @@ export declare function mayResign(game: PartyGame): boolean;
|
|
|
63
192
|
* resigned too — it ends tied between everybody who resigned since a player
|
|
64
193
|
* still in last pressed Done. John: "if you resign, as the turns pass, if
|
|
65
194
|
* someone else can't go and has to resign then they are tied."
|
|
195
|
+
*
|
|
196
|
+
* @param game - The current immutable shared-bag party game.
|
|
197
|
+
* @returns RESIGN: the player whose turn it is is out of the game, their table kept for the finish, and their turn passed over from then on. In the last round it is their last turn ended without going out. Otherwise the game goes on with whoever is still in; and when nobody is — the last one standing resigned too — it ends tied between everybody who resigned since a player still in last pressed Done. John: "if you resign, as the turns pass, if someone else can't go and has to resign then they are tied."
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* import { resign, startParty } from "@johnmorrisdotca/kumimoji";
|
|
201
|
+
*
|
|
202
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
203
|
+
* const bag = "catdogsun";
|
|
204
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
205
|
+
* console.log(resign(game));
|
|
206
|
+
* ```
|
|
66
207
|
*/
|
|
67
208
|
export declare function resign(game: PartyGame): PartyGame;
|
|
68
209
|
/**
|
|
69
210
|
* The winners of a finished game, by place: everybody who went out; the last
|
|
70
211
|
* one standing; or, tied, the final run of resignations. Empty while it is
|
|
71
212
|
* played.
|
|
213
|
+
*
|
|
214
|
+
* @param game - The current immutable shared-bag party game.
|
|
215
|
+
* @returns The winners of a finished game, by place: everybody who went out; the last one standing; or, tied, the final run of resignations. Empty while it is played.
|
|
216
|
+
* @example
|
|
217
|
+
* ```ts
|
|
218
|
+
* import { startParty, winnersOf } from "@johnmorrisdotca/kumimoji";
|
|
219
|
+
*
|
|
220
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
221
|
+
* const bag = "catdogsun";
|
|
222
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
223
|
+
* console.log(winnersOf(game));
|
|
224
|
+
* ```
|
|
72
225
|
*/
|
|
73
226
|
export declare function winnersOf(game: PartyGame): readonly number[];
|
|
74
|
-
/** Whether the turn now being played is a player's last.
|
|
227
|
+
/** Whether the turn now being played is a player's last.
|
|
228
|
+
*
|
|
229
|
+
* @param game - The current immutable shared-bag party game.
|
|
230
|
+
* @returns Whether the turn now being played is a player's last.
|
|
231
|
+
* @example
|
|
232
|
+
* ```ts
|
|
233
|
+
* import { isLastTurn, startParty } from "@johnmorrisdotca/kumimoji";
|
|
234
|
+
*
|
|
235
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
236
|
+
* const bag = "catdogsun";
|
|
237
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
238
|
+
* console.log(isLastTurn(game));
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
75
241
|
export declare function isLastTurn(game: PartyGame): boolean;
|
package/dist/partyTurns.js
CHANGED
|
@@ -8,7 +8,20 @@ import { KUMIMOJI_TRADE } from "./tiles.constants.js";
|
|
|
8
8
|
* solo game's (`play.ts`), and the deal and the draw are `party.ts`'s. Pure:
|
|
9
9
|
* each returns a new game, or the one it was given where the move is refused.
|
|
10
10
|
*/
|
|
11
|
-
/** Whether the game is over.
|
|
11
|
+
/** Whether the game is over.
|
|
12
|
+
*
|
|
13
|
+
* @param game - The current immutable shared-bag party game.
|
|
14
|
+
* @returns Whether the game is over.
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import { isOver, startParty } from "@johnmorrisdotca/kumimoji";
|
|
18
|
+
*
|
|
19
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
20
|
+
* const bag = "catdogsun";
|
|
21
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
22
|
+
* console.log(isOver(game));
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
12
25
|
export function isOver(game) {
|
|
13
26
|
return game.ending !== null;
|
|
14
27
|
}
|
|
@@ -16,15 +29,60 @@ export function isOver(game) {
|
|
|
16
29
|
* Whether the player whose turn it is would go out with Done: their hand
|
|
17
30
|
* used, their grid sound, and too few tiles in the bag for everybody still in
|
|
18
31
|
* to draw.
|
|
32
|
+
*
|
|
33
|
+
* @param game - The current immutable shared-bag party game.
|
|
34
|
+
* @param verdict - The native word and connectivity verdict for this grid.
|
|
35
|
+
* @returns Whether the player whose turn it is would go out with Done: their hand used, their grid sound, and too few tiles in the bag for everybody still in to draw.
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
39
|
+
* import { goesOut, judgeWithWords, startParty } from "@johnmorrisdotca/kumimoji";
|
|
40
|
+
*
|
|
41
|
+
* const words = await loadTileWordsFromModule("english");
|
|
42
|
+
* const verdict = judgeWithWords(new Map([["0,0","c"],["0,1","a"],["0,2","t"]]),words);
|
|
43
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
44
|
+
* const bag = "catdogsun";
|
|
45
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
46
|
+
* console.log(goesOut(game,verdict));
|
|
47
|
+
* ```
|
|
19
48
|
*/
|
|
20
49
|
export function goesOut(game, verdict) {
|
|
21
|
-
return !isOver(game) &&
|
|
50
|
+
return (!isOver(game) &&
|
|
51
|
+
game.players[game.turn].hand.length === 0 &&
|
|
52
|
+
verdict.sound &&
|
|
53
|
+
partyTilesLeft(game) < stillIn(game).length);
|
|
22
54
|
}
|
|
23
|
-
/** Whether the player whose turn it is has laid a tile on their table this turn: more tiles there than when it began.
|
|
55
|
+
/** Whether the player whose turn it is has laid a tile on their table this turn: more tiles there than when it began.
|
|
56
|
+
*
|
|
57
|
+
* @param game - The current immutable shared-bag party game.
|
|
58
|
+
* @returns Whether the player whose turn it is has laid a tile on their table this turn: more tiles there than when it began.
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* import { laidThisTurn, startParty } from "@johnmorrisdotca/kumimoji";
|
|
62
|
+
*
|
|
63
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
64
|
+
* const bag = "catdogsun";
|
|
65
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
66
|
+
* console.log(laidThisTurn(game));
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
24
69
|
export function laidThisTurn(game) {
|
|
25
70
|
return game.players[game.turn].tiles.size > [...game.startTable].length;
|
|
26
71
|
}
|
|
27
|
-
/** Whether the player whose turn it is is the only one still in: everybody else has resigned.
|
|
72
|
+
/** Whether the player whose turn it is is the only one still in: everybody else has resigned.
|
|
73
|
+
*
|
|
74
|
+
* @param game - The current immutable shared-bag party game.
|
|
75
|
+
* @returns Whether the player whose turn it is is the only one still in: everybody else has resigned.
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* import { lastStanding, startParty } from "@johnmorrisdotca/kumimoji";
|
|
79
|
+
*
|
|
80
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
81
|
+
* const bag = "catdogsun";
|
|
82
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
83
|
+
* console.log(lastStanding(game));
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
28
86
|
export function lastStanding(game) {
|
|
29
87
|
return !isOver(game) && game.lastTurns === null && stillIn(game).length === 1;
|
|
30
88
|
}
|
|
@@ -35,6 +93,21 @@ export function lastStanding(game) {
|
|
|
35
93
|
* spell no word, has laid nothing on (or lifted nothing from) their table this
|
|
36
94
|
* turn, and has not traded — unless the bag cannot give three, when there is
|
|
37
95
|
* nothing to trade for. `handSpells` answers for a hand (`handCanSpell`).
|
|
96
|
+
*
|
|
97
|
+
* @param game - The current immutable shared-bag party game.
|
|
98
|
+
* @param handSpells - Whether a hand can form any accepted word.
|
|
99
|
+
* @returns A HAND THAT SPELLS NOTHING MUST BE TRADED. John, 2026-09-28: "If no words can be formed which is possible, they must dump a tile to get 3 more. Easy rule." So Done waits while the player whose turn it is holds tiles that spell no word, has laid nothing on (or lifted nothing from) their table this turn, and has not traded — unless the bag cannot give three, when there is nothing to trade for. `handSpells` answers for a hand (`handCanSpell`).
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
103
|
+
* import { handCanSpell, mustTradeFirst, startParty } from "@johnmorrisdotca/kumimoji";
|
|
104
|
+
*
|
|
105
|
+
* const words = await loadTileWordsFromModule("english");
|
|
106
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
107
|
+
* const bag = "catdogsun";
|
|
108
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
109
|
+
* console.log(mustTradeFirst(game,hand=>handCanSpell(hand,words)));
|
|
110
|
+
* ```
|
|
38
111
|
*/
|
|
39
112
|
export function mustTradeFirst(game, handSpells) {
|
|
40
113
|
if (isOver(game) || game.traded)
|
|
@@ -44,7 +117,20 @@ export function mustTradeFirst(game, handSpells) {
|
|
|
44
117
|
return false;
|
|
45
118
|
return !handSpells(play.hand);
|
|
46
119
|
}
|
|
47
|
-
/** Whether a hand can spell a word from the list: any word of two tiles or more, a wild counting as able to be anything.
|
|
120
|
+
/** Whether a hand can spell a word from the list: any word of two tiles or more, a wild counting as able to be anything.
|
|
121
|
+
*
|
|
122
|
+
* @param hand - The hand size or tile codes, as declared by the function signature.
|
|
123
|
+
* @param words - The loaded native language word list and tile mappings.
|
|
124
|
+
* @returns Whether a hand can spell a word from the list: any word of two tiles or more, a wild counting as able to be anything.
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
128
|
+
* import { handCanSpell } from "@johnmorrisdotca/kumimoji";
|
|
129
|
+
*
|
|
130
|
+
* const words = await loadTileWordsFromModule("english");
|
|
131
|
+
* console.log(handCanSpell(["c","a","t"],words));
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
48
134
|
export function handCanSpell(hand, words) {
|
|
49
135
|
return hand.some(words.isWild) || wordsInHand(hand, words).length > 0;
|
|
50
136
|
}
|
|
@@ -52,6 +138,23 @@ export function handCanSpell(hand, words) {
|
|
|
52
138
|
* Why Done cannot be pressed now, or null when it can: a hand to trade first,
|
|
53
139
|
* or, for the last one standing, no tile laid on a sound grid yet (a hand
|
|
54
140
|
* already used on a sound grid has nothing left to lay, and wins as it is).
|
|
141
|
+
*
|
|
142
|
+
* @param game - The current immutable shared-bag party game.
|
|
143
|
+
* @param verdict - The native word and connectivity verdict for this grid.
|
|
144
|
+
* @param handSpells - Whether a hand can form any accepted word.
|
|
145
|
+
* @returns Why Done cannot be pressed now, or null when it can: a hand to trade first, or, for the last one standing, no tile laid on a sound grid yet (a hand already used on a sound grid has nothing left to lay, and wins as it is).
|
|
146
|
+
* @example
|
|
147
|
+
* ```ts
|
|
148
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
149
|
+
* import { doneRefused, handCanSpell, judgeWithWords, seatPlay, startParty } from "@johnmorrisdotca/kumimoji";
|
|
150
|
+
*
|
|
151
|
+
* const words = await loadTileWordsFromModule("english");
|
|
152
|
+
* const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
|
|
153
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
154
|
+
* const bag = "catdogsun";
|
|
155
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
156
|
+
* console.log(doneRefused(game,judgeWithWords(seatPlay(game).tiles,words),hand=>handCanSpell(hand,words)));
|
|
157
|
+
* ```
|
|
55
158
|
*/
|
|
56
159
|
export function doneRefused(game, verdict, handSpells) {
|
|
57
160
|
if (mustTradeFirst(game, handSpells))
|
|
@@ -63,7 +166,15 @@ export function doneRefused(game, verdict, handSpells) {
|
|
|
63
166
|
}
|
|
64
167
|
/** The game passed to `turn`, a new turn with nothing traded and its table as it stands. */
|
|
65
168
|
function passTo(game, turn, rest) {
|
|
66
|
-
return {
|
|
169
|
+
return {
|
|
170
|
+
...game,
|
|
171
|
+
...rest,
|
|
172
|
+
turns: game.turns + 1,
|
|
173
|
+
turn,
|
|
174
|
+
traded: false,
|
|
175
|
+
startTable: tableTally(game.players[turn].tiles),
|
|
176
|
+
passedBy: game.turn,
|
|
177
|
+
};
|
|
67
178
|
}
|
|
68
179
|
/**
|
|
69
180
|
* DONE: the end of a turn, and the device passed on. Before anybody is out,
|
|
@@ -75,6 +186,23 @@ function passTo(game, turn, rest) {
|
|
|
75
186
|
* The last one standing, everybody else resigned, wins by laying a tile on a
|
|
76
187
|
* sound grid and pressing Done. `verdict` is the grid of the player whose turn
|
|
77
188
|
* it is. Refused, the game given back as it was, while `doneRefused` says so.
|
|
189
|
+
*
|
|
190
|
+
* @param game - The current immutable shared-bag party game.
|
|
191
|
+
* @param verdict - The native word and connectivity verdict for this grid.
|
|
192
|
+
* @param handSpells - Whether a hand can form any accepted word.
|
|
193
|
+
* @returns DONE: the end of a turn, and the device passed on. Before anybody is out, to the next player still in. A player who goes out with it starts the last round: everybody else still in, in order from the next, has one last turn, and any of them who goes out on it shares the win (John, 2026-09-28: "Sharing the win is correct"). After the last of those, the game is over.
|
|
194
|
+
* @example
|
|
195
|
+
* ```ts
|
|
196
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
197
|
+
* import { endTurn, handCanSpell, judgeWithWords, seatPlay, startParty } from "@johnmorrisdotca/kumimoji";
|
|
198
|
+
*
|
|
199
|
+
* const words = await loadTileWordsFromModule("english");
|
|
200
|
+
* const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
|
|
201
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
202
|
+
* const bag = "catdogsun";
|
|
203
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
204
|
+
* console.log(endTurn(game,judgeWithWords(seatPlay(game).tiles,words),hand=>handCanSpell(hand,words)).turn);
|
|
205
|
+
* ```
|
|
78
206
|
*/
|
|
79
207
|
export function endTurn(game, verdict, handSpells) {
|
|
80
208
|
if (isOver(game) || doneRefused(game, verdict, handSpells) !== null)
|
|
@@ -85,7 +213,9 @@ export function endTurn(game, verdict, handSpells) {
|
|
|
85
213
|
if (game.lastTurns === null) {
|
|
86
214
|
if (out.length === 0)
|
|
87
215
|
return passTo(game, nextIn(game, game.turn), { resignRun: [] });
|
|
88
|
-
const lastTurns = stillIn(game)
|
|
216
|
+
const lastTurns = stillIn(game)
|
|
217
|
+
.filter((at) => at !== game.turn)
|
|
218
|
+
.sort((a, b) => fromTurn(game, a) - fromTurn(game, b));
|
|
89
219
|
if (lastTurns.length === 0)
|
|
90
220
|
return passTo(game, game.turn, { out, lastTurns, ending: "out" });
|
|
91
221
|
return passTo(game, lastTurns[0], { out, lastTurns, resignRun: [] });
|
|
@@ -108,6 +238,18 @@ function nextLastTurn(game, out, rest = {}) {
|
|
|
108
238
|
* give a trade, so nothing more can be got from it. John, 2026-09-28:
|
|
109
239
|
* "Perhaps we offer a resign button only when there are no remaining tiles
|
|
110
240
|
* they can get from the bag."
|
|
241
|
+
*
|
|
242
|
+
* @param game - The current immutable shared-bag party game.
|
|
243
|
+
* @returns Whether the player whose turn it is may resign: only once the bag cannot give a trade, so nothing more can be got from it. John, 2026-09-28: "Perhaps we offer a resign button only when there are no remaining tiles they can get from the bag."
|
|
244
|
+
* @example
|
|
245
|
+
* ```ts
|
|
246
|
+
* import { mayResign, startParty } from "@johnmorrisdotca/kumimoji";
|
|
247
|
+
*
|
|
248
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
249
|
+
* const bag = "catdogsun";
|
|
250
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
251
|
+
* console.log(mayResign(game));
|
|
252
|
+
* ```
|
|
111
253
|
*/
|
|
112
254
|
export function mayResign(game) {
|
|
113
255
|
return !isOver(game) && partyTilesLeft(game) < KUMIMOJI_TRADE.take;
|
|
@@ -120,6 +262,18 @@ export function mayResign(game) {
|
|
|
120
262
|
* resigned too — it ends tied between everybody who resigned since a player
|
|
121
263
|
* still in last pressed Done. John: "if you resign, as the turns pass, if
|
|
122
264
|
* someone else can't go and has to resign then they are tied."
|
|
265
|
+
*
|
|
266
|
+
* @param game - The current immutable shared-bag party game.
|
|
267
|
+
* @returns RESIGN: the player whose turn it is is out of the game, their table kept for the finish, and their turn passed over from then on. In the last round it is their last turn ended without going out. Otherwise the game goes on with whoever is still in; and when nobody is — the last one standing resigned too — it ends tied between everybody who resigned since a player still in last pressed Done. John: "if you resign, as the turns pass, if someone else can't go and has to resign then they are tied."
|
|
268
|
+
* @example
|
|
269
|
+
* ```ts
|
|
270
|
+
* import { resign, startParty } from "@johnmorrisdotca/kumimoji";
|
|
271
|
+
*
|
|
272
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
273
|
+
* const bag = "catdogsun";
|
|
274
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
275
|
+
* console.log(resign(game));
|
|
276
|
+
* ```
|
|
123
277
|
*/
|
|
124
278
|
export function resign(game) {
|
|
125
279
|
if (!mayResign(game))
|
|
@@ -137,6 +291,18 @@ export function resign(game) {
|
|
|
137
291
|
* The winners of a finished game, by place: everybody who went out; the last
|
|
138
292
|
* one standing; or, tied, the final run of resignations. Empty while it is
|
|
139
293
|
* played.
|
|
294
|
+
*
|
|
295
|
+
* @param game - The current immutable shared-bag party game.
|
|
296
|
+
* @returns The winners of a finished game, by place: everybody who went out; the last one standing; or, tied, the final run of resignations. Empty while it is played.
|
|
297
|
+
* @example
|
|
298
|
+
* ```ts
|
|
299
|
+
* import { startParty, winnersOf } from "@johnmorrisdotca/kumimoji";
|
|
300
|
+
*
|
|
301
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
302
|
+
* const bag = "catdogsun";
|
|
303
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
304
|
+
* console.log(winnersOf(game));
|
|
305
|
+
* ```
|
|
140
306
|
*/
|
|
141
307
|
export function winnersOf(game) {
|
|
142
308
|
if (game.ending === "out")
|
|
@@ -147,7 +313,20 @@ export function winnersOf(game) {
|
|
|
147
313
|
return game.resignRun;
|
|
148
314
|
return [];
|
|
149
315
|
}
|
|
150
|
-
/** Whether the turn now being played is a player's last.
|
|
316
|
+
/** Whether the turn now being played is a player's last.
|
|
317
|
+
*
|
|
318
|
+
* @param game - The current immutable shared-bag party game.
|
|
319
|
+
* @returns Whether the turn now being played is a player's last.
|
|
320
|
+
* @example
|
|
321
|
+
* ```ts
|
|
322
|
+
* import { isLastTurn, startParty } from "@johnmorrisdotca/kumimoji";
|
|
323
|
+
*
|
|
324
|
+
* const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
|
|
325
|
+
* const bag = "catdogsun";
|
|
326
|
+
* const game = startParty(settings,bag,["Seat 1","Seat 2"]);
|
|
327
|
+
* console.log(isLastTurn(game));
|
|
328
|
+
* ```
|
|
329
|
+
*/
|
|
151
330
|
export function isLastTurn(game) {
|
|
152
331
|
return !isOver(game) && game.lastTurns !== null;
|
|
153
332
|
}
|
package/dist/placement.d.ts
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
|
-
/** A square by its row and column.
|
|
1
|
+
/** A square by its row and column.
|
|
2
|
+
*
|
|
3
|
+
* @example
|
|
4
|
+
* ```ts
|
|
5
|
+
* import type { Square } from "@johnmorrisdotca/kumimoji";
|
|
6
|
+
*
|
|
7
|
+
* const value: Square = {row:0,col:0};
|
|
8
|
+
* console.log(value);
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
2
11
|
export type Square = {
|
|
3
12
|
row: number;
|
|
4
13
|
col: number;
|
|
5
14
|
};
|
|
6
|
-
/** A word's place: its first square, and the squares it lays a new tile on, in the word's order.
|
|
15
|
+
/** A word's place: its first square, and the squares it lays a new tile on, in the word's order.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* import type { CrossingFit } from "@johnmorrisdotca/kumimoji";
|
|
20
|
+
*
|
|
21
|
+
* const value: CrossingFit = {first:{row:0,col:0},fresh:[{row:0,col:1},{row:0,col:2}]};
|
|
22
|
+
* console.log(value);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
7
25
|
export type CrossingFit = {
|
|
8
26
|
first: Square;
|
|
9
27
|
fresh: Square[];
|
|
@@ -15,6 +33,21 @@ export type CrossingFit = {
|
|
|
15
33
|
* neighbour at its side, or no new tile at all — and, given `diagonalWord`
|
|
16
34
|
* (a game with Diagonals), a new tile standing in a diagonal run of three or
|
|
17
35
|
* more that `diagonalWord` does not take (`diagonalsRead`).
|
|
36
|
+
*
|
|
37
|
+
* @param letterAt - The tile at a coordinate, or an empty string for no tile.
|
|
38
|
+
* @param inside - Whether a coordinate is within the caller’s working grid.
|
|
39
|
+
* @param word - The encoded word to lay or spell.
|
|
40
|
+
* @param anchor - The existing tile that the word crosses.
|
|
41
|
+
* @param at - The word offset, seat index or date specified by this signature.
|
|
42
|
+
* @param across - Whether to lay or type horizontally rather than vertically.
|
|
43
|
+
* @param diagonalWord - Optional diagonal-word predicate; null disables diagonal checking.
|
|
44
|
+
* @returns Where `word` stands with its letter `at` on the tile at `anchor`, running across or down, or null where it cannot: a square outside what may be used, a tile at either end, a different letter in its way, a new tile with a neighbour at its side, or no new tile at all — and, given `diagonalWord` (a game with Diagonals), a new tile standing in a diagonal run of three or more that `diagonalWord` does not take (`diagonalsRead`).
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* import { crossingFit } from "@johnmorrisdotca/kumimoji";
|
|
48
|
+
*
|
|
49
|
+
* console.log(crossingFit((r,c)=>r===0&&c===0?"c":"",()=>true,"cat",{row:0,col:0},0,true));
|
|
50
|
+
* ```
|
|
18
51
|
*/
|
|
19
52
|
export declare function crossingFit(letterAt: (row: number, col: number) => string, inside: (row: number, col: number) => boolean, word: string, anchor: Square, at: number, across: boolean, diagonalWord?: ((run: string) => boolean) | null): CrossingFit | null;
|
|
20
53
|
/**
|
|
@@ -23,5 +56,20 @@ export declare function crossingFit(letterAt: (row: number, col: number) => stri
|
|
|
23
56
|
* its new squares. Only a run through a new tile can change, and two new
|
|
24
57
|
* tiles of one word never share a diagonal (they share a row or a column), so
|
|
25
58
|
* each is read on its own. A run of two, a corner touch, is never read.
|
|
59
|
+
*
|
|
60
|
+
* @param letterAt - The tile at a coordinate, or an empty string for no tile.
|
|
61
|
+
* @param inside - Whether a coordinate is within the caller’s working grid.
|
|
62
|
+
* @param word - The encoded word to lay or spell.
|
|
63
|
+
* @param fit - The first coordinate and fresh tile coordinates of the proposed laying.
|
|
64
|
+
* @param isWord - The predicate that accepts a readable word.
|
|
65
|
+
* @returns Whether every diagonal run a laying's new tiles would stand in is taken by `isWord`: each walked from its top end down, with the word's own letters on its new squares. Only a run through a new tile can change, and two new tiles of one word never share a diagonal (they share a row or a column), so each is read on its own. A run of two, a corner touch, is never read.
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
69
|
+
* import { diagonalsRead } from "@johnmorrisdotca/kumimoji";
|
|
70
|
+
*
|
|
71
|
+
* const words = await loadTileWordsFromModule("english");
|
|
72
|
+
* console.log(diagonalsRead(()=>"",()=>true,"cat",{first:{row:0,col:0},fresh:[{row:0,col:0},{row:0,col:1},{row:0,col:2}]},word=>words.allowed.has(word)));
|
|
73
|
+
* ```
|
|
26
74
|
*/
|
|
27
75
|
export declare function diagonalsRead(letterAt: (row: number, col: number) => string, inside: (row: number, col: number) => boolean, word: string, fit: CrossingFit, isWord: (run: string) => boolean): boolean;
|
package/dist/placement.js
CHANGED
|
@@ -28,6 +28,21 @@ import { DIAGONAL_RUN_LEAST } from "./grid.js";
|
|
|
28
28
|
* neighbour at its side, or no new tile at all — and, given `diagonalWord`
|
|
29
29
|
* (a game with Diagonals), a new tile standing in a diagonal run of three or
|
|
30
30
|
* more that `diagonalWord` does not take (`diagonalsRead`).
|
|
31
|
+
*
|
|
32
|
+
* @param letterAt - The tile at a coordinate, or an empty string for no tile.
|
|
33
|
+
* @param inside - Whether a coordinate is within the caller’s working grid.
|
|
34
|
+
* @param word - The encoded word to lay or spell.
|
|
35
|
+
* @param anchor - The existing tile that the word crosses.
|
|
36
|
+
* @param at - The word offset, seat index or date specified by this signature.
|
|
37
|
+
* @param across - Whether to lay or type horizontally rather than vertically.
|
|
38
|
+
* @param diagonalWord - Optional diagonal-word predicate; null disables diagonal checking.
|
|
39
|
+
* @returns Where `word` stands with its letter `at` on the tile at `anchor`, running across or down, or null where it cannot: a square outside what may be used, a tile at either end, a different letter in its way, a new tile with a neighbour at its side, or no new tile at all — and, given `diagonalWord` (a game with Diagonals), a new tile standing in a diagonal run of three or more that `diagonalWord` does not take (`diagonalsRead`).
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* import { crossingFit } from "@johnmorrisdotca/kumimoji";
|
|
43
|
+
*
|
|
44
|
+
* console.log(crossingFit((r,c)=>r===0&&c===0?"c":"",()=>true,"cat",{row:0,col:0},0,true));
|
|
45
|
+
* ```
|
|
31
46
|
*/
|
|
32
47
|
export function crossingFit(letterAt, inside, word, anchor, at, across, diagonalWord = null) {
|
|
33
48
|
const dr = across ? 0 : 1;
|
|
@@ -67,10 +82,28 @@ export function crossingFit(letterAt, inside, word, anchor, at, across, diagonal
|
|
|
67
82
|
* its new squares. Only a run through a new tile can change, and two new
|
|
68
83
|
* tiles of one word never share a diagonal (they share a row or a column), so
|
|
69
84
|
* each is read on its own. A run of two, a corner touch, is never read.
|
|
85
|
+
*
|
|
86
|
+
* @param letterAt - The tile at a coordinate, or an empty string for no tile.
|
|
87
|
+
* @param inside - Whether a coordinate is within the caller’s working grid.
|
|
88
|
+
* @param word - The encoded word to lay or spell.
|
|
89
|
+
* @param fit - The first coordinate and fresh tile coordinates of the proposed laying.
|
|
90
|
+
* @param isWord - The predicate that accepts a readable word.
|
|
91
|
+
* @returns Whether every diagonal run a laying's new tiles would stand in is taken by `isWord`: each walked from its top end down, with the word's own letters on its new squares. Only a run through a new tile can change, and two new tiles of one word never share a diagonal (they share a row or a column), so each is read on its own. A run of two, a corner touch, is never read.
|
|
92
|
+
* @example
|
|
93
|
+
* ```ts
|
|
94
|
+
* import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
|
|
95
|
+
* import { diagonalsRead } from "@johnmorrisdotca/kumimoji";
|
|
96
|
+
*
|
|
97
|
+
* const words = await loadTileWordsFromModule("english");
|
|
98
|
+
* console.log(diagonalsRead(()=>"",()=>true,"cat",{first:{row:0,col:0},fresh:[{row:0,col:0},{row:0,col:1},{row:0,col:2}]},word=>words.allowed.has(word)));
|
|
99
|
+
* ```
|
|
70
100
|
*/
|
|
71
101
|
export function diagonalsRead(letterAt, inside, word, fit, isWord) {
|
|
72
|
-
const freshAt = new Map(fit.fresh.map((square) => [
|
|
73
|
-
|
|
102
|
+
const freshAt = new Map(fit.fresh.map((square) => [
|
|
103
|
+
`${square.row},${square.col}`,
|
|
104
|
+
word[square.row - fit.first.row + (square.col - fit.first.col)],
|
|
105
|
+
]));
|
|
106
|
+
const at = (row, col) => inside(row, col) ? letterAt(row, col) || (freshAt.get(`${row},${col}`) ?? "") : "";
|
|
74
107
|
for (const { row, col } of fit.fresh) {
|
|
75
108
|
for (const lean of [1, -1]) {
|
|
76
109
|
let top = 0;
|