feat(server): moderator announces every move and attempt to both players

All move-event announcements in translator.ts and all attempted-move
announcements in commit.ts now use audience 'both' so the moderator
panel is a complete shared transcript for both players.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
claude (blind_chess)
2026-05-18 19:54:34 -04:00
parent be8ecd96b6
commit 41b3ab93bb
3 changed files with 28 additions and 13 deletions
+6 -6
View File
@@ -37,7 +37,7 @@ export function handleCommit(game: Game, color: Color, msg: CommitInput): Commit
const piece = game.chess.get(msg.from) as { color: Color; type: Piece['type'] } | false;
if (!piece || piece.color !== color) {
return announceWith(game, 'no_such_piece', color);
return announceWith(game, 'no_such_piece');
}
const pseudo = geometricMoves(
@@ -46,12 +46,12 @@ export function handleCommit(game: Game, color: Color, msg: CommitInput): Commit
ownSquares(game, color),
);
if (pseudo.length === 0) {
return announceWith(game, 'no_legal_moves', color);
return announceWith(game, 'no_legal_moves');
}
const legal = chessJsLegalFrom(game, msg.from);
if (legal.length === 0) {
return announceWith(game, 'wont_help', color);
return announceWith(game, 'wont_help');
}
game.armed = { color, from: msg.from };
@@ -77,7 +77,7 @@ function tryMove(
}
if (!move) {
return announceWith(game, 'illegal_move', color);
return announceWith(game, 'illegal_move');
}
game.armed = null;
@@ -110,10 +110,10 @@ function tryMove(
function announceWith(
game: Game,
text: 'no_such_piece' | 'no_legal_moves' | 'wont_help' | 'illegal_move',
color: Color,
): CommitResult {
const ply = game.chess.history().length;
const a = announce(text, color, ply);
// Feature 1: attempted moves are announced to both players.
const a = announce(text, 'both', ply);
game.announcements.push(a);
return { kind: 'announce', announcements: [a] };
}
+8 -7
View File
@@ -33,21 +33,22 @@ export function translateMove(game: Game, move: Move): Announcement[] {
const isQueensideCastle = move.isQueensideCastle();
const isProm = !!move.promotion;
// To opponent: the move event itself.
// To both players: the move event itself (Feature 1 — the moderator
// announces every move aloud; both players hear it).
if (isKingsideCastle) {
out.push(announce(`${moverWord}_castled_kingside` as ModeratorText, opp, ply));
out.push(announce(`${moverWord}_castled_kingside` as ModeratorText, 'both', ply));
} else if (isQueensideCastle) {
out.push(announce(`${moverWord}_castled_queenside` as ModeratorText, opp, ply));
out.push(announce(`${moverWord}_castled_queenside` as ModeratorText, 'both', ply));
} else if (isCap && isEp) {
out.push(announce(`${moverWord}_moved_captured_ep` as ModeratorText, opp, ply));
out.push(announce(`${moverWord}_moved_captured_ep` as ModeratorText, 'both', ply));
} else if (isCap) {
out.push(announce(`${moverWord}_moved_captured` as ModeratorText, opp, ply));
out.push(announce(`${moverWord}_moved_captured` as ModeratorText, 'both', ply));
} else {
out.push(announce(`${moverWord}_moved` as ModeratorText, opp, ply));
out.push(announce(`${moverWord}_moved` as ModeratorText, 'both', ply));
}
if (isProm) {
out.push(announce(`${moverWord}_promoted` as ModeratorText, opp, ply, { promotedTo: move.promotion }));
out.push(announce(`${moverWord}_promoted` as ModeratorText, 'both', ply, { promotedTo: move.promotion }));
}
// To both: state changes.