@johnmorrisdotca/domino 1.0.0 → 1.1.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/dist/cli.js ADDED
@@ -0,0 +1,243 @@
1
+ import { endsOf } from "./mexicanTrain/dominoes.js";
2
+ import { TRAIN_DEFAULT_OPTIONS, TRAIN_DOUBLES, TRAIN_LENGTHS, TRAIN_MEXICAN } from "./mexicanTrain/mexicanTrain.constants.js";
3
+ import { TRAIN_PHASES, moveCount, movesOf, playTrain, startTrain, trainTotals } from "./mexicanTrain/mexicanTrain.js";
4
+ import { decodeTrain, encodeTrain } from "./mexicanTrain/trainCodec.js";
5
+ import { computerMove } from "./mexicanTrain/trainComputer.js";
6
+ import { DOMINO_STRINGS, dominoLanguage, dominoSay } from "./strings.js";
7
+ import { VERSION } from "./version.js";
8
+ import { trainNews, trainSeatName, trainSetLabel, trainStatus } from "./words.js";
9
+ /** The most moves a played game may take before the command line gives up, far above any game the rules allow. */
10
+ export const CLI_MOVES_MOST = 100000;
11
+ /** The language the command line speaks: `--lang`, or the environment's, or the system's; Japanese for `ja…`, English for anything else. */
12
+ export function cliLanguage(flag, env = {}, locale) {
13
+ const named = [flag, env.LC_ALL, env.LC_MESSAGES, env.LANG].find((value) => value !== undefined && value !== "" && value !== "C" && value !== "POSIX" && !value.startsWith("C."));
14
+ return dominoLanguage(named ?? locale);
15
+ }
16
+ const FLAGS_WITH_VALUES = { "-s": "seed", "--seed": "seed", "--set": "set", "--players": "players", "--length": "length", "--doubles": "doubles", "--mexican": "mexican", "--lang": "lang" };
17
+ const FLAGS = { "--moves": "moves", "--save": "save", "--stdin": "stdin", "-j": "json", "--json": "json", "-h": "help", "--help": "help", "-v": "version", "--version": "version" };
18
+ const COMMANDS = ["deal", "play", "check", "replay"];
19
+ /** The arguments sorted into options and words. */
20
+ function sortArguments(args) {
21
+ const asked = { values: {}, flags: new Set(), words: [], wrong: null };
22
+ for (let i = 0; i < args.length; i += 1) {
23
+ const arg = args[i];
24
+ const [name, inline] = arg.startsWith("--") && arg.includes("=") ? [arg.slice(0, arg.indexOf("=")), arg.slice(arg.indexOf("=") + 1)] : [arg, undefined];
25
+ if (arg === "--") {
26
+ asked.words.push(...args.slice(i + 1));
27
+ break;
28
+ }
29
+ if (name in FLAGS_WITH_VALUES) {
30
+ const value = inline ?? args[++i];
31
+ if (value === undefined) {
32
+ asked.wrong ?? (asked.wrong = { message: "cliNeeds", part: name });
33
+ break;
34
+ }
35
+ asked.values[FLAGS_WITH_VALUES[name]] = value;
36
+ }
37
+ else if (name in FLAGS && inline === undefined)
38
+ asked.flags.add(FLAGS[name]);
39
+ // The first wrong option is the one reported; the rest are still read, so that the report comes in the language asked for.
40
+ else if (arg.startsWith("-") && arg !== "-")
41
+ asked.wrong ?? (asked.wrong = { message: "cliUnknown", part: arg });
42
+ else
43
+ asked.words.push(arg);
44
+ }
45
+ return asked;
46
+ }
47
+ const wholeNumber = (text, least, most) => (text !== undefined && /^\d{1,10}$/.test(text) && Number(text) >= least && Number(text) <= most ? Number(text) : null);
48
+ /** A tile as text a program can read: the larger end first, "12-3". */
49
+ const tileText = (tile) => {
50
+ const [low, high] = endsOf(tile);
51
+ return `${high}-${low}`;
52
+ };
53
+ /** A hand in the order a person sorts it: by the larger end, then the smaller. */
54
+ const sorted = (hand) => [...hand].sort((x, y) => {
55
+ const [xl, xh] = endsOf(x);
56
+ const [yl, yh] = endsOf(y);
57
+ return xh - yh || xl - yl;
58
+ });
59
+ /** Names every seat, for a table the command line made. */
60
+ const seatNames = (game, language) => game.players.map((_, seat) => trainSeatName(game, seat, language));
61
+ /** Run the command line. See `domino --help` for what it takes. One seed serves the whole run, so the same command prints the same lines on every machine. */
62
+ export function runCli(args, around = {}) {
63
+ const asked = sortArguments(args);
64
+ const env = around.env ?? {};
65
+ const lang = asked.values.lang;
66
+ const language = cliLanguage(lang, env, around.locale);
67
+ const t = DOMINO_STRINGS[language];
68
+ const wrong = (message) => ({ code: 2, out: "", err: `domino: ${message}\n${t.cliTryHelp}\n` });
69
+ const failed = (message) => ({ code: 1, out: "", err: `domino: ${message}\n` });
70
+ if (asked.wrong !== null)
71
+ return wrong(dominoSay(t[asked.wrong.message], { part: asked.wrong.part }));
72
+ if (lang !== undefined && lang !== "en" && lang !== "ja")
73
+ return wrong(t.cliLangBad);
74
+ if (asked.flags.has("help"))
75
+ return { code: 0, out: t.cliUsage, err: "" };
76
+ if (asked.flags.has("version"))
77
+ return { code: 0, out: `${VERSION}\n`, err: "" };
78
+ const command = asked.words[0];
79
+ if (command === undefined)
80
+ return wrong(t.cliNoCommand);
81
+ if (!COMMANDS.includes(command))
82
+ return wrong(dominoSay(t.cliCommandBad, { part: command }));
83
+ const json = asked.flags.has("json");
84
+ // A saved game: read it back through the rules, and say where it stands (`check`), or every move (`replay`).
85
+ if (command === "check" || command === "replay") {
86
+ const text = asked.flags.has("stdin") ? (around.stdin ?? "").trim() : asked.words.slice(1).join(" ").trim();
87
+ if (text === "")
88
+ return wrong(t.cliNoSaved);
89
+ const game = decodeTrain(text);
90
+ if (game === null)
91
+ return failed(t.cliSavedBad);
92
+ return command === "check" ? checkOut(game, language, json) : replayOut(game, language);
93
+ }
94
+ // A table: the set, the players, the seed and the house rules.
95
+ const set = asked.values.set === undefined ? 12 : wholeNumber(asked.values.set, 9, 15);
96
+ if (set === null || ![9, 12, 15].includes(set))
97
+ return wrong(t.cliSetBad);
98
+ const players = asked.values.players === undefined ? 4 : wholeNumber(asked.values.players, 2, 8);
99
+ if (players === null)
100
+ return wrong(t.cliPlayersBad);
101
+ let seed;
102
+ let fresh = false;
103
+ if (asked.values.seed === undefined) {
104
+ seed = Math.floor((around.random ?? Math.random)() * 2147483647) + 1;
105
+ fresh = true;
106
+ }
107
+ else {
108
+ const read = wholeNumber(asked.values.seed, 0, 4294967295);
109
+ if (read === null)
110
+ return wrong(t.cliSeedBad);
111
+ seed = read;
112
+ }
113
+ const options = { ...TRAIN_DEFAULT_OPTIONS };
114
+ const choose = (flag, allowed, spelled = {}) => {
115
+ const value = asked.values[flag];
116
+ if (value === undefined)
117
+ return options[flag];
118
+ const key = spelled[value] ?? value;
119
+ return key in allowed ? key : null;
120
+ };
121
+ const length = choose("length", TRAIN_LENGTHS);
122
+ const doubles = choose("doubles", TRAIN_DOUBLES);
123
+ const mexican = choose("mexican", TRAIN_MEXICAN, { "own-first": "ownFirst" });
124
+ if (length === null)
125
+ return wrong(dominoSay(t.cliValueBad, { part: "--length", choices: Object.keys(TRAIN_LENGTHS).join(", ") }));
126
+ if (doubles === null)
127
+ return wrong(dominoSay(t.cliValueBad, { part: "--doubles", choices: Object.keys(TRAIN_DOUBLES).join(", ") }));
128
+ if (mexican === null)
129
+ return wrong(dominoSay(t.cliValueBad, { part: "--mexican", choices: "any, own-first" }));
130
+ Object.assign(options, { length, doubles, mexican });
131
+ const dealt = startTrain(set, Array.from({ length: players }, () => ""), seed, options, Array.from({ length: players }, () => true));
132
+ if (dealt === null)
133
+ return wrong(t.cliSetBad);
134
+ const note = fresh ? `domino: ${dominoSay(t.cliFresh, { seed })}\n` : "";
135
+ const done = command === "deal" ? dealOut(dealt, language, json) : playOut(dealt, language, json, asked.flags.has("moves"), asked.flags.has("save"));
136
+ return { ...done, err: done.err + note };
137
+ }
138
+ /** The deal, written as lines: the hub, and every hand. */
139
+ function dealOut(game, language, json) {
140
+ const t = DOMINO_STRINGS[language];
141
+ const names = seatNames(game, language);
142
+ if (json) {
143
+ const data = { format: 1, generator: `domino ${VERSION}`, set: game.set, players: names, seed: game.seed, options: game.options, engine: game.engine, hands: game.hands.map((hand) => sorted(hand).map(tileText)), boneyard: game.boneyard.length };
144
+ return { code: 0, out: `${JSON.stringify(data, null, 2)}\n`, err: "" };
145
+ }
146
+ const lines = [
147
+ dominoSay(t.cliDeal, { set: trainSetLabel(game.set, language) ?? game.set, players: game.players.length, seed: game.seed }),
148
+ dominoSay(t.round, { n: 1, total: game.rounds, engine: game.engine }),
149
+ ...game.hands.map((hand, seat) => dominoSay(t.cliSeat, { who: names[seat], tiles: sorted(hand).map(tileText).join(" ") })),
150
+ dominoSay(t.cliBoneyard, { n: game.boneyard.length }),
151
+ ];
152
+ return { code: 0, out: `${lines.join("\n")}\n`, err: "" };
153
+ }
154
+ /** A table of scores, one row to a round and a row of totals, in columns. */
155
+ function scoreTable(game, language) {
156
+ const t = DOMINO_STRINGS[language];
157
+ const names = seatNames(game, language);
158
+ const rows = [[t.hub, ...names], ...game.results.map((result) => [String(result.engine), ...result.pips.map((pips, seat) => `${pips}${result.out === seat ? "*" : ""}`)]), [t.total, ...trainTotals(game).map(String)]];
159
+ const widths = rows[0].map((_, column) => Math.max(...rows.map((row) => [...row[column]].length)));
160
+ return rows.map((row) => row.map((cell, column) => (column === 0 ? cell.padEnd(widths[column]) : cell.padStart(widths[column]))).join(" ").trimEnd());
161
+ }
162
+ /** The winners' line. */
163
+ function winnerLine(game, language) {
164
+ const t = DOMINO_STRINGS[language];
165
+ const who = game.winners.map((seat) => trainSeatName(game, seat, language)).join(", ");
166
+ return dominoSay(game.winners.length === 1 ? t.cliWinner : t.cliWinners, { who, pips: Math.min(...trainTotals(game)) });
167
+ }
168
+ /** A game played out by the computers. */
169
+ function playOut(start, language, json, moves, save) {
170
+ const t = DOMINO_STRINGS[language];
171
+ let game = start;
172
+ const told = [];
173
+ while (game.phase !== TRAIN_PHASES.finished) {
174
+ if (moveCount(game) >= CLI_MOVES_MOST)
175
+ return { code: 1, out: "", err: `domino: ${t.cliSavedBad}\n` };
176
+ const next = playTrain(game, computerMove(game));
177
+ if (next === null)
178
+ return { code: 1, out: "", err: `domino: ${t.cliSavedBad}\n` };
179
+ game = next;
180
+ if (moves) {
181
+ told.push(`${moveCount(game)}. ${trainNews(game, language)}`);
182
+ }
183
+ }
184
+ if (json)
185
+ return { code: 0, out: `${JSON.stringify(summary(game, language, true), null, 2)}\n`, err: "" };
186
+ const head = dominoSay(t.cliPlayed, { set: trainSetLabel(game.set, language) ?? game.set, players: game.players.length, seed: game.seed, rounds: game.rounds, moves: moveCount(game) });
187
+ const lines = [head, ...(moves ? ["", ...told, ""] : []), ...scoreTable(game, language), "", winnerLine(game, language), ...(save ? ["", t.cliSaved, encodeTrain(game)] : [])];
188
+ return { code: 0, out: `${lines.join("\n")}\n`, err: "" };
189
+ }
190
+ /** What a game comes to, as data for `--json`. */
191
+ function summary(game, language, saved) {
192
+ return {
193
+ format: 1,
194
+ generator: `domino ${VERSION}`,
195
+ set: game.set,
196
+ players: seatNames(game, language),
197
+ computers: game.computers,
198
+ seed: game.seed,
199
+ options: game.options,
200
+ rounds: game.rounds,
201
+ round: game.round + 1,
202
+ moves: moveCount(game),
203
+ phase: game.phase,
204
+ toPlay: game.phase === TRAIN_PHASES.playing ? game.toPlay : null,
205
+ results: game.results.map((result) => ({ hub: result.engine, pips: result.pips, ending: result.ending, out: result.out })),
206
+ totals: trainTotals(game),
207
+ winners: game.winners,
208
+ ...(saved ? { saved: encodeTrain(game) } : {}),
209
+ };
210
+ }
211
+ /** Where a kept game stands. */
212
+ function checkOut(game, language, json) {
213
+ const t = DOMINO_STRINGS[language];
214
+ if (json)
215
+ return { code: 0, out: `${JSON.stringify(summary(game, language, false), null, 2)}\n`, err: "" };
216
+ const where = game.phase === TRAIN_PHASES.finished
217
+ ? t.cliPhaseFinished
218
+ : dominoSay(game.phase === TRAIN_PHASES.roundOver ? t.cliPhaseRoundOver : t.cliPhasePlaying, { n: game.round + 1, total: game.rounds, who: trainSeatName(game, game.toPlay, language) });
219
+ const lines = [dominoSay(t.cliPlayed, { set: trainSetLabel(game.set, language) ?? game.set, players: game.players.length, seed: game.seed, rounds: game.rounds, moves: moveCount(game) }), where];
220
+ if (game.results.length > 0)
221
+ lines.push("", ...scoreTable(game, language));
222
+ if (game.phase === TRAIN_PHASES.finished)
223
+ lines.push("", winnerLine(game, language));
224
+ return { code: 0, out: `${lines.join("\n")}\n`, err: "" };
225
+ }
226
+ /** Every move of a kept game, in words, played again from its seed. */
227
+ function replayOut(kept, language) {
228
+ const t = DOMINO_STRINGS[language];
229
+ let game = startTrain(kept.set, kept.players, kept.seed, kept.options, kept.computers);
230
+ const lines = [];
231
+ for (const move of movesOf(kept)) {
232
+ if (game === null)
233
+ break;
234
+ game = playTrain(game, move);
235
+ if (game === null)
236
+ break;
237
+ lines.push(`${moveCount(game)}. ${trainNews(game, language)}`);
238
+ }
239
+ if (game === null)
240
+ return { code: 1, out: "", err: `domino: ${t.cliSavedBad}\n` };
241
+ lines.push(trainStatus(game, language));
242
+ return { code: 0, out: `${lines.join("\n")}\n`, err: "" };
243
+ }
package/dist/index.d.ts CHANGED
@@ -8,3 +8,8 @@ export * from "./mexicanTrain/trainCodec.ts";
8
8
  export { seededRandom, shuffled } from "./random.ts";
9
9
  export type { Random } from "./random.ts";
10
10
  export { VERSION } from "./version.ts";
11
+ export { DOMINO_STRINGS, dominoLanguage, dominoSay } from "./strings.ts";
12
+ export type { DominoStrings, Language } from "./strings.ts";
13
+ export { trainName, trainNews, trainSeatName, trainSetLabel, trainStatus } from "./words.ts";
14
+ export { CLI_MOVES_MOST, cliLanguage, runCli } from "./cli.ts";
15
+ export type { CliResult, CliSurroundings } from "./cli.ts";
package/dist/index.js CHANGED
@@ -6,3 +6,6 @@ export * from "./mexicanTrain/trainComputer.js";
6
6
  export * from "./mexicanTrain/trainCodec.js";
7
7
  export { seededRandom, shuffled } from "./random.js";
8
8
  export { VERSION } from "./version.js";
9
+ export { DOMINO_STRINGS, dominoLanguage, dominoSay } from "./strings.js";
10
+ export { trainName, trainNews, trainSeatName, trainSetLabel, trainStatus } from "./words.js";
11
+ export { CLI_MOVES_MOST, cliLanguage, runCli } from "./cli.js";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The tile sounds, recorded: tiles shuffled, drawn, laid and knocked on the
3
+ * table, as base64 AAC (.m4a). From Kenney's Casino Audio
4
+ * pack, CC0; see docs/credits.md. Written by scripts/sounds.mjs from the files
5
+ * in ./sounds, never by hand. `createTileSounds` loads this module only when
6
+ * a sound is first played, so a page that stays silent never downloads it.
7
+ */
8
+ export declare const TILE_SOUND_DATA: Readonly<Record<TileSoundFile, string>>;
9
+ /** The name of one recording: its kind of sound and a number, such as `lay-2`. */
10
+ export type TileSoundFile = "draw-1" | "draw-2" | "knock-1" | "knock-2" | "lay-1" | "lay-2" | "lay-3" | "shuffle-1" | "shuffle-2";
package/dist/sounds.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The tile sounds, recorded: tiles shuffled, drawn, laid and knocked on the
3
+ * table, as base64 AAC (.m4a). From Kenney's Casino Audio
4
+ * pack, CC0; see docs/credits.md. Written by scripts/sounds.mjs from the files
5
+ * in ./sounds, never by hand. `createTileSounds` loads this module only when
6
+ * a sound is first played, so a page that stays silent never downloads it.
7
+ */
8
+ export const TILE_SOUND_DATA = {
9
+ "draw-1": "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",
10
+ "draw-2": "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",
11
+ "knock-1": "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",
12
+ "knock-2": "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",
13
+ "lay-1": "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",
14
+ "lay-2": "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",
15
+ "lay-3": "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",
16
+ "shuffle-1": "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",
17
+ "shuffle-2": "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",
18
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Every word Domino says to a person, in English and Japanese: what a table
3
+ * says (whose turn it is, what was laid, how the round ended) and what the
4
+ * command line says. One table, so the two languages are kept side by side
5
+ * and a test can hold them together. `{n}`, `{who}` and the other braces are
6
+ * filled in with `dominoSay`; a table of your own must keep them.
7
+ */
8
+ /** The languages Domino speaks. */
9
+ export type Language = "en" | "ja";
10
+ /** The names of the strings. Each is one line or one block of text. */
11
+ export type DominoStrings = {
12
+ /** The set's names, by its highest double. */
13
+ set9: string;
14
+ set12: string;
15
+ set15: string;
16
+ /** The house rules' names, each with the way it is chosen. */
17
+ lengthFull: string;
18
+ lengthShort: string;
19
+ doublesOne: string;
20
+ doublesChain: string;
21
+ mexicanAny: string;
22
+ mexicanOwnFirst: string;
23
+ /** A seat's default name. */
24
+ you: string;
25
+ computer: string;
26
+ player: string;
27
+ /** The trains. */
28
+ mexican: string;
29
+ trainYours: string;
30
+ trainOf: string;
31
+ open: string;
32
+ closed: string;
33
+ tilesLeft: string;
34
+ boneyard: string;
35
+ round: string;
36
+ needs: string;
37
+ hub: string;
38
+ total: string;
39
+ pips: string;
40
+ /** What to press. */
41
+ layHere: string;
42
+ draw: string;
43
+ pass: string;
44
+ nextRound: string;
45
+ again: string;
46
+ /** The status line: what is wanted of a person, or who is to play. */
47
+ yourTurn: string;
48
+ yourTurnCover: string;
49
+ yourTurnDraw: string;
50
+ yourTurnPass: string;
51
+ yourTurnChoose: string;
52
+ thinking: string;
53
+ roundOver: string;
54
+ outBy: string;
55
+ blocked: string;
56
+ gameOver: string;
57
+ /** What just happened. */
58
+ laid: string;
59
+ drew: string;
60
+ passed: string;
61
+ dealt: string;
62
+ /** The command line's help, whole. */
63
+ cliUsage: string;
64
+ cliUnknown: string;
65
+ cliNeeds: string;
66
+ cliTryHelp: string;
67
+ cliLangBad: string;
68
+ cliNoCommand: string;
69
+ cliCommandBad: string;
70
+ cliSetBad: string;
71
+ cliPlayersBad: string;
72
+ cliSeedBad: string;
73
+ cliValueBad: string;
74
+ cliNoSaved: string;
75
+ cliSavedBad: string;
76
+ cliFresh: string;
77
+ cliDeal: string;
78
+ cliSeat: string;
79
+ cliBoneyard: string;
80
+ cliPlayed: string;
81
+ cliWinner: string;
82
+ cliWinners: string;
83
+ cliSaved: string;
84
+ cliPhasePlaying: string;
85
+ cliPhaseRoundOver: string;
86
+ cliPhaseFinished: string;
87
+ };
88
+ /** Every string, in both languages. The Japanese has not yet been read by a native reader. */
89
+ export declare const DOMINO_STRINGS: Record<Language, DominoStrings>;
90
+ /** A string with its braces filled in from `values`; a brace with no value is left as it is. */
91
+ export declare function dominoSay(text: string, values?: Readonly<Record<string, string | number>>): string;
92
+ /** The language a person asked for, by `en` or `ja` or a tag that begins with one: English for anything else. */
93
+ export declare function dominoLanguage(tag: string | undefined): Language;