@journey-to-code/games 0.0.0-stage → 0.1.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.
Files changed (207) hide show
  1. package/LICENSE +21 -0
  2. package/dist/backgammon.d.ts +146 -0
  3. package/dist/backgammon.js +1221 -0
  4. package/dist/backgammon.js.map +1 -0
  5. package/dist/blackjack.d.ts +66 -0
  6. package/dist/blackjack.js +452 -0
  7. package/dist/blackjack.js.map +1 -0
  8. package/dist/bluff.d.ts +60 -0
  9. package/dist/bluff.js +239 -0
  10. package/dist/bluff.js.map +1 -0
  11. package/dist/board.d.ts +387 -0
  12. package/dist/board.js +105 -0
  13. package/dist/board.js.map +1 -0
  14. package/dist/bridge.d.ts +86 -0
  15. package/dist/bridge.js +494 -0
  16. package/dist/bridge.js.map +1 -0
  17. package/dist/canasta.d.ts +88 -0
  18. package/dist/canasta.js +646 -0
  19. package/dist/canasta.js.map +1 -0
  20. package/dist/checkers.d.ts +108 -0
  21. package/dist/checkers.js +756 -0
  22. package/dist/checkers.js.map +1 -0
  23. package/dist/chess.d.ts +162 -0
  24. package/dist/chess.js +904 -0
  25. package/dist/chess.js.map +1 -0
  26. package/dist/chesscore.d.ts +136 -0
  27. package/dist/chesscore.js +1121 -0
  28. package/dist/chesscore.js.map +1 -0
  29. package/dist/combat.d.ts +102 -0
  30. package/dist/combat.js +772 -0
  31. package/dist/combat.js.map +1 -0
  32. package/dist/contractrummy.d.ts +77 -0
  33. package/dist/contractrummy.js +430 -0
  34. package/dist/contractrummy.js.map +1 -0
  35. package/dist/crazy8.d.ts +65 -0
  36. package/dist/crazy8.js +366 -0
  37. package/dist/crazy8.js.map +1 -0
  38. package/dist/cribbage.d.ts +75 -0
  39. package/dist/cribbage.js +428 -0
  40. package/dist/cribbage.js.map +1 -0
  41. package/dist/cuckoo.d.ts +43 -0
  42. package/dist/cuckoo.js +216 -0
  43. package/dist/cuckoo.js.map +1 -0
  44. package/dist/drinkingpyramid.d.ts +54 -0
  45. package/dist/drinkingpyramid.js +213 -0
  46. package/dist/drinkingpyramid.js.map +1 -0
  47. package/dist/drinks.d.ts +25 -0
  48. package/dist/drinks.js +40 -0
  49. package/dist/drinks.js.map +1 -0
  50. package/dist/dropfour.d.ts +103 -0
  51. package/dist/dropfour.js +577 -0
  52. package/dist/dropfour.js.map +1 -0
  53. package/dist/durak.d.ts +58 -0
  54. package/dist/durak.js +332 -0
  55. package/dist/durak.js.map +1 -0
  56. package/dist/euchre.d.ts +80 -0
  57. package/dist/euchre.js +453 -0
  58. package/dist/euchre.js.map +1 -0
  59. package/dist/fivedraw.d.ts +50 -0
  60. package/dist/fivedraw.js +289 -0
  61. package/dist/fivedraw.js.map +1 -0
  62. package/dist/fivehundred.d.ts +90 -0
  63. package/dist/fivehundred.js +504 -0
  64. package/dist/fivehundred.js.map +1 -0
  65. package/dist/freecell.d.ts +50 -0
  66. package/dist/freecell.js +327 -0
  67. package/dist/freecell.js.map +1 -0
  68. package/dist/fruitsalad.d.ts +69 -0
  69. package/dist/fruitsalad.js +427 -0
  70. package/dist/fruitsalad.js.map +1 -0
  71. package/dist/gin.d.ts +72 -0
  72. package/dist/gin.js +393 -0
  73. package/dist/gin.js.map +1 -0
  74. package/dist/go.d.ts +132 -0
  75. package/dist/go.js +659 -0
  76. package/dist/go.js.map +1 -0
  77. package/dist/goboard.d.ts +218 -0
  78. package/dist/goboard.js +880 -0
  79. package/dist/goboard.js.map +1 -0
  80. package/dist/gofish.d.ts +37 -0
  81. package/dist/gofish.js +235 -0
  82. package/dist/gofish.js.map +1 -0
  83. package/dist/golf.d.ts +64 -0
  84. package/dist/golf.js +343 -0
  85. package/dist/golf.js.map +1 -0
  86. package/dist/handfoot.d.ts +86 -0
  87. package/dist/handfoot.js +515 -0
  88. package/dist/handfoot.js.map +1 -0
  89. package/dist/hearts.d.ts +65 -0
  90. package/dist/hearts.js +390 -0
  91. package/dist/hearts.js.map +1 -0
  92. package/dist/holdem.d.ts +45 -0
  93. package/dist/holdem.js +286 -0
  94. package/dist/holdem.js.map +1 -0
  95. package/dist/horserace.d.ts +49 -0
  96. package/dist/horserace.js +207 -0
  97. package/dist/horserace.js.map +1 -0
  98. package/dist/index.d.ts +65 -0
  99. package/dist/index.js +66 -0
  100. package/dist/index.js.map +1 -0
  101. package/dist/kingscorner.d.ts +59 -0
  102. package/dist/kingscorner.js +374 -0
  103. package/dist/kingscorner.js.map +1 -0
  104. package/dist/kingscup.d.ts +44 -0
  105. package/dist/kingscup.js +185 -0
  106. package/dist/kingscup.js.map +1 -0
  107. package/dist/kit.d.ts +283 -0
  108. package/dist/kit.js +73 -0
  109. package/dist/kit.js.map +1 -0
  110. package/dist/klondike.d.ts +57 -0
  111. package/dist/klondike.js +314 -0
  112. package/dist/klondike.js.map +1 -0
  113. package/dist/liarspoker.d.ts +61 -0
  114. package/dist/liarspoker.js +298 -0
  115. package/dist/liarspoker.js.map +1 -0
  116. package/dist/memory.d.ts +36 -0
  117. package/dist/memory.js +181 -0
  118. package/dist/memory.js.map +1 -0
  119. package/dist/minigolf.d.ts +104 -0
  120. package/dist/minigolf.js +724 -0
  121. package/dist/minigolf.js.map +1 -0
  122. package/dist/minigolfcourse.d.ts +127 -0
  123. package/dist/minigolfcourse.js +470 -0
  124. package/dist/minigolfcourse.js.map +1 -0
  125. package/dist/offers.d.ts +136 -0
  126. package/dist/offers.js +279 -0
  127. package/dist/offers.js.map +1 -0
  128. package/dist/ohhell.d.ts +66 -0
  129. package/dist/ohhell.js +331 -0
  130. package/dist/ohhell.js.map +1 -0
  131. package/dist/oldmaid.d.ts +40 -0
  132. package/dist/oldmaid.js +186 -0
  133. package/dist/oldmaid.js.map +1 -0
  134. package/dist/pinochle.d.ts +94 -0
  135. package/dist/pinochle.js +466 -0
  136. package/dist/pinochle.js.map +1 -0
  137. package/dist/pitch.d.ts +77 -0
  138. package/dist/pitch.js +362 -0
  139. package/dist/pitch.js.map +1 -0
  140. package/dist/poker.d.ts +79 -0
  141. package/dist/poker.js +268 -0
  142. package/dist/poker.js.map +1 -0
  143. package/dist/president.d.ts +56 -0
  144. package/dist/president.js +339 -0
  145. package/dist/president.js.map +1 -0
  146. package/dist/pyramid.d.ts +39 -0
  147. package/dist/pyramid.js +200 -0
  148. package/dist/pyramid.js.map +1 -0
  149. package/dist/registry.d.ts +17 -0
  150. package/dist/registry.js +70 -0
  151. package/dist/registry.js.map +1 -0
  152. package/dist/reversi.d.ts +69 -0
  153. package/dist/reversi.js +584 -0
  154. package/dist/reversi.js.map +1 -0
  155. package/dist/ridethebus.d.ts +48 -0
  156. package/dist/ridethebus.js +257 -0
  157. package/dist/ridethebus.js.map +1 -0
  158. package/dist/rummy500.d.ts +89 -0
  159. package/dist/rummy500.js +474 -0
  160. package/dist/rummy500.js.map +1 -0
  161. package/dist/sevens.d.ts +48 -0
  162. package/dist/sevens.js +221 -0
  163. package/dist/sevens.js.map +1 -0
  164. package/dist/slapjack.d.ts +42 -0
  165. package/dist/slapjack.js +224 -0
  166. package/dist/slapjack.js.map +1 -0
  167. package/dist/speed.d.ts +44 -0
  168. package/dist/speed.js +201 -0
  169. package/dist/speed.js.map +1 -0
  170. package/dist/spider.d.ts +51 -0
  171. package/dist/spider.js +320 -0
  172. package/dist/spider.js.map +1 -0
  173. package/dist/spite.d.ts +75 -0
  174. package/dist/spite.js +372 -0
  175. package/dist/spite.js.map +1 -0
  176. package/dist/sport.d.ts +266 -0
  177. package/dist/sport.js +48 -0
  178. package/dist/sport.js.map +1 -0
  179. package/dist/switch.d.ts +69 -0
  180. package/dist/switch.js +393 -0
  181. package/dist/switch.js.map +1 -0
  182. package/dist/table.d.ts +284 -0
  183. package/dist/table.js +67 -0
  184. package/dist/table.js.map +1 -0
  185. package/dist/talk.d.ts +17 -0
  186. package/dist/talk.js +44 -0
  187. package/dist/talk.js.map +1 -0
  188. package/dist/thirtyone.d.ts +66 -0
  189. package/dist/thirtyone.js +372 -0
  190. package/dist/thirtyone.js.map +1 -0
  191. package/dist/tictactoe.d.ts +68 -0
  192. package/dist/tictactoe.js +541 -0
  193. package/dist/tictactoe.js.map +1 -0
  194. package/dist/tricks.d.ts +21 -0
  195. package/dist/tricks.js +48 -0
  196. package/dist/tricks.js.map +1 -0
  197. package/dist/war.d.ts +44 -0
  198. package/dist/war.js +185 -0
  199. package/dist/war.js.map +1 -0
  200. package/dist/whist.d.ts +52 -0
  201. package/dist/whist.js +218 -0
  202. package/dist/whist.js.map +1 -0
  203. package/dist/yacht.d.ts +96 -0
  204. package/dist/yacht.js +653 -0
  205. package/dist/yacht.js.map +1 -0
  206. package/package.json +41 -4
  207. package/README.md +0 -3
@@ -0,0 +1,284 @@
1
+ import { type PlayStyle } from "@journey-to-code/rules";
2
+ /** The games that are played with cards. */
3
+ export type CardGameId = "hearts" | "ohhell" | "euchre" | "crazy8" | "gin" | "pinochle" | "canasta" | "gofish" | "oldmaid" | "war" | "whist" | "golf" | "rummy500" | "president" | "klondike" | "freecell" | "handfoot" | "cribbage" | "fivedraw" | "holdem" | "bluff" | "liarspoker" | "switch" | "thirtyone" | "kingscorner" | "blackjack" | "spite" | "pitch" | "fivehundred" | "durak" | "bridge" | "fruitsalad" | "slapjack" | "speed" | "memory" | "sevens" | "cuckoo" | "contractrummy" | "spider" | "pyramid" | "ridethebus" | "kingscup" | "horserace" | "drinkingpyramid";
4
+ /** The games that are played on a board. */
5
+ export type BoardGameId = "tictactoe" | "dropfour" | "combat" | "reversi" | "checkers" | "go" | "chess" | "yacht" | "backgammon";
6
+ /** The games that are played by striking a ball across a table. */
7
+ export type SportGameId = "minigolf";
8
+ /** Every game on the kit. (Rooms, houses and saved rules have always called these "kit" games.) */
9
+ export type KitGameId = CardGameId | BoardGameId | SportGameId;
10
+ /** A move that isn't allowed. The message is shown to the player as is. */
11
+ export declare class GameError extends Error {
12
+ }
13
+ export declare function shuffled<T>(items: readonly T[], rand: () => number): T[];
14
+ /** A seeded random source that survives being stored in a state (a number, advanced each use). */
15
+ export declare function nextRandom(state: {
16
+ rng: number;
17
+ }): () => number;
18
+ /** One choice a player can make with a button. `move` is sent as is. */
19
+ export interface Choice {
20
+ label: string;
21
+ move: Record<string, unknown>;
22
+ /** The main choice: drawn as the big gold button. */
23
+ primary?: boolean;
24
+ hint?: string;
25
+ /** A question to put before it is sent ("Pass your turn?"): for a button that is easy to press and hard to undo. */
26
+ confirm?: string;
27
+ }
28
+ /** Buttons a seat can press now: a bid, a trump call, "Next hand". */
29
+ export interface Choices {
30
+ prompt: string;
31
+ options: Choice[];
32
+ layout?: "grid" | "row" | "list";
33
+ }
34
+ /** One line of a hand summary table: a label and a value per side (team or player). */
35
+ export interface SummaryRow {
36
+ label: string;
37
+ values: string[];
38
+ tone?: "score" | "total";
39
+ }
40
+ /** The scoreboard: one entry per side (team or player): the seats on that side and its score. */
41
+ export type Scores = {
42
+ seats: number[];
43
+ value: number;
44
+ }[];
45
+ /** After a hand or a round: what it scored, a column per side (the seats on it), until everyone moves on. */
46
+ export interface RoundResult {
47
+ key: string;
48
+ title: string;
49
+ sides: number[][];
50
+ rows: SummaryRow[];
51
+ }
52
+ export interface GameOver {
53
+ winners: number[];
54
+ title: string;
55
+ }
56
+ /**
57
+ * A score sheet: a column per thing that is scored (a hole, a frame, a category of dice) and a row
58
+ * per player. Mini Golf's scorecard and Yacht's sheet are both one of these; how it is laid out
59
+ * (holes across the page, categories down it) is the table's business.
60
+ */
61
+ export interface ScoreSheet {
62
+ title: string;
63
+ columns: SheetColumn[];
64
+ /** The column being played, if any. */
65
+ current: number | null;
66
+ rows: SheetRow[];
67
+ /** What `total` and `note` are, for the headings: "Total", "To par". */
68
+ totalLabel: string;
69
+ noteLabel?: string;
70
+ /** The cell filled in last, so the table can point at it. */
71
+ last?: {
72
+ seat: number;
73
+ column: number;
74
+ } | null;
75
+ /**
76
+ * Cells of this seat's own row that it can choose now (score this roll as a full house). Each
77
+ * says what it would score; tapping one sends `{ type, cell }` with the column's `id`. Only ever
78
+ * sent to the seat that can choose.
79
+ */
80
+ pick?: {
81
+ type: string;
82
+ cells: {
83
+ column: number;
84
+ would: string;
85
+ }[];
86
+ } | null;
87
+ }
88
+ export interface SheetColumn {
89
+ label: string;
90
+ /** A second line under the label: a hole's par, what a category scores. */
91
+ sub?: string;
92
+ /** Names this column in a move. Needed only where a cell can be chosen. */
93
+ id?: string;
94
+ /** A column nobody fills in: it is worked out from the others (a bonus). */
95
+ kind?: "sum";
96
+ }
97
+ export interface SheetRow {
98
+ seat: number;
99
+ /** One per column; "" for a cell not filled in yet. */
100
+ cells: string[];
101
+ total: string;
102
+ note?: string;
103
+ }
104
+ /** A finished or running game, in a few words, for lobbies, houses, leaderboards and tournaments. */
105
+ export interface KitSummary {
106
+ game: KitGameId;
107
+ rules: string;
108
+ phase: string;
109
+ over: boolean;
110
+ /** Which hand (or round) it's on, from 1. */
111
+ hand: number;
112
+ /** "120 to 85", "Ann 34 · Bo 51 · â€Ķ". */
113
+ scoreLine: string;
114
+ /** Seats best to worst once it's over (partners together); null while playing. */
115
+ order: number[] | null;
116
+ winners: number[] | null;
117
+ teams: boolean;
118
+ seats: number;
119
+ }
120
+ export declare const who: (seat: number) => string;
121
+ export interface BaseRules {
122
+ id: string;
123
+ name: string;
124
+ }
125
+ /** One house rule, described so the rules editor can draw it. */
126
+ export type RuleField<R> = {
127
+ key: keyof R & string;
128
+ label: string;
129
+ hint?: string;
130
+ kind: "toggle";
131
+ } | {
132
+ key: keyof R & string;
133
+ label: string;
134
+ hint?: string;
135
+ kind: "choice";
136
+ options: [string | number, string][];
137
+ } | {
138
+ key: keyof R & string;
139
+ label: string;
140
+ hint?: string;
141
+ kind: "number";
142
+ min: number;
143
+ max: number;
144
+ step?: number;
145
+ };
146
+ /**
147
+ * A clock of the game's own: a shot clock, a chess clock, a window for claiming a discard. The
148
+ * table keeps the time (the server for a family table, the browser in practice) and makes `move`
149
+ * as `seat` if nothing else happens first.
150
+ */
151
+ export interface GameDeadline<M = Record<string, unknown>> {
152
+ /** How long the seat has, in ms, counted from the last move anyone made (or the deal). */
153
+ after: number;
154
+ /** Whose clock it is: the table counts down on this seat. */
155
+ seat: number;
156
+ /** The move made as that seat when time runs out. It goes through `apply` like any other. */
157
+ move: M;
158
+ /**
159
+ * The clock holds whoever is in the seat, a computer player too (a chess clock: the time is
160
+ * part of the game, and a bot that thinks too long loses on it like anyone). Without it a
161
+ * computer player's seat has no clock, and the table only keeps it moving inside the time.
162
+ */
163
+ strict?: boolean;
164
+ }
165
+ /**
166
+ * How long a computer player may think, when the table says. A game that searches (Chess, Go) can
167
+ * look until the time is nearly gone and play the best move it has; the table gives up on a think
168
+ * that runs far past it and plays a quick move instead (see `TableGame.bot`).
169
+ */
170
+ export interface ThinkBudget {
171
+ /** Ms this think may take. A handful means "answer at once": the first sensible move will do. */
172
+ ms: number;
173
+ }
174
+ /** The style a computer player is asked in when its real think was given up on: no skill wanted, just a legal move, now. */
175
+ export declare const QUICK_STYLE: PlayStyle;
176
+ /** And the budget that goes with it. */
177
+ export declare const QUICK_BUDGET: ThinkBudget;
178
+ /**
179
+ * When a move was made, as the table that keeps the time tells it (a room on the server, the
180
+ * browser in practice). Only a game that keeps time in its state needs it, and a table that keeps
181
+ * no time leaves it out.
182
+ */
183
+ export interface MoveTime {
184
+ /** Ms since the move before it (or since the game began). */
185
+ elapsed: number;
186
+ }
187
+ /**
188
+ * A game, apart from what its table looks like: `View` is what one seat sees (a `KitView` for a
189
+ * card game, a `BoardView` for a board game, a `SportView` for a table sport).
190
+ */
191
+ export interface TableGame<R extends BaseRules = BaseRules, S = unknown, M = Record<string, unknown>, View = unknown> {
192
+ id: KitGameId;
193
+ name: string;
194
+ emoji: string;
195
+ /** For the game picker. */
196
+ blurb: string;
197
+ /** For the setup screen's heading. */
198
+ tagline: string;
199
+ seats: {
200
+ min: number;
201
+ max: number;
202
+ default: number;
203
+ };
204
+ /** Partners across the table (needs four seats). */
205
+ teams: boolean;
206
+ presets: R[];
207
+ fields: RuleField<R>[];
208
+ /** Rules from storage or a page: anything missing comes from the first preset. */
209
+ normalize(rules: unknown): R;
210
+ validate(rules: R): string[];
211
+ /** The rules in plain sentences, for the rules sheet and summaries. */
212
+ describe(rules: R): string[];
213
+ newGame(rules: R, seats: number, seed: number): S;
214
+ /** Apply a seat's move; throws GameError when it isn't allowed. Never changes the given state. */
215
+ apply(state: S, seat: number, move: M, time?: MoveTime): S;
216
+ view(state: S, seat: number): View;
217
+ /**
218
+ * The move a computer player makes now, or null when it has nothing to do. Whether there is a move
219
+ * at all must not depend on the style.
220
+ *
221
+ * `rand` is where a bot that rolls dice gets them (default `Math.random`); tests pass a seeded one.
222
+ *
223
+ * `budget` is how long the think may take, when the table says. The bots here ignore it: they
224
+ * ration themselves by work done (so many positions, so many putts), which gives the same move on
225
+ * a fast machine and a slow one. A game that wants to use the time may, and must then keep to it:
226
+ * a think that runs far past its budget is abandoned by the table, and the seat is asked again
227
+ * with `QUICK_STYLE` and `QUICK_BUDGET`, on the table's own thread, so that answer has to come at
228
+ * once whatever the position.
229
+ */
230
+ bot(state: S, seat: number, style: PlayStyle, rand?: () => number, budget?: ThinkBudget): M | null;
231
+ /**
232
+ * Whether working out this seat's move here is slow enough (a few ms or more) that the table
233
+ * should do it off its main thread: a search, a simulation. It must answer at once, from the state
234
+ * alone, and say no when the seat has nothing to do or its move is obvious ("Next hand"). A game
235
+ * without it always thinks on the table's own thread, which is right for anything quick: handing a
236
+ * think to another thread costs a little too.
237
+ */
238
+ heavy?(state: S, seat: number): boolean;
239
+ /**
240
+ * The computer player's move on the board alone, when `bot` may do something else first (answer
241
+ * an offer: see offers.ts). The turn timer plays this for a person who has run out of time; a
242
+ * game without it is played `bot`'s move.
243
+ */
244
+ play?(state: S, seat: number, style: PlayStyle, rand?: () => number, budget?: ThinkBudget): M | null;
245
+ /** How long a person would think here, in ms (before the persona's own pace). */
246
+ thinkingTime(state: S, seat: number): number;
247
+ /**
248
+ * For a race (a slap in Slapjack): how long this computer player takes to react, in ms. When it
249
+ * gives a number, it's used as is, whatever pace the table is set to, so a person can win the race.
250
+ */
251
+ reactionTime?(state: S, seat: number, style: PlayStyle): number | null;
252
+ /**
253
+ * The game's own clock in this state, if one is running: null (or no `deadline` at all) when the
254
+ * game waits as long as it takes. Asked again after every move, so it describes this state only,
255
+ * and like everything else here it must be worked out from the state alone, never from the time
256
+ * of day. The table's turn timer is separate; when both are running, the sooner one counts. A
257
+ * computer player in the seat is never held to it (and never takes longer than it).
258
+ */
259
+ deadline?(state: S): GameDeadline<M> | null;
260
+ /**
261
+ * Ms of the wait for the next move on the board that have already gone by, over moves that
262
+ * aren't one (an offer and its answer). The table's clocks count from the last move of any kind,
263
+ * so the turn timer takes this off; a game's own `deadline` has already allowed for it.
264
+ */
265
+ spent?(state: S): number;
266
+ summary(state: S): KitSummary;
267
+ }
268
+ export declare const clone: <T>(value: T) => T;
269
+ /** Order sides best-first by score (higher better unless `lowWins`), then flatten to seats. */
270
+ export declare function rankSeats(sides: {
271
+ seats: number[];
272
+ score: number;
273
+ }[], lowWins?: boolean): number[];
274
+ export declare const teamOfSeat: (seat: number) => 0 | 1;
275
+ export declare const teamSeats: (team: 0 | 1) => number[];
276
+ /** A "Next hand" button for each person who hasn't moved on yet; bots move on at once. */
277
+ export declare function nextHandChoices(ready: boolean[], seat: number, last: boolean, next?: string): Choices | null;
278
+ /** Moving on after a hand: marks the seat ready; returns true once everyone is. */
279
+ export declare function markReady(ready: boolean[], seat: number): boolean;
280
+ /** How long to think: a beat plus a little per option, longer when it matters. */
281
+ export declare function thinkFor(options: number, weight?: number): number;
282
+ /** The persona's sharpness: careless players sometimes pick a random option. */
283
+ export declare function careless(style: PlayStyle, rand?: () => number): boolean;
284
+ export declare function pickRandom<T>(items: T[], rand?: () => number): T;
package/dist/table.js ADDED
@@ -0,0 +1,67 @@
1
+ // What every game here has in common, whatever is on the table. A game is a pure, deterministic
2
+ // engine (the same seed and moves always give the same game, so a room can replay its log): its
3
+ // house rules, how a game starts, what a move does, what each seat may see, what a computer player
4
+ // does, and a summary for lobbies and leaderboards. What a seat sees is the one part that differs:
5
+ // card games describe hands, tricks and piles (a `KitView`, in kit.ts) and board games a grid and
6
+ // its pieces (a `BoardView`, in board.ts), and table sports a physics world and whose shot it is
7
+ // (a `SportView`, in sport.ts). Everything else (the room on the server, the setup
8
+ // screen, the rules editor, houses, challenges, tournaments, practice) only needs this file.
9
+ import { mulberry32 } from "@journey-to-code/rules";
10
+ /** A move that isn't allowed. The message is shown to the player as is. */
11
+ export class GameError extends Error {
12
+ }
13
+ export function shuffled(items, rand) {
14
+ const out = [...items];
15
+ for (let i = out.length - 1; i > 0; i--) {
16
+ const j = Math.floor(rand() * (i + 1));
17
+ [out[i], out[j]] = [out[j], out[i]];
18
+ }
19
+ return out;
20
+ }
21
+ /** A seeded random source that survives being stored in a state (a number, advanced each use). */
22
+ export function nextRandom(state) {
23
+ return () => {
24
+ const r = mulberry32(state.rng)();
25
+ state.rng = (state.rng + 0x9e3779b9) >>> 0;
26
+ return r;
27
+ };
28
+ }
29
+ // Text from engines names seats as {0}, {1}â€Ķ; the table puts the players' names in.
30
+ export const who = (seat) => `{${seat}}`;
31
+ /** The style a computer player is asked in when its real think was given up on: no skill wanted, just a legal move, now. */
32
+ export const QUICK_STYLE = { skill: 0, sloppy: 0 };
33
+ /** And the budget that goes with it. */
34
+ export const QUICK_BUDGET = { ms: 10 };
35
+ // ---- Helpers for engines ------------------------------------------------------------------------
36
+ export const clone = (value) => structuredClone(value);
37
+ /** Order sides best-first by score (higher better unless `lowWins`), then flatten to seats. */
38
+ export function rankSeats(sides, lowWins = false) {
39
+ return [...sides].sort((a, b) => (lowWins ? a.score - b.score : b.score - a.score)).flatMap((s) => s.seats);
40
+ }
41
+ export const teamOfSeat = (seat) => (seat % 2);
42
+ export const teamSeats = (team) => [team, team + 2];
43
+ /** A "Next hand" button for each person who hasn't moved on yet; bots move on at once. */
44
+ export function nextHandChoices(ready, seat, last, next = "Deal the next hand") {
45
+ if (ready[seat])
46
+ return null;
47
+ return { prompt: "", options: [{ label: last ? "See the final score" : next, move: { type: "next" }, primary: true }] };
48
+ }
49
+ /** Moving on after a hand: marks the seat ready; returns true once everyone is. */
50
+ export function markReady(ready, seat) {
51
+ if (ready[seat])
52
+ throw new GameError("Waiting for the others.");
53
+ ready[seat] = true;
54
+ return ready.every(Boolean);
55
+ }
56
+ /** How long to think: a beat plus a little per option, longer when it matters. */
57
+ export function thinkFor(options, weight = 1) {
58
+ return Math.round((700 + Math.min(options, 10) * 120) * weight);
59
+ }
60
+ /** The persona's sharpness: careless players sometimes pick a random option. */
61
+ export function careless(style, rand = Math.random) {
62
+ return rand() < (style.sloppy ?? 0) + (1 - (style.skill ?? 1)) * 0.25;
63
+ }
64
+ export function pickRandom(items, rand = Math.random) {
65
+ return items[Math.floor(rand() * items.length)];
66
+ }
67
+ //# sourceMappingURL=table.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table.js","sourceRoot":"","sources":["../src/table.ts"],"names":[],"mappings":"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"}
package/dist/talk.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import type { KitGameId, KitSummary } from "./kit.js";
2
+ export interface KitRemark {
3
+ seat: number;
4
+ text: string;
5
+ delay: number;
6
+ }
7
+ type Cast = {
8
+ bots: {
9
+ seat: number;
10
+ name: string;
11
+ }[];
12
+ };
13
+ type Rand = () => number;
14
+ export declare function kitGreet(cast: Cast, rand?: Rand, game?: KitGameId): KitRemark[];
15
+ /** When a game has just ended: the winners cheer, the rest congratulate. */
16
+ export declare function reactToKit(before: KitSummary, after: KitSummary, cast: Cast, rand?: Rand): KitRemark[];
17
+ export {};
package/dist/talk.js ADDED
@@ -0,0 +1,44 @@
1
+ // Table talk for the kit games: the same cast, in their own voices, saying hello and reacting
2
+ // when a game ends. (Answers to people's chat come from the shared reply lines.)
3
+ import { personaFor } from "@journey-to-code/rules";
4
+ import { isBoardGame, isSportGame } from "./registry.js";
5
+ const LINES = {
6
+ warm: { hello: ["Hi everybody! Good luck 🍀", "Ooh, a new game. Be nice to me 😊"], won: ["I won?! Good game everybody âĪïļ"], lost: ["Good game! I'll get you next time 😅"], someoneWon: ["Congrats! 🎉", "Well played 👏"] },
7
+ dry: { hello: ["Deal 'em.", "Let's go."], won: ["GG.", "That's how it's done."], lost: ["Yeah, yeah. GG."], someoneWon: ["GG.", "Not bad."] },
8
+ goofy: { hello: ["Card party! 🎉", "Who's ready to lose to me? 😂"], won: ["WINNER WINNER 🍗😂", "Woohoo! 🎉ðŸĪŠ"], lost: ["I meant to do that 😂"], someoneWon: ["🎉🎉", "Bow down! 👑😂"] },
9
+ sharp: { hello: ["Good luck. You'll need it 😏"], won: ["As expected 😏", "🏆"], lost: ["Rematch. Now. ðŸ˜Ī"], someoneWon: ["Well played. Rematch?"] },
10
+ quiet: { hello: ["👋"], won: ["GG 🙂"], lost: ["GG"], someoneWon: ["👏"] },
11
+ nervous: { hello: ["Oh gosh, okay, here we go 😎"], won: ["Wait, I won?? ðŸ˜ģ🎉"], lost: ["I knew it ðŸ˜Đ"], someoneWon: ["Congrats! You earned it 👏"] },
12
+ dramatic: { hello: ["Let the games BEGIN 🎭"], won: ["VICTORY IS MINE 🏆âœĻ"], lost: ["Betrayed by the cards ðŸ˜Đ"], someoneWon: ["A worthy champion 👑"] },
13
+ };
14
+ /** At a board or on a green there are no cards to deal or blame: these stand in for the lines that mention them. */
15
+ const BOARD_LINES = {
16
+ dry: { hello: ["Your move.", "Let's go."] },
17
+ goofy: { hello: ["Game time! 🎉", "Who's ready to lose to me? 😂"] },
18
+ dramatic: { lost: ["Outplayed. For now ðŸ˜Đ"] },
19
+ };
20
+ function line(seat, name, event, weight, rand, delay, game) {
21
+ const persona = personaFor(name);
22
+ const voice = persona.voice ?? "warm";
23
+ const options = (isBoardGame(game) || isSportGame(game) ? BOARD_LINES[voice]?.[event] : undefined) ?? LINES[voice][event] ?? LINES.warm[event];
24
+ if (!options?.length || rand() > persona.chatty * weight)
25
+ return null;
26
+ return { seat, text: options[Math.floor(rand() * options.length)], delay: delay[0] + rand() * (delay[1] - delay[0]) };
27
+ }
28
+ export function kitGreet(cast, rand = Math.random, game) {
29
+ return cast.bots
30
+ .map((b) => line(b.seat, b.name, "hello", 0.8, rand, [800, 2500], game))
31
+ .filter((r) => r !== null)
32
+ .slice(0, 2)
33
+ .map((r, i) => ({ ...r, delay: r.delay + i * 1100 }));
34
+ }
35
+ /** When a game has just ended: the winners cheer, the rest congratulate. */
36
+ export function reactToKit(before, after, cast, rand = Math.random) {
37
+ if (!after.over || before.over)
38
+ return [];
39
+ const out = cast.bots
40
+ .map((b) => line(b.seat, b.name, after.winners?.includes(b.seat) ? "won" : after.order?.at(-1) === b.seat ? "lost" : "someoneWon", 2, rand, [500, 2400], after.game))
41
+ .filter((r) => r !== null);
42
+ return out.slice(0, 3).map((r, i) => ({ ...r, delay: r.delay + i * 900 }));
43
+ }
44
+ //# sourceMappingURL=talk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"talk.js","sourceRoot":"","sources":["../src/talk.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { type BaseRules, type CardGame, type KCard } from "./kit.js";
2
+ export interface ThirtyOneRules extends BaseRules {
3
+ lives: number;
4
+ /** Three of a kind counts 30Â―. */
5
+ trips: boolean;
6
+ /** A knocker with the lowest hand loses two lives. */
7
+ knockerPays: boolean;
8
+ /** A house rule: suits don't matter, all three cards count together. Missing on older saved rules: off. */
9
+ anySuit?: boolean;
10
+ /** Drinking mode, a house rule: see the game's describe(). */
11
+ drinking?: boolean;
12
+ }
13
+ export declare const classicThirtyOne: ThirtyOneRules;
14
+ export declare const longThirtyOne: ThirtyOneRules;
15
+ export declare const plainThirtyOne: ThirtyOneRules;
16
+ export declare const noSuitsThirtyOne: ThirtyOneRules;
17
+ export interface ThirtyOneState {
18
+ game: "thirtyone";
19
+ rules: ThirtyOneRules;
20
+ seats: number;
21
+ rng: number;
22
+ hand: number;
23
+ dealer: number;
24
+ lives: number[];
25
+ /** The hand number each seat went out on, for the final order. */
26
+ outAt: (number | null)[];
27
+ hands: KCard[][];
28
+ stock: KCard[];
29
+ discard: KCard[];
30
+ turn: number;
31
+ phase: "draw" | "discard" | "reveal" | "finished";
32
+ /** The card just taken from the discards: it can't go straight back. */
33
+ taken: string | null;
34
+ knocker: number | null;
35
+ /** Turns played this round, so computer players knock sooner as it drags on. */
36
+ turns: number;
37
+ /** Who lost lives at the last showdown, and how many. */
38
+ lost: number[];
39
+ ready: boolean[];
40
+ narration: string | null;
41
+ }
42
+ /**
43
+ * The hand's score: the best suit total, or 30Â― for three of a kind. Only cards of one suit count
44
+ * together, unless the house plays `anySuit`: then every card counts.
45
+ */
46
+ export declare function score31(cards: KCard[], trips?: boolean, anySuit?: boolean): number;
47
+ /**
48
+ * A hand's score in words, saying which cards made it: "20 in â™Ķ (Aâ™Ĩ doesn't count)". The number alone
49
+ * leaves a player with Aâ™Ĩ 10â™Ķ Qâ™Ķ wondering why it isn't 31.
50
+ */
51
+ export declare function explain31(cards: KCard[], trips?: boolean, anySuit?: boolean): string;
52
+ export declare function newThirtyOne(rules: ThirtyOneRules, seats: number, seed: number): ThirtyOneState;
53
+ type Move = {
54
+ type: "draw";
55
+ } | {
56
+ type: "take";
57
+ } | {
58
+ type: "knock";
59
+ } | {
60
+ type: "discard";
61
+ card: string;
62
+ } | {
63
+ type: "next";
64
+ };
65
+ export declare const thirtyOne: CardGame<ThirtyOneRules, ThirtyOneState, Move>;
66
+ export {};