chesspal 0.2.2 → 0.2.4
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/commands/agents.js +2 -1
- package/dist/commands/ai-say.d.ts +2 -1
- package/dist/commands/analyse-game.d.ts +2 -0
- package/dist/commands/analyse-game.js +7 -4
- package/dist/commands/annotate-game.d.ts +2 -0
- package/dist/commands/annotate-game.js +26 -8
- package/dist/commands/answer-lines.d.ts +8 -4
- package/dist/commands/answer-lines.js +19 -3
- package/dist/commands/coach.js +2 -2
- package/dist/commands/context.d.ts +2 -0
- package/dist/commands/delete-review.js +3 -2
- package/dist/commands/game-summary.d.ts +7 -2
- package/dist/commands/game-summary.js +7 -0
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.js +1 -1
- package/dist/commands/look.d.ts +9 -5
- package/dist/commands/look.js +19 -9
- package/dist/commands/ponder.js +1 -1
- package/dist/commands/position.js +2 -2
- package/dist/commands/say.d.ts +11 -6
- package/dist/commands/say.js +21 -13
- package/dist/commands/status.d.ts +3 -1
- package/dist/commands/status.js +31 -6
- package/dist/commands/study.d.ts +11 -1
- package/dist/commands/study.js +31 -2
- package/dist/commands/view.d.ts +17 -5
- package/dist/commands/view.js +67 -10
- package/dist/core/facts.d.ts +27 -2
- package/dist/core/facts.js +59 -8
- package/dist/core/game.d.ts +5 -0
- package/dist/core/game.js +9 -0
- package/dist/core/index.d.ts +2 -1
- package/dist/core/placement.d.ts +63 -0
- package/dist/core/placement.js +259 -0
- package/dist/core/possibilities.d.ts +38 -0
- package/dist/core/possibilities.js +254 -0
- package/dist/core/review.d.ts +4 -1
- package/dist/core/review.js +17 -6
- package/dist/games/analysis.d.ts +8 -4
- package/dist/games/analysis.js +9 -5
- package/dist/games/current-game.d.ts +16 -1
- package/dist/games/current-game.js +10 -8
- package/dist/games/files.d.ts +2 -0
- package/dist/games/files.js +7 -1
- package/dist/games/index.d.ts +2 -2
- package/dist/games/index.js +1 -1
- package/dist/games/table-talk.d.ts +2 -0
- package/dist/games/table-talk.js +5 -1
- package/dist/games/talk.d.ts +4 -3
- package/dist/games/talk.js +3 -3
- package/dist/page/app/app.js +36 -12
- package/dist/page/app/keys.js +16 -2
- package/dist/page/app/served.js +2 -2
- package/dist/page/board/board.js +10 -4
- package/dist/page/board/marks.js +53 -31
- package/dist/page/board/signs.js +23 -0
- package/dist/page/game/column.js +2 -2
- package/dist/page/game/game.css +12 -0
- package/dist/page/game/move-list.js +2 -2
- package/dist/page/game/plies.js +8 -0
- package/dist/page/helps/helps.css +87 -0
- package/dist/page/helps/helps.js +18 -13
- package/dist/page/helps/view.js +19 -5
- package/dist/page/layout/layout.css +29 -5
- package/dist/page/legend/legend.css +203 -0
- package/dist/page/legend/legend.js +169 -0
- package/dist/page/legend/view.js +54 -0
- package/dist/page/style.css +2 -0
- package/dist/page/talk/chat.css +90 -0
- package/dist/page/talk/talk.js +27 -5
- package/dist/page/text.js +46 -6
- package/dist/server/mcp.js +8 -6
- package/dist/server/page/game.js +20 -7
- package/dist/server/page/page.js +5 -3
- package/instructions/opponent.md +1 -1
- package/package.json +1 -1
package/dist/page/text.js
CHANGED
|
@@ -64,7 +64,7 @@ const english = {
|
|
|
64
64
|
noGame: "No game yet: press + to start one, or ask for one in the chat.",
|
|
65
65
|
foldCard: "Fold the card",
|
|
66
66
|
openCard: "Open the card",
|
|
67
|
-
saveMoves: "
|
|
67
|
+
saveMoves: "PGN",
|
|
68
68
|
saveMovesHint: "The moves alone, to open on Lichess or chess.com",
|
|
69
69
|
noGameNow: "No game in progress",
|
|
70
70
|
analysing: "Analysis in progress",
|
|
@@ -103,6 +103,8 @@ const english = {
|
|
|
103
103
|
send: "Send",
|
|
104
104
|
sent: "Sent",
|
|
105
105
|
withMove: "Or move: your words go with it",
|
|
106
|
+
onMove: "On",
|
|
107
|
+
onWholeGame: "On the whole game",
|
|
106
108
|
talk: "The talk",
|
|
107
109
|
move: "Move",
|
|
108
110
|
draw: "draw",
|
|
@@ -129,6 +131,7 @@ const english = {
|
|
|
129
131
|
startClocks: "Start",
|
|
130
132
|
thinking: { move: "Thinking about its move", ponder: "Thinking while you play" },
|
|
131
133
|
asleep: (name) => `${name} is not listening: write a message, such as "hi", in the window where you opened ${name}, not here. Its clock waits until it answers.`,
|
|
134
|
+
asleepInAnalysis: (name) => `${name} is not listening: write to it in the window where you opened it, not here.`,
|
|
132
135
|
commandHere: (name) => `/clear and the other commands go in the window where you opened ${name}, not here.`,
|
|
133
136
|
refused: {
|
|
134
137
|
"no-game": () => "There is no game going on: press + to start one, or ask for one in the chat.",
|
|
@@ -149,6 +152,8 @@ const english = {
|
|
|
149
152
|
piece: (letter, side) => `${side} ${englishNames[letter]}`,
|
|
150
153
|
taken: (pieces, ahead) => `Pieces taken: ${pieces.length === 0 ? "none" : pieces.join(", ")}${ahead > 0 ? `; ahead by ${ahead}` : ""}`,
|
|
151
154
|
writeTo: (name) => `Write to ${name}…`,
|
|
155
|
+
askAbout: (name) => `Ask ${name} about this game…`,
|
|
156
|
+
goTo: (move) => `Go to ${move}`,
|
|
152
157
|
readBy: (name) => `Read by ${name}`,
|
|
153
158
|
wrote: (name) => `${name} wrote`,
|
|
154
159
|
offers: "offers a draw",
|
|
@@ -274,16 +279,18 @@ const english = {
|
|
|
274
279
|
outnumbered: "more attackers than defenders",
|
|
275
280
|
pinnedTo: (letter) => `pinned to the ${englishNames[letter]}`,
|
|
276
281
|
pinnedBy: (letter) => `by the ${englishNames[letter]}`,
|
|
282
|
+
skeweredBefore: (letter) => `ahead of the ${englishNames[letter]}`,
|
|
277
283
|
nothingAtRisk: "No piece at risk, no pins.",
|
|
278
284
|
line: (kind, side, you, toMove) => {
|
|
279
285
|
const name = side === "white" ? "White" : "Black";
|
|
286
|
+
const pieces = { risk: "pieces at risk", pin: "pinned pieces", skewer: "skewered pieces" };
|
|
280
287
|
if (you === undefined) {
|
|
281
|
-
return kind === "
|
|
288
|
+
return kind === "check" ? `${name}'s checks${toMove ? "" : `, if it were ${name}'s move`}` : `${name}'s ${pieces[kind]}`;
|
|
282
289
|
}
|
|
283
290
|
if (kind === "check") {
|
|
284
291
|
return side === you ? `Checks you ${toMove ? "can" : "could"} give` : `Checks the opponent ${toMove ? "can" : "could"} give you`;
|
|
285
292
|
}
|
|
286
|
-
return `${side === you ? "Your" : "The opponent's"} ${kind
|
|
293
|
+
return `${side === you ? "Your" : "The opponent's"} ${pieces[kind]}`;
|
|
287
294
|
},
|
|
288
295
|
loose: "Pieces neither attacked nor defended",
|
|
289
296
|
yours: "yours",
|
|
@@ -323,6 +330,19 @@ const english = {
|
|
|
323
330
|
pawnSquare: (pawn, caught) => `The enemy king is ${caught ? "inside" : "outside"} the square of the pawn on ${pawn}`,
|
|
324
331
|
opposition: (side) => `${side === "white" ? "White" : "Black"} has the opposition`,
|
|
325
332
|
},
|
|
333
|
+
legend: {
|
|
334
|
+
title: "Marks on the board",
|
|
335
|
+
hint: "Marks on the board (?)",
|
|
336
|
+
none: "No marks on the board now",
|
|
337
|
+
badge: "the move's judgment",
|
|
338
|
+
sign: (kind, tone) => `${tone === "threat" ? "Your" : tone === "chance" ? "Opponent's" : "A"} ${{ risk: "piece at risk", pin: "pinned piece", skewer: "skewered piece" }[kind]}`,
|
|
339
|
+
pinLine: "The pin's line",
|
|
340
|
+
skewerLine: "The skewer's line",
|
|
341
|
+
drawnBy: (name) => `drawn by ${name}`,
|
|
342
|
+
drawnByCoach: "drawn by the coach",
|
|
343
|
+
control: "How many of White's pieces and of Black's reach the square",
|
|
344
|
+
ends: { crown: "Won", mate: "Checkmated", flag: "Resigned", half: "Draw, half a point each" },
|
|
345
|
+
},
|
|
326
346
|
};
|
|
327
347
|
const italian = {
|
|
328
348
|
language: "it",
|
|
@@ -383,7 +403,7 @@ const italian = {
|
|
|
383
403
|
noGame: "Nessuna partita: premi + per iniziarne una, o chiedila nella chat.",
|
|
384
404
|
foldCard: "Chiudi la scheda",
|
|
385
405
|
openCard: "Apri la scheda",
|
|
386
|
-
saveMoves: "PGN
|
|
406
|
+
saveMoves: "PGN",
|
|
387
407
|
saveMovesHint: "Solo le mosse, da aprire su Lichess o chess.com",
|
|
388
408
|
noGameNow: "Nessuna partita in corso",
|
|
389
409
|
analysing: "Analisi in corso",
|
|
@@ -422,6 +442,8 @@ const italian = {
|
|
|
422
442
|
send: "Manda",
|
|
423
443
|
sent: "Mandato",
|
|
424
444
|
withMove: "Oppure muovi: parte con la mossa",
|
|
445
|
+
onMove: "Su",
|
|
446
|
+
onWholeGame: "Su tutta la partita",
|
|
425
447
|
talk: "Conversazione",
|
|
426
448
|
move: "Mossa",
|
|
427
449
|
draw: "patta",
|
|
@@ -448,6 +470,7 @@ const italian = {
|
|
|
448
470
|
startClocks: "Inizia",
|
|
449
471
|
thinking: { move: "Sta pensando alla sua mossa", ponder: "Pensa mentre giochi tu" },
|
|
450
472
|
asleep: (name) => `${name} non ti sta ascoltando: scrivi un messaggio, per esempio "ciao", nella finestra dove hai aperto ${name}, non qui. Il suo orologio aspetta che risponda.`,
|
|
473
|
+
asleepInAnalysis: (name) => `${name} non ti sta ascoltando: scrivigli nella finestra dove l'hai aperto, non qui.`,
|
|
451
474
|
commandHere: (name) => `/clear e gli altri comandi vanno scritti nella finestra dove hai aperto ${name}, non qui.`,
|
|
452
475
|
refused: {
|
|
453
476
|
"no-game": () => "Nessuna partita in corso: premi + per iniziarne una, o chiedila nella chat.",
|
|
@@ -469,6 +492,8 @@ const italian = {
|
|
|
469
492
|
piece: (letter, side) => `${italianNames[letter]} ${side === "white" ? (letter === "Q" || letter === "R" ? "bianca" : "bianco") : (letter === "Q" || letter === "R" ? "nera" : "nero")}`,
|
|
470
493
|
taken: (pieces, ahead) => `Pezzi presi: ${pieces.length === 0 ? "nessuno" : pieces.join(", ")}${ahead > 0 ? `; avanti di ${ahead}` : ""}`,
|
|
471
494
|
writeTo: (name) => `Scrivi a ${name}…`,
|
|
495
|
+
askAbout: (name) => `Chiedi a ${name} di questa partita…`,
|
|
496
|
+
goTo: (move) => `Vai a ${move}`,
|
|
472
497
|
readBy: (name) => `Letto da ${name}`,
|
|
473
498
|
wrote: (name) => `${name} ha scritto`,
|
|
474
499
|
offers: "propone patta",
|
|
@@ -594,16 +619,18 @@ const italian = {
|
|
|
594
619
|
outnumbered: "più attaccanti che difensori",
|
|
595
620
|
pinnedTo: (letter) => `davanti ${italianWith("a", letter)}`,
|
|
596
621
|
pinnedBy: (letter) => italianWith("da", letter),
|
|
622
|
+
skeweredBefore: (letter) => `con dietro ${italianThe[letter]}`,
|
|
597
623
|
nothingAtRisk: "Nessun pezzo in pericolo, nessuna inchiodatura.",
|
|
598
624
|
line: (kind, side, you, toMove) => {
|
|
599
625
|
const name = side === "white" ? "Bianco" : "Nero";
|
|
626
|
+
const what = { risk: "in pericolo", pin: "inchiodati", skewer: "infilati" };
|
|
600
627
|
if (you === undefined) {
|
|
601
|
-
return kind === "
|
|
628
|
+
return kind === "check" ? `Scacchi del ${name}${toMove ? "" : ", se toccasse a lui"}` : `Pezzi del ${name} ${what[kind]}`;
|
|
602
629
|
}
|
|
603
630
|
if (kind === "check") {
|
|
604
631
|
return side === you ? `Scacchi che ${toMove ? "puoi" : "potresti"} dare` : `Scacchi che l'avversario ${toMove ? "può" : "potrebbe"} darti`;
|
|
605
632
|
}
|
|
606
|
-
return `${side === you ? "I tuoi pezzi" : "I pezzi avversari"} ${kind
|
|
633
|
+
return `${side === you ? "I tuoi pezzi" : "I pezzi avversari"} ${what[kind]}`;
|
|
607
634
|
},
|
|
608
635
|
loose: "Pezzi né attaccati né difesi",
|
|
609
636
|
yours: "tuoi",
|
|
@@ -643,6 +670,19 @@ const italian = {
|
|
|
643
670
|
pawnSquare: (pawn, caught) => `Il re nemico è ${caught ? "dentro il" : "fuori dal"} quadrato del pedone in ${pawn}`,
|
|
644
671
|
opposition: (side) => `L'opposizione è del ${side === "white" ? "Bianco" : "Nero"}`,
|
|
645
672
|
},
|
|
673
|
+
legend: {
|
|
674
|
+
title: "Segni sulla scacchiera",
|
|
675
|
+
hint: "Segni sulla scacchiera (?)",
|
|
676
|
+
none: "Nessun segno sulla scacchiera ora",
|
|
677
|
+
badge: "il giudizio della mossa",
|
|
678
|
+
sign: (kind, tone) => `${tone === "threat" ? "Tuo pezzo" : tone === "chance" ? "Pezzo avversario" : "Pezzo"} ${{ risk: "in pericolo", pin: "inchiodato", skewer: "infilato" }[kind]}`,
|
|
679
|
+
pinLine: "Linea dell'inchiodatura",
|
|
680
|
+
skewerLine: "Linea dell'infilata",
|
|
681
|
+
drawnBy: (name) => `disegnato da ${name}`,
|
|
682
|
+
drawnByCoach: "disegnato dall'allenatore",
|
|
683
|
+
control: "Quanti pezzi del Bianco e del Nero raggiungono la casa",
|
|
684
|
+
ends: { crown: "Ha vinto", mate: "Scacco matto", flag: "Ha abbandonato", half: "Patta, mezzo punto a testa" },
|
|
685
|
+
},
|
|
646
686
|
};
|
|
647
687
|
// How each piece moves, said when it was taken where it cannot go (Anatolij's words).
|
|
648
688
|
const englishMoves = {
|
package/dist/server/mcp.js
CHANGED
|
@@ -154,14 +154,16 @@ function toolConfig(command) {
|
|
|
154
154
|
return { description: command.description, inputSchema: z.looseObject({}).meta({ ...shownInput(command), $schema: undefined }), annotations, _meta: meta };
|
|
155
155
|
}
|
|
156
156
|
async function runTool(command, input, context, caller, meta) {
|
|
157
|
-
const
|
|
157
|
+
const calling = await sharingCaller(command, meta, context);
|
|
158
|
+
const agent = calling !== undefined && "agent" in calling ? calling.agent : undefined;
|
|
158
159
|
const chat = command.byAgents === undefined && agent === undefined;
|
|
159
160
|
let refused = false;
|
|
160
161
|
try {
|
|
161
162
|
if (chat) {
|
|
162
163
|
context.calls?.();
|
|
163
164
|
}
|
|
164
|
-
const
|
|
165
|
+
const who = agent !== undefined ? { agent } : calling !== undefined ? { chat: true } : {};
|
|
166
|
+
const result = await command.run(checkedInput(command, input), { ...context, caller, ...who });
|
|
165
167
|
return { content: [{ type: "text", text: command.format(result, context) }] };
|
|
166
168
|
}
|
|
167
169
|
catch (error) {
|
|
@@ -177,16 +179,16 @@ async function runTool(command, input, context, caller, meta) {
|
|
|
177
179
|
}
|
|
178
180
|
}
|
|
179
181
|
/**
|
|
180
|
-
*
|
|
181
|
-
* goes on, so that no call of the chat's waits for it while it plays
|
|
182
|
+
* Who calls a tool the chat shares with an agent, as the analyst (D-89): the chat, or the agent by its type; looked up only in a folder where one may run
|
|
183
|
+
* and while no game goes on, so that no call of the chat's waits for it while it plays. None when it cannot be told, or for an agent the tool is not shared with.
|
|
182
184
|
*/
|
|
183
|
-
async function
|
|
185
|
+
async function sharingCaller(command, meta, context) {
|
|
184
186
|
const agents = command.sharedWith?.filter((agent) => hasAgent(context.folder, agent, context.host?.())) ?? [];
|
|
185
187
|
if (agents.length === 0 || gameGoingOn(context)) {
|
|
186
188
|
return undefined;
|
|
187
189
|
}
|
|
188
190
|
const caller = await callingAgent(meta, context.folder);
|
|
189
|
-
return caller !== undefined && "agent" in caller && agents.includes(caller.agent) ?
|
|
191
|
+
return caller !== undefined && "agent" in caller && !agents.includes(caller.agent) ? undefined : caller;
|
|
190
192
|
}
|
|
191
193
|
/** Whether a game goes on, or may: a game that cannot be read is the command's to report, as the chat's call (Federico). */
|
|
192
194
|
function gameGoingOn(context) {
|
package/dist/server/page/game.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { analyseGame, answerDraw, bookPlies, ChatNotClear, DrawRefused, endGame, gameAt, gameToStudy, namedGame, newGame, offerDraw, playMove, samePosition, say, startNewGame, } from "../../commands/index.js";
|
|
1
|
+
import { analyseGame, answerDraw, bookPlies, ChatNotClear, DrawRefused, endGame, gameAt, gameToStudy, namedGame, newGame, offerDraw, playMove, samePosition, say, shownPosition, startNewGame, } from "../../commands/index.js";
|
|
2
2
|
import { clockStarted, isReviewed, writePgn } from "../../core/index.js";
|
|
3
3
|
import { aiModelOf, archiveName, coachShown, coachWords, drawOfferOf, endOf, gameInAnalysis, heardLines, loadGame, loadTableTalk, playerPgn, ponderingOn, saveGame, startAnalysis, talkLimits, thoughtKey, wakesTheChat, WordsRefused, } from "../../games/index.js";
|
|
4
4
|
import { gameFolder, helpsIn } from "../../profiles/index.js";
|
|
@@ -72,28 +72,34 @@ async function moveFromPage(request, response, served) {
|
|
|
72
72
|
served.bell.ring();
|
|
73
73
|
send(response, 200, json, JSON.stringify({ played: true }));
|
|
74
74
|
}
|
|
75
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* The player's words from the page, without a move: saved with the game as player_say saves them, refused in the player's words, the doorbell rung for the
|
|
77
|
+
* first; in analysis with the position the page shows, and the doorbell rung for each (D-101).
|
|
78
|
+
*/
|
|
76
79
|
async function messageFromPage(request, response, served) {
|
|
77
80
|
if (!isJson(request)) {
|
|
78
81
|
send(response, 415, json, JSON.stringify({ error: 'Send the message as JSON: {"text": "…"}.' }));
|
|
79
82
|
return;
|
|
80
83
|
}
|
|
81
|
-
const
|
|
84
|
+
const body = jsonOf(await bodyOf(request));
|
|
85
|
+
const text = textOf(body?.text);
|
|
86
|
+
const shown = typeof body?.game === "string" && typeof body.at === "number" ? shownPosition(served.context, body.game, body.at) : undefined;
|
|
82
87
|
if (text === undefined) {
|
|
83
88
|
send(response, 400, json, JSON.stringify({ error: `Send the message as JSON, ${talkLimits.player} characters at most: {"text": "…"}.` }));
|
|
84
89
|
return;
|
|
85
90
|
}
|
|
86
91
|
let current;
|
|
87
92
|
try {
|
|
88
|
-
current = say(gameFolder(served.context.home()), "player", text, served.context.now());
|
|
93
|
+
current = say(gameFolder(served.context.home()), "player", text, served.context.now(), undefined, shown);
|
|
89
94
|
}
|
|
90
95
|
catch (error) {
|
|
91
96
|
const noGame = refusalOf(served)?.refusal === "no-game";
|
|
92
97
|
send(response, 409, json, JSON.stringify(refusalFrom(error, noGame ? "no-game" : undefined)));
|
|
93
98
|
return;
|
|
94
99
|
}
|
|
95
|
-
// The first words of a move wake the chat, and between games the first since the AI spoke; the AI finds the rest at its next call (D-54, D-74).
|
|
96
|
-
|
|
100
|
+
// The first words of a move wake the chat, and between games the first since the AI spoke; the AI finds the rest at its next call (D-54, D-74). In
|
|
101
|
+
// analysis every line does: a chat that answered in its own window would not hear the next (Priya).
|
|
102
|
+
if (shown !== undefined || wakesTheChat(current)) {
|
|
97
103
|
served.bell.ring();
|
|
98
104
|
}
|
|
99
105
|
send(response, 200, json, JSON.stringify({ said: true }));
|
|
@@ -244,7 +250,14 @@ function sendGame(response, served) {
|
|
|
244
250
|
// With no game the page still shows the talk before the first (D-77).
|
|
245
251
|
const spoken = current ?? loadTableTalk(folder);
|
|
246
252
|
const heard = heardLines(spoken, served.context.given);
|
|
247
|
-
const talk = spoken.talk.map(({ who, text, ply }, index) =>
|
|
253
|
+
const talk = spoken.talk.map(({ who, text, ply, shown }, index) => ({
|
|
254
|
+
who,
|
|
255
|
+
text,
|
|
256
|
+
ply,
|
|
257
|
+
...(heard[index] === undefined ? {} : { heard: heard[index] }),
|
|
258
|
+
// In analysis the page goes back to the move the player wrote about (D-101).
|
|
259
|
+
...(shown === undefined ? {} : { shown }),
|
|
260
|
+
}));
|
|
248
261
|
const offer = current === undefined ? undefined : drawOfferOf(current);
|
|
249
262
|
const coach = coachOn(folder, current);
|
|
250
263
|
const game = current === undefined ? null : {
|
package/dist/server/page/page.js
CHANGED
|
@@ -4,7 +4,7 @@ import { watch } from "node:fs";
|
|
|
4
4
|
import { createServer } from "node:http";
|
|
5
5
|
import { join } from "node:path";
|
|
6
6
|
import { positionIn, samePosition } from "../../commands/index.js";
|
|
7
|
-
import { gameStamp } from "../../games/index.js";
|
|
7
|
+
import { gameStamp, tableTalkStamp } from "../../games/index.js";
|
|
8
8
|
import { outdatedHostFiles } from "../../host-files/index.js";
|
|
9
9
|
import { gameFolder } from "../../profiles/index.js";
|
|
10
10
|
import { Bell, bellProbe, pagePorts } from "../bell.js";
|
|
@@ -111,12 +111,14 @@ function answer(request, response, served) {
|
|
|
111
111
|
function hasNews(context) {
|
|
112
112
|
return gameSeen(context) !== context.seen;
|
|
113
113
|
}
|
|
114
|
-
// The game the chat sees: the profile's folder, and the
|
|
114
|
+
// The game the chat sees: the profile's folder, and the stamps of its game.json and of the talk with no game, where the player's words go in analysis
|
|
115
|
+
// before any game was played here (Priya); none while the data folder cannot be made.
|
|
115
116
|
function gameSeen(context) {
|
|
116
117
|
try {
|
|
117
118
|
const folder = gameFolder(context.home());
|
|
118
119
|
return `${folder}
|
|
119
|
-
${gameStamp(folder) ?? ""}
|
|
120
|
+
${gameStamp(folder) ?? ""}
|
|
121
|
+
${tableTalkStamp(folder) ?? ""}`;
|
|
120
122
|
}
|
|
121
123
|
catch {
|
|
122
124
|
return "";
|
package/instructions/opponent.md
CHANGED
|
@@ -10,6 +10,6 @@ This folder is the player's chess table, and you are their opponent: you choose
|
|
|
10
10
|
- You have a face on the board page, and it shows how you feel. Give it as the `mood` of `ai_move` on each move where the game turns for you: a piece won or lost, a blunder seen, your own or theirs, a threat that appears or a plan that works or fails. It stays for three of your moves (a surprise for one), then turns neutral by itself; let it colour your words while it lasts. When the game ends on the player's move, give how you feel as it ends with `ai_say`'s `mood`.
|
|
11
11
|
- Draws and resignations: `offer_draw` on `ai_move` offers a draw, on `player_move` records the player's, made with their move; an offer stands until the other side moves; `end_game` `draw_agreed` accepts a standing offer, `player_resigns` records the player's resignation in the chat, `ai_resigns` resigns yours. On the board page the player offers, answers and resigns there.
|
|
12
12
|
- The player chooses a new game in the board page's New game window, which asks the sides, the clock and the level: when they ask you for a game, call `new_game` without choices, which opens it there, and don't ask those in the chat. Ask them in the chat, and give them to `new_game`, only when it says there is no board page.
|
|
13
|
-
- To go over a game with the player once no game goes on, the one just over or one they bring as a PGN, give it to `analyse_game`: it reads the PGN for you; where Chesspal's answers say to start the analyst agent for it, start that instead, once per game
|
|
13
|
+
- To go over a game with the player once no game goes on, the one just over or one they bring as a PGN, give it to `analyse_game`: it reads the PGN for you; where Chesspal's answers say to start the analyst agent for it, start that instead, once per game, and never tell them it starts again, even when they ask for the analysis again. While it works, and after, talk the game over whenever the player asks: `look` and `position` with game and move show you any of its positions. When they show you something the review missed, first correct that move's note with `annotate_game` {game, notes: [{move, badge, note}]}, that move only, then answer. Words from the board page you answer with `ai_say`; words in the chat, in the chat. While a game goes on, go over no game, not even in your head or from a PGN they paste: tell them it waits until this one ends.
|
|
14
14
|
- How to read the facts: attackers and defenders count only the pieces that reach the square directly, and a piece in brackets stands behind them; "Loose" means neither attacked nor defended, not attacked now; "At risk" lists the pieces attacked and undefended, attacked by a cheaper piece, or attacked more times than defended.
|
|
15
15
|
- Talk with the player in their language, in a few words. The facts and the tools are for you: tell the player what a chess player would say, never the lists or the tools' names. Name pieces and moves in words, as people talk at the board ("your bishop took on f7"); when you write a move, yours or the player's, write it with the pieces' figurines, as the tools show it (♞f6 for Nf6, ♝xf7+ for Bxf7+), for both sides and in every language: never with letters, neither N B R Q K nor the player's own (Cf6), even when they write them.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chesspal",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Play chess against an AI with a character, and learn with an AI coach: Chesspal keeps the rules, the board, the games and the memory; the AI plays, reasons and talks.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|