diff --git a/packages/server/src/commit.ts b/packages/server/src/commit.ts index 76ea5d1..aeaf6a5 100644 --- a/packages/server/src/commit.ts +++ b/packages/server/src/commit.ts @@ -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] }; } diff --git a/packages/server/src/translator.ts b/packages/server/src/translator.ts index 54e063e..f5e7ab8 100644 --- a/packages/server/src/translator.ts +++ b/packages/server/src/translator.ts @@ -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. diff --git a/packages/server/test/unit/commit-fsm.test.ts b/packages/server/test/unit/commit-fsm.test.ts index d8a9421..42a330e 100644 --- a/packages/server/test/unit/commit-fsm.test.ts +++ b/packages/server/test/unit/commit-fsm.test.ts @@ -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', () => {