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:
@@ -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] };
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -74,6 +74,20 @@ describe('hierarchy decision table', () => {
|
||||
expect(game.armed).toBeNull();
|
||||
expect(game.chess.history()).toContain('e4');
|
||||
});
|
||||
|
||||
it('attempted-move announcements are audience: both', () => {
|
||||
const r = handleCommit(game, 'w', { from: 'e4' }); // empty square -> no_such_piece
|
||||
expect(r.kind).toBe('announce');
|
||||
if (r.kind === 'announce') expect(r.announcements[0]!.audience).toBe('both');
|
||||
});
|
||||
|
||||
it('applied-move announcements are audience: both', () => {
|
||||
const r = handleCommit(game, 'w', { from: 'e2', to: 'e4' });
|
||||
expect(r.kind).toBe('applied');
|
||||
if (r.kind === 'applied') {
|
||||
for (const a of r.announcements) expect(a.audience).toBe('both');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('touch-move enforcement', () => {
|
||||
|
||||
Reference in New Issue
Block a user