From 86f861f6518a1a873d44c4b1f7fc7a8f6eb3bd2e Mon Sep 17 00:00:00 2001 From: Eric Wagoner Date: Mon, 26 Oct 2026 19:52:34 -0400 Subject: [PATCH] The checkmate scene has one mate: a Scarlet Thief watches d1, so the Oliphant that goes there gives check and dies for it Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01CCLa2PaEeCj8njQZSWGqHx --- src/lib/game/lessons.test.ts | 15 ++++++++++++--- src/lib/game/lessons.ts | 8 ++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/lib/game/lessons.test.ts b/src/lib/game/lessons.test.ts index 22eefad..bb89171 100644 --- a/src/lib/game/lessons.test.ts +++ b/src/lib/game/lessons.test.ts @@ -52,10 +52,19 @@ describe('the lessons', () => { const king = sceneCells(at('king')); expect(inCheck(king, 'gold')).toBe(true); expect(legalMovesFrom(king, square('2g1'), 'printed').map((i) => i).sort()).toEqual([square('1g1'), square('2f1'), square('2h1'), square('3g1')].sort()); - // The last scene is mate in one, and only that move mates. + // The last scene is mate in one by either listed move, and by no other. const mate = play('mate'); - expect(inCheck(mate.after.cells, 'scarlet')).toBe(true); - expect(hasLegalMove(mate.after.cells, 'scarlet', 'printed')).toBe(false); + for (const [from, to] of mate.s.moves) { + const after = playOn(mate.cells, square(from), square(to)).cells; + expect(inCheck(after, 'scarlet'), `${from} to ${to}`).toBe(true); + expect(hasLegalMove(after, 'scarlet', 'printed'), `${from} to ${to}`).toBe(false); + } + const listed = new Set(mate.s.moves.map(([a, b]) => `${square(a)}-${square(b)}`)); + for (const to of legalMovesFrom(mate.cells, square('2a1'), 'printed')) { + if (listed.has(`${square('2a1')}-${to}`)) continue; + const after = playOn(mate.cells, square('2a1'), to).cells; + expect(inCheck(after, 'scarlet') && !hasLegalMove(after, 'scarlet', 'printed'), `unlisted mate by the Oliphant to ${to}`).toBe(false); + } expect(kindOf(mate.cells[square('2d4')])).toBe('K'); expect(frozen(mate.cells, square('2d4'))).toBe(true); void other; diff --git a/src/lib/game/lessons.ts b/src/lib/game/lessons.ts index 9c37314..b1aee60 100644 --- a/src/lib/game/lessons.ts +++ b/src/lib/game/lessons.ts @@ -211,14 +211,14 @@ export const SCENES: Scene[] = [ { id: 'mate', title: 'Checkmate', - pieces: { '2d4': 'sK', '1d4': 'gB', '2a1': 'gO', '2g1': 'gK' }, + pieces: { '2d4': 'sK', '1d4': 'gB', '2a1': 'gO', '2g1': 'gK', '2f3': 'sT' }, side: 'gold', levels: [2, 1], - before: 'Your Basilisk sits beneath the Scarlet King at 2d4, so the King is frozen and cannot step out of anything. Gygax wrote that a frozen King is checkmated by any attack it cannot capture or block. Your Oliphant stands on the a file with the fourth rank empty between it and the King.', + before: 'Your Basilisk sits beneath the Scarlet King at 2d4, so the King is frozen and cannot step out of anything. Gygax wrote that a frozen King is checkmated by any attack it cannot capture or block. Your Oliphant stands in the corner, and two open lines lead to the King, along the fourth rank and up the d file. But a Scarlet Thief at 2f3 watches 2d1, and an attacker that can be taken gives no mate.', task: 'Give checkmate. Tap the Oliphant, then 2a4.', moves: [['2a1', '2a4']], - after: 'Checkmate. The King is attacked along the rank, it cannot move, nothing of Scarlet’s can take the Oliphant, and nothing can step between. That is the Basilisk’s other gift. A well-timed move beneath a King, or beneath the piece that guards it, ends a game.', - aside: 'A legal move, but the Oliphant to 2a4 is the mate. Set the scene again and look for it.' + after: 'Checkmate. The King is attacked along the rank, it cannot move, the Thief can neither take the Oliphant on a4 nor step between, and nothing else of Scarlet’s can either. On d1 the Oliphant would have given check and died for it. That is the Basilisk’s other gift. A well-timed move beneath a King, or beneath the piece that guards it, ends a game.', + aside: 'A legal move, but not the mate. The Thief at 2f3 covers 2d1, so set the scene again and look for the line it does not cover.' } ];