@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/dist/party.js CHANGED
@@ -15,15 +15,48 @@ import { KUMIMOJI_PARTY, kumimojiTileCount } from "./tiles.constants.js";
15
15
  * everybody — and in `partyTurns.ts`: Done, going out, the last round,
16
16
  * resigning and who won. Pure, like every rule here: each returns a new game.
17
17
  */
18
- /** The tiles a bag must hold for this many players: a hand each and one round of draws. */
18
+ /** The tiles a bag must hold for this many players: a hand each and one round of draws.
19
+ *
20
+ * @param players - The requested player count or current player rows.
21
+ * @param hand - The hand size or tile codes, as declared by the function signature.
22
+ * @returns The tiles a bag must hold for this many players: a hand each and one round of draws.
23
+ * @example
24
+ * ```ts
25
+ * import { partyTilesNeeded } from "@johnmorrisdotca/kumimoji";
26
+ *
27
+ * console.log(partyTilesNeeded(2,3));
28
+ * ```
29
+ */
19
30
  export function partyTilesNeeded(players, hand) {
20
31
  return players * hand + players;
21
32
  }
22
- /** Whether a bag of this many tiles can be played by this many: one player can play any bag. */
33
+ /** Whether a bag of this many tiles can be played by this many: one player can play any bag.
34
+ *
35
+ * @param players - The requested player count or current player rows.
36
+ * @param hand - The hand size or tile codes, as declared by the function signature.
37
+ * @param tiles - The map from row,column square names to internal tile codes.
38
+ * @returns Whether a bag of this many tiles can be played by this many: one player can play any bag.
39
+ * @example
40
+ * ```ts
41
+ * import { partyFits } from "@johnmorrisdotca/kumimoji";
42
+ *
43
+ * console.log(partyFits(2,3,12));
44
+ * ```
45
+ */
23
46
  export function partyFits(players, hand, tiles) {
24
47
  return players < KUMIMOJI_PARTY.least || tiles >= partyTilesNeeded(players, hand);
25
48
  }
26
- /** A players count from the address, or 1 (the solo game) for anything that is not two to eight. */
49
+ /** A players count from the address, or 1 (the solo game) for anything that is not two to eight.
50
+ *
51
+ * @param value - The unknown setup value to interpret.
52
+ * @returns A players count from the address, or 1 (the solo game) for anything that is not two to eight.
53
+ * @example
54
+ * ```ts
55
+ * import { partyPlayersAsked } from "@johnmorrisdotca/kumimoji";
56
+ *
57
+ * console.log(partyPlayersAsked("3"));
58
+ * ```
59
+ */
27
60
  export function partyPlayersAsked(value) {
28
61
  const count = Number(value);
29
62
  return Number.isInteger(count) && count >= KUMIMOJI_PARTY.least && count <= KUMIMOJI_PARTY.most ? count : 1;
@@ -35,6 +68,18 @@ const LENGTHS = ["short", "medium", "full"];
35
68
  * players comes back when there are fewer again. A Full bag of one set is 144
36
69
  * tiles, and eight Classic hands with a round of draws are 96, so there is
37
70
  * always one.
71
+ *
72
+ * @param players - The requested player count or current player rows.
73
+ * @param hand - The hand size or tile codes, as declared by the function signature.
74
+ * @param chosen - The selected game length.
75
+ * @param doubleSet - Whether to use two English tile sets.
76
+ * @returns The game length a set-up plays at: the one chosen where the bag holds this many players, else the shortest that does, so a choice made for fewer players comes back when there are fewer again. A Full bag of one set is 144 tiles, and eight Classic hands with a round of draws are 96, so there is always one.
77
+ * @example
78
+ * ```ts
79
+ * import { partyLength } from "@johnmorrisdotca/kumimoji";
80
+ *
81
+ * console.log(partyLength(8,11,"short",false));
82
+ * ```
38
83
  */
39
84
  export function partyLength(players, hand, chosen, doubleSet) {
40
85
  // One player plays any length, and asks nothing of the size: the set-up screen asks this of every puzzle's.
@@ -49,6 +94,19 @@ export function partyLength(players, hand, chosen, doubleSet) {
49
94
  * The deal: each player the next hand from the bag in turn, player one first,
50
95
  * and every table empty. A seat is a name, or a name and whether a computer
51
96
  * plays it; a computer is named "Computer 1", "Computer 2"… in seat order.
97
+ *
98
+ * @param settings - The native party setup settings.
99
+ * @param bag - The original bag in draw order.
100
+ * @param seats - Local player names and optional computer flags.
101
+ * @returns The deal: each player the next hand from the bag in turn, player one first, and every table empty. A seat is a name, or a name and whether a computer plays it; a computer is named "Computer 1", "Computer 2"… in seat order.
102
+ * @example
103
+ * ```ts
104
+ * import { startParty } from "@johnmorrisdotca/kumimoji";
105
+ *
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
+ * console.log(startParty(settings,bag,["Seat 1","Seat 2"]).players.length);
109
+ * ```
52
110
  */
53
111
  export function startParty(settings, bag, seats) {
54
112
  const count = Math.min(Math.max(seats.length, KUMIMOJI_PARTY.least), KUMIMOJI_PARTY.most);
@@ -60,35 +118,129 @@ export function startParty(settings, bag, seats) {
60
118
  const seat = seats[at] ?? "";
61
119
  const asked = typeof seat === "string" ? { name: seat } : seat;
62
120
  const dealt = { hand: [...bag.slice(at * hand, (at + 1) * hand)], tiles: new Map() };
63
- players.push(asked.computer === true ? { name: computerName(players), computer: true, ...dealt } : { name: tidyName(asked.name), ...dealt });
121
+ players.push(asked.computer === true
122
+ ? { name: computerName(players), computer: true, ...dealt }
123
+ : { name: tidyName(asked.name), ...dealt });
64
124
  }
65
- return { settings, bag, returned: "", taken: count * hand, players, dealt: count, turn: 0, turns: 0, out: [], lastTurns: null, resigned: [], resignRun: [], ending: null, startTable: "", traded: false, passedBy: null };
125
+ return {
126
+ settings,
127
+ bag,
128
+ returned: "",
129
+ taken: count * hand,
130
+ players,
131
+ dealt: count,
132
+ turn: 0,
133
+ turns: 0,
134
+ out: [],
135
+ lastTurns: null,
136
+ resigned: [],
137
+ resignRun: [],
138
+ ending: null,
139
+ startTable: "",
140
+ traded: false,
141
+ passedBy: null,
142
+ };
66
143
  }
67
- /** The name a new computer takes: "Computer" and the lowest number no player at the table already goes by. */
144
+ /** The name a new computer takes: "Computer" and the lowest number no player at the table already goes by.
145
+ *
146
+ * @param players - The requested player count or current player rows.
147
+ * @returns The name a new computer takes: "Computer" and the lowest number no player at the table already goes by.
148
+ * @example
149
+ * ```ts
150
+ * import { computerName, startParty } from "@johnmorrisdotca/kumimoji";
151
+ *
152
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
153
+ * const bag = "catdogsun";
154
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
155
+ * console.log(computerName(game.players));
156
+ * ```
157
+ */
68
158
  export function computerName(players) {
69
159
  const taken = new Set(players.map((player) => player.name));
70
160
  for (let number = 1;; number += 1)
71
161
  if (!taken.has(`Computer ${number}`))
72
162
  return `Computer ${number}`;
73
163
  }
74
- /** Whether a computer plays this seat. */
164
+ /** Whether a computer plays this seat.
165
+ *
166
+ * @param game - The current immutable shared-bag party game.
167
+ * @param at - The word offset, seat index or date specified by this signature.
168
+ * @returns Whether a computer plays this seat.
169
+ * @example
170
+ * ```ts
171
+ * import { isComputer, startParty } from "@johnmorrisdotca/kumimoji";
172
+ *
173
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
174
+ * const bag = "catdogsun";
175
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
176
+ * console.log(isComputer(game,0));
177
+ * ```
178
+ */
75
179
  export function isComputer(game, at) {
76
180
  return game.players[at]?.computer === true;
77
181
  }
78
- /** A table's tiles as one string in order, wherever they stand: what a turn compares to see whether anything was laid or lifted. */
182
+ /** A table's tiles as one string in order, wherever they stand: what a turn compares to see whether anything was laid or lifted.
183
+ *
184
+ * @param tiles - The map from row,column square names to internal tile codes.
185
+ * @returns A table's tiles as one string in order, wherever they stand: what a turn compares to see whether anything was laid or lifted.
186
+ * @example
187
+ * ```ts
188
+ * import { tableTally } from "@johnmorrisdotca/kumimoji";
189
+ *
190
+ * const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
191
+ * console.log(tableTally(tiles));
192
+ * ```
193
+ */
79
194
  export function tableTally(tiles) {
80
195
  return [...tiles.values()].sort().join("");
81
196
  }
82
- /** A name as kept: trimmed, one line, and no longer than a line holds. */
197
+ /** A name as kept: trimmed, one line, and no longer than a line holds.
198
+ *
199
+ * @param name - The local player name to tidy.
200
+ * @returns A name as kept: trimmed, one line, and no longer than a line holds.
201
+ * @example
202
+ * ```ts
203
+ * import { tidyName } from "@johnmorrisdotca/kumimoji";
204
+ *
205
+ * console.log(tidyName(" Seat 1 "));
206
+ * ```
207
+ */
83
208
  export function tidyName(name) {
84
209
  return name.replace(/\s+/g, " ").trim().slice(0, KUMIMOJI_PARTY.nameMost);
85
210
  }
86
- /** What a player is called: their name, or "Player 2". */
211
+ /** What a player is called: their name, or "Player 2".
212
+ *
213
+ * @param game - The current immutable shared-bag party game.
214
+ * @param at - The word offset, seat index or date specified by this signature.
215
+ * @returns What a player is called: their name, or "Player 2".
216
+ * @example
217
+ * ```ts
218
+ * import { nameOf, startParty } 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(nameOf(game,0));
224
+ * ```
225
+ */
87
226
  export function nameOf(game, at) {
88
227
  const name = game.players[at]?.name ?? "";
89
228
  return name === "" ? `Player ${at + 1}` : name;
90
229
  }
91
- /** The player whose turn it is, as the solo game's moves take a game: the shared bag, and their own hand and table. */
230
+ /** The player whose turn it is, as the solo game's moves take a game: the shared bag, and their own hand and table.
231
+ *
232
+ * @param game - The current immutable shared-bag party game.
233
+ * @returns The player whose turn it is, as the solo game's moves take a game: the shared bag, and their own hand and table.
234
+ * @example
235
+ * ```ts
236
+ * import { seatPlay, startParty } from "@johnmorrisdotca/kumimoji";
237
+ *
238
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
239
+ * const bag = "catdogsun";
240
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
241
+ * console.log(seatPlay(game));
242
+ * ```
243
+ */
92
244
  export function seatPlay(game) {
93
245
  const player = game.players[game.turn];
94
246
  return { bag: game.bag, returned: game.returned, taken: game.taken, tiles: player.tiles, hand: player.hand };
@@ -100,29 +252,95 @@ export function seatPlay(game) {
100
252
  * takes three, so trading without end lets one player take the bag from the
101
253
  * others. After a trade the player may still lay, lift and draw, and press
102
254
  * Done; the next trade is on their next turn.
255
+ *
256
+ * @param game - The current immutable shared-bag party game.
257
+ * @returns ONE TRADE A TURN. John, 2026-09-29, after a player traded over and over and built up a hand: "if they swap tiles … give them a chance to lay some tiles but cannot ask for another swap until next turn." A trade gives one tile and takes three, so trading without end lets one player take the bag from the others. After a trade the player may still lay, lift and draw, and press Done; the next trade is on their next turn.
258
+ * @example
259
+ * ```ts
260
+ * import { mayTradeThisTurn, startParty } from "@johnmorrisdotca/kumimoji";
261
+ *
262
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
263
+ * const bag = "catdogsun";
264
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
265
+ * console.log(mayTradeThisTurn(game));
266
+ * ```
103
267
  */
104
268
  export function mayTradeThisTurn(game) {
105
269
  return game.ending === null && !game.traded && mayTrade(seatPlay(game));
106
270
  }
107
- /** A move made on `seatPlay` put back: the bag as it left it, and the hand and table to the player whose turn it is. A second trade in one turn is refused, the game given back as it was. */
271
+ /** A move made on `seatPlay` put back: the bag as it left it, and the hand and table to the player whose turn it is. A second trade in one turn is refused, the game given back as it was.
272
+ *
273
+ * @param game - The current immutable shared-bag party game.
274
+ * @param play - The current immutable solo-game progress.
275
+ * @returns A move made on `seatPlay` put back: the bag as it left it, and the hand and table to the player whose turn it is. A second trade in one turn is refused, the game given back as it was.
276
+ * @example
277
+ * ```ts
278
+ * import { seatPlay, startParty, withSeatPlay } from "@johnmorrisdotca/kumimoji";
279
+ *
280
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
281
+ * const bag = "catdogsun";
282
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
283
+ * console.log(withSeatPlay(game,seatPlay(game)).taken);
284
+ * ```
285
+ */
108
286
  export function withSeatPlay(game, play) {
109
287
  if (game.ending !== null)
110
288
  return game;
111
289
  if (game.traded && play.returned.length > game.returned.length)
112
290
  return game;
113
- const players = game.players.map((player, at) => (at === game.turn ? { ...player, hand: play.hand, tiles: play.tiles } : player));
291
+ const players = game.players.map((player, at) => at === game.turn ? { ...player, hand: play.hand, tiles: play.tiles } : player);
114
292
  const traded = game.traded || play.returned.length > game.returned.length;
115
293
  return { ...game, returned: play.returned, taken: play.taken, players, traded };
116
294
  }
117
- /** Tiles still in the shared bag. */
295
+ /** Tiles still in the shared bag.
296
+ *
297
+ * @param game - The current immutable shared-bag party game.
298
+ * @returns Tiles still in the shared bag.
299
+ * @example
300
+ * ```ts
301
+ * import { partyTilesLeft, startParty } from "@johnmorrisdotca/kumimoji";
302
+ *
303
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
304
+ * const bag = "catdogsun";
305
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
306
+ * console.log(partyTilesLeft(game));
307
+ * ```
308
+ */
118
309
  export function partyTilesLeft(game) {
119
310
  return tilesLeft(seatPlay(game));
120
311
  }
121
- /** The players still in — not resigned — by place, in turn order. Somebody who went out is still in until the game ends. */
312
+ /** The players still in — not resigned — by place, in turn order. Somebody who went out is still in until the game ends.
313
+ *
314
+ * @param game - The current immutable shared-bag party game.
315
+ * @returns The players still in — not resigned — by place, in turn order. Somebody who went out is still in until the game ends.
316
+ * @example
317
+ * ```ts
318
+ * import { startParty, stillIn } from "@johnmorrisdotca/kumimoji";
319
+ *
320
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
321
+ * const bag = "catdogsun";
322
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
323
+ * console.log(stillIn(game));
324
+ * ```
325
+ */
122
326
  export function stillIn(game) {
123
327
  return game.players.flatMap((_, at) => (game.resigned.includes(at) ? [] : [at]));
124
328
  }
125
- /** The next player still in after `from`, round the table; `from` itself when nobody else is. */
329
+ /** The next player still in after `from`, round the table; `from` itself when nobody else is.
330
+ *
331
+ * @param game - The current immutable shared-bag party game.
332
+ * @param from - The source square or seat index.
333
+ * @returns The next player still in after `from`, round the table; `from` itself when nobody else is.
334
+ * @example
335
+ * ```ts
336
+ * import { nextIn, startParty } from "@johnmorrisdotca/kumimoji";
337
+ *
338
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
339
+ * const bag = "catdogsun";
340
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
341
+ * console.log(nextIn(game,0));
342
+ * ```
343
+ */
126
344
  export function nextIn(game, from) {
127
345
  const count = game.players.length;
128
346
  for (let step = 1; step <= count; step += 1) {
@@ -136,14 +354,46 @@ export function nextIn(game, from) {
136
354
  * Whether Draw may be pressed: the hand of the player whose turn it is used,
137
355
  * their grid sound, and a tile in the bag for every player still in. Never in
138
356
  * the last round, when the bag already holds fewer than that.
357
+ *
358
+ * @param game - The current immutable shared-bag party game.
359
+ * @param verdict - The native word and connectivity verdict for this grid.
360
+ * @returns Whether Draw may be pressed: the hand of the player whose turn it is used, their grid sound, and a tile in the bag for every player still in. Never in the last round, when the bag already holds fewer than that.
361
+ * @example
362
+ * ```ts
363
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
364
+ * import { judgeWithWords, mayDrawAll, startParty } from "@johnmorrisdotca/kumimoji";
365
+ *
366
+ * const words = await loadTileWordsFromModule("english");
367
+ * const verdict = judgeWithWords(new Map([["0,0","c"],["0,1","a"],["0,2","t"]]),words);
368
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
369
+ * const bag = "catdogsun";
370
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
371
+ * console.log(mayDrawAll(game,verdict));
372
+ * ```
139
373
  */
140
374
  export function mayDrawAll(game, verdict) {
141
- return game.ending === null && game.lastTurns === null && game.players[game.turn].hand.length === 0 && verdict.sound && partyTilesLeft(game) >= stillIn(game).length;
375
+ return (game.ending === null &&
376
+ game.lastTurns === null &&
377
+ game.players[game.turn].hand.length === 0 &&
378
+ verdict.sound &&
379
+ partyTilesLeft(game) >= stillIn(game).length);
142
380
  }
143
381
  /**
144
382
  * DRAW: every player still in takes one tile, in turn order from the one who
145
383
  * pressed it — the race game's rule that when one player uses their hand,
146
384
  * everybody draws.
385
+ *
386
+ * @param game - The current immutable shared-bag party game.
387
+ * @returns DRAW: every player still in takes one tile, in turn order from the one who pressed it — the race game's rule that when one player uses their hand, everybody draws.
388
+ * @example
389
+ * ```ts
390
+ * import { drawAll, startParty } from "@johnmorrisdotca/kumimoji";
391
+ *
392
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
393
+ * const bag = "catdogsun";
394
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
395
+ * console.log(drawAll(game));
396
+ * ```
147
397
  */
148
398
  export function drawAll(game) {
149
399
  const count = game.players.length;
@@ -151,7 +401,7 @@ export function drawAll(game) {
151
401
  if (partyTilesLeft(game) < order.length)
152
402
  return game;
153
403
  const coming = (game.bag + game.returned).slice(game.taken, game.taken + order.length);
154
- const players = game.players.map((player, at) => (order.includes(at) ? { ...player, hand: [...player.hand, coming[order.indexOf(at)]] } : player));
404
+ const players = game.players.map((player, at) => order.includes(at) ? { ...player, hand: [...player.hand, coming[order.indexOf(at)]] } : player);
155
405
  return { ...game, taken: game.taken + order.length, players };
156
406
  }
157
407
  /**
@@ -161,11 +411,38 @@ export function drawAll(game) {
161
411
  * screen opens on the table of whoever has just played (the first player's,
162
412
  * before anybody has), and a swipe steps round every player in seat order,
163
413
  * resigned ones included.
414
+ *
415
+ * @param game - The current immutable shared-bag party game.
416
+ * @returns WHOSE TABLE IS SHOWN. Nothing in a pass-and-play game is secret — John, 2026-09-28: "there are no secrets because they are face up" — so every table and every hand can be looked at by anybody, read-only. The pass screen opens on the table of whoever has just played (the first player's, before anybody has), and a swipe steps round every player in seat order, resigned ones included.
417
+ * @example
418
+ * ```ts
419
+ * import { passViewStart, startParty } from "@johnmorrisdotca/kumimoji";
420
+ *
421
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
422
+ * const bag = "catdogsun";
423
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
424
+ * console.log(passViewStart(game));
425
+ * ```
164
426
  */
165
427
  export function passViewStart(game) {
166
428
  return game.passedBy ?? game.turn;
167
429
  }
168
- /** The player a swipe or an arrow shows next: `by` seats on, round the table. */
430
+ /** The player a swipe or an arrow shows next: `by` seats on, round the table.
431
+ *
432
+ * @param game - The current immutable shared-bag party game.
433
+ * @param from - The source square or seat index.
434
+ * @param by - The direction or distance to step between seats.
435
+ * @returns The player a swipe or an arrow shows next: `by` seats on, round the table.
436
+ * @example
437
+ * ```ts
438
+ * import { startParty, stepView } from "@johnmorrisdotca/kumimoji";
439
+ *
440
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
441
+ * const bag = "catdogsun";
442
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
443
+ * console.log(stepView(game,0,1));
444
+ * ```
445
+ */
169
446
  export function stepView(game, from, by) {
170
447
  const count = game.players.length;
171
448
  return (((from + by) % count) + count) % count;
@@ -1,6 +1,16 @@
1
1
  import type { Tiles } from "./grid.ts";
2
2
  import type { KumimojiLanguage, KumimojiLength, KumimojiLevel } from "./kumimoji.types.ts";
3
- /** What a pass-and-play game was set up as: the address's own choices, which a kept game is matched to. */
3
+ /** What a pass-and-play game was set up as: the address's own choices, which a kept game is matched to.
4
+ *
5
+ * @example
6
+ * ```ts
7
+ * import type { PartySettings } from "@johnmorrisdotca/kumimoji";
8
+ *
9
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
10
+ * const value: PartySettings = settings;
11
+ * console.log(value);
12
+ * ```
13
+ */
4
14
  export type PartySettings = {
5
15
  /** The hand each player is dealt: 7 Quick or 11 Classic (3 in the browser tests alone). */
6
16
  size: number;
@@ -20,6 +30,15 @@ export type PartySettings = {
20
30
  * their own table, and whether the seat is a computer's (`computerTurn.ts`),
21
31
  * whose turns play themselves in the browser. A computer's name is always
22
32
  * written out ("Computer 1"), never left for its number.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * import type { PartyPlayer } from "@johnmorrisdotca/kumimoji";
37
+ *
38
+ * const tiles = new Map([["0,0","c"],["0,1","a"],["0,2","t"]]);
39
+ * const value: PartyPlayer = {name:"Seat 1",hand:["c","a","t"],tiles:new Map()};
40
+ * console.log(value);
41
+ * ```
23
42
  */
24
43
  export type PartyPlayer = {
25
44
  name: string;
@@ -27,7 +46,16 @@ export type PartyPlayer = {
27
46
  tiles: Tiles;
28
47
  computer?: boolean;
29
48
  };
30
- /** A seat asked for before the deal or at a join: a name (empty for its number) and whether a computer plays it. */
49
+ /** A seat asked for before the deal or at a join: a name (empty for its number) and whether a computer plays it.
50
+ *
51
+ * @example
52
+ * ```ts
53
+ * import type { PartySeat } from "@johnmorrisdotca/kumimoji";
54
+ *
55
+ * const value: PartySeat = {name:"Computer",computer:true};
56
+ * console.log(value);
57
+ * ```
58
+ */
31
59
  export type PartySeat = {
32
60
  name: string;
33
61
  computer?: boolean;
@@ -35,6 +63,18 @@ export type PartySeat = {
35
63
  /**
36
64
  * A PASS-AND-PLAY KUMIMOJI: one bag, read as a line the way the solo game
37
65
  * reads it (`TilePlay`), and a hand and a table for each player.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import { startParty } from "@johnmorrisdotca/kumimoji";
70
+ * import type { PartyGame } from "@johnmorrisdotca/kumimoji";
71
+ *
72
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
73
+ * const bag = "catdogsun";
74
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
75
+ * const value: PartyGame = game;
76
+ * console.log(value);
77
+ * ```
38
78
  */
39
79
  export type PartyGame = {
40
80
  settings: PartySettings;
@@ -82,5 +122,13 @@ export type PartyGame = {
82
122
  * had a last turn; the winners are everybody who went out. `standing`: every
83
123
  * other player resigned and the last one in laid a tile. `tied`: nobody was
84
124
  * left standing, and the players who resigned in that final run are tied.
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * import type { PartyEnding } from "@johnmorrisdotca/kumimoji";
129
+ *
130
+ * const value: PartyEnding = "out";
131
+ * console.log(value);
132
+ * ```
85
133
  */
86
134
  export type PartyEnding = "out" | "standing" | "tied";
@@ -1,7 +1,31 @@
1
1
  import type { PartyGame, PartySettings } from "./party.types.ts";
2
- /** A table of players as text to keep. `decodeParty` reads it back, and given the word list's `familyKey` checks every tile held against the bag. */
2
+ /** A table of players as text to keep. `decodeParty` reads it back, and given the word list's `familyKey` checks every tile held against the bag.
3
+ *
4
+ * @param game - The current immutable shared-bag party game.
5
+ * @returns A table of players as text to keep. `decodeParty` reads it back, and given the word list's `familyKey` checks every tile held against the bag.
6
+ * @example
7
+ * ```ts
8
+ * import { encodeParty, startParty } from "@johnmorrisdotca/kumimoji";
9
+ *
10
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
11
+ * const bag = "catdogsun";
12
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
13
+ * console.log(encodeParty(game));
14
+ * ```
15
+ */
3
16
  export declare function encodeParty(game: PartyGame): string;
4
- /** A game's settings as something outside this browser sent them — a table on several devices, a kept game — checked, or null. */
17
+ /** A game's settings as something outside this browser sent them — a table on several devices, a kept game — checked, or null.
18
+ *
19
+ * @param value - The unknown setup value to interpret.
20
+ * @returns A game's settings as something outside this browser sent them — a table on several devices, a kept game — checked, or null.
21
+ * @example
22
+ * ```ts
23
+ * import { readSettings } from "@johnmorrisdotca/kumimoji";
24
+ *
25
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
26
+ * console.log(readSettings(settings));
27
+ * ```
28
+ */
5
29
  export declare function readSettings(value: unknown): PartySettings | null;
6
30
  /**
7
31
  * A kept game read back, or null for anything that is not one. With
@@ -10,9 +34,55 @@ export declare function readSettings(value: unknown): PartySettings | null;
10
34
  * it and not given back, as the solo game checks a kept run
11
35
  * (`decodeTileProgress`). Without, its shape alone, which is enough to offer
12
36
  * it on the set-up screen.
37
+ *
38
+ * @param code - The encoded grid or progress text.
39
+ * @param familyKey - The function identifying physical tile families, or null for invalid codes.
40
+ * @returns A kept game read back, or null for anything that is not one. With `familyKey` (the loaded word list's), it is also checked against its own bag: every tile held by anybody — hand and table — must be one taken out of it and not given back, as the solo game checks a kept run (`decodeTileProgress`). Without, its shape alone, which is enough to offer it on the set-up screen.
41
+ * @example
42
+ * ```ts
43
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
44
+ * import { decodeParty, encodeParty, startParty } from "@johnmorrisdotca/kumimoji";
45
+ *
46
+ * const words = await loadTileWordsFromModule("english");
47
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
48
+ * const bag = "catdogsun";
49
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
50
+ * console.log(decodeParty(encodeParty(game),words.familyKey)?.players.length);
51
+ * ```
13
52
  */
14
53
  export declare function decodeParty(code: string | null, familyKey?: (tile: string) => string | null): PartyGame | null;
15
- /** Whether what everybody holds is exactly what was taken from the bag, less what was given back. */
54
+ /** Whether what everybody holds is exactly what was taken from the bag, less what was given back.
55
+ *
56
+ * @param game - The current immutable shared-bag party game.
57
+ * @param familyKey - The function identifying physical tile families, or null for invalid codes.
58
+ * @returns Whether what everybody holds is exactly what was taken from the bag, less what was given back.
59
+ * @example
60
+ * ```ts
61
+ * import { loadTileWordsFromModule } from "@johnmorrisdotca/kumimoji/words";
62
+ * import { holdsItsBag, startParty } from "@johnmorrisdotca/kumimoji";
63
+ *
64
+ * const words = await loadTileWordsFromModule("english");
65
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
66
+ * const bag = "catdogsun";
67
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
68
+ * console.log(holdsItsBag(game,words.familyKey));
69
+ * ```
70
+ */
16
71
  export declare function holdsItsBag(game: PartyGame, familyKey: (tile: string) => string | null): boolean;
17
- /** Whether a kept game is the one an address asks for: the same settings, and dealt to as many players, whoever has joined or left since. */
72
+ /** Whether a kept game is the one an address asks for: the same settings, and dealt to as many players, whoever has joined or left since.
73
+ *
74
+ * @param game - The current immutable shared-bag party game.
75
+ * @param settings - The native party setup settings.
76
+ * @param players - The requested player count or current player rows.
77
+ * @returns Whether a kept game is the one an address asks for: the same settings, and dealt to as many players, whoever has joined or left since.
78
+ * @example
79
+ * ```ts
80
+ * import { isPartyFor, startParty } from "@johnmorrisdotca/kumimoji";
81
+ *
82
+ * const settings = {size:3,level:"hard",seed:44,gameLength:"short",language:"english",doubleSet:false,diagonals:false,hints:false} as const;
83
+ * const bag = "catdogsun";
84
+ * const game = startParty(settings,bag,["Seat 1","Seat 2"]);
85
+ * console.log(isPartyFor(game,settings,2));
86
+ * ```
87
+ */
18
88
  export declare function isPartyFor(game: PartyGame, settings: PartySettings, players: number): boolean;