From 83fefbc71af1c2de4a2b184cde86ba6d4ae45bbb Mon Sep 17 00:00:00 2001 From: Cozma Rares Date: Mon, 1 May 2023 02:37:43 +0300 Subject: [PATCH] fix: tests and attacked squares --- server/src/__test__/getMovesForSquare.test.ts | 268 +++++++++++------- server/src/__test__/isSquareAttacked.test.ts | 2 +- server/src/chess/engine.ts | 48 ++-- 3 files changed, 193 insertions(+), 125 deletions(-) diff --git a/server/src/__test__/getMovesForSquare.test.ts b/server/src/__test__/getMovesForSquare.test.ts index 9a2c826..1332109 100644 --- a/server/src/__test__/getMovesForSquare.test.ts +++ b/server/src/__test__/getMovesForSquare.test.ts @@ -28,8 +28,17 @@ function runTest(fen: string, expectedMoves: ExpectedMoves) { ); } +function runTests( + fen: string, + expectedMovesW: ExpectedMoves, + expectedMovesB: ExpectedMoves +) { + runTest(fen.replace("%", "w"), expectedMovesW); + runTest(fen.replace("%", "b"), expectedMovesB); +} + describe("pawn moves", () => { - const expectedMoves: ExpectedMoves = { + const expectedMovesW: ExpectedMoves = { can_jump: [ { square: "a2", @@ -38,13 +47,6 @@ describe("pawn moves", () => { { from: "a2", to: "a4", flag: MOVE_FLAGS.PAWN_JUMP }, ], }, - { - square: "h7", - moves: [ - { from: "h7", to: "h6", flag: MOVE_FLAGS.NORMAL }, - { from: "h7", to: "h5", flag: MOVE_FLAGS.PAWN_JUMP }, - ], - }, ], promotion: [ { @@ -56,6 +58,41 @@ describe("pawn moves", () => { { from: "b7", to: "b8", flag: MOVE_FLAGS.PROMOTION, promotion: "q" }, ], }, + ], + regular: [ + { + square: "c3", + moves: [{ from: "c3", to: "c4", flag: MOVE_FLAGS.NORMAL }], + }, + ], + attack: [ + { + square: "d4", + moves: [ + { from: "d4", to: "d5", flag: MOVE_FLAGS.NORMAL }, + { from: "d4", to: "e5", flag: MOVE_FLAGS.CAPTURE }, + ], + }, + ], + blocked: [ + { + square: "c6", + moves: [], + }, + ], + }; + + const expectedMovesB: ExpectedMoves = { + can_jump: [ + { + square: "h7", + moves: [ + { from: "h7", to: "h6", flag: MOVE_FLAGS.NORMAL }, + { from: "h7", to: "h5", flag: MOVE_FLAGS.PAWN_JUMP }, + ], + }, + ], + promotion: [ { square: "g2", moves: [ @@ -67,23 +104,12 @@ describe("pawn moves", () => { }, ], regular: [ - { - square: "c3", - moves: [{ from: "c3", to: "c4", flag: MOVE_FLAGS.NORMAL }], - }, { square: "f6", moves: [{ from: "f6", to: "f5", flag: MOVE_FLAGS.NORMAL }], }, ], attack: [ - { - square: "d4", - moves: [ - { from: "d4", to: "d5", flag: MOVE_FLAGS.NORMAL }, - { from: "d4", to: "e5", flag: MOVE_FLAGS.CAPTURE }, - ], - }, { square: "e5", moves: [ @@ -97,10 +123,7 @@ describe("pawn moves", () => { square: "c7", moves: [], }, - { - square: "c6", - moves: [], - }, + { square: "e7", moves: [{ from: "e7", to: "e6", flag: MOVE_FLAGS.NORMAL }], @@ -108,31 +131,15 @@ describe("pawn moves", () => { ], }; - runTest("7k/1Pp1p2p/2P2p2/4p3/3P4/2P5/P5p1/K7 w - - 0 1", expectedMoves); + runTests( + "7k/1Pp1p2p/2P2p2/4p3/3P4/2P5/P5p1/K7 % - - 0 1", + expectedMovesW, + expectedMovesB + ); }); describe("knight moves", () => { - const expectedMoves: ExpectedMoves = { - exclude_a: [ - { - square: "a8", - moves: [ - { from: "a8", to: "b6", flag: MOVE_FLAGS.NORMAL }, - { from: "a8", to: "c7", flag: MOVE_FLAGS.NORMAL }, - ], - }, - ], - exclude_b: [ - { - square: "b7", - moves: [ - { from: "b7", to: "a5", flag: MOVE_FLAGS.NORMAL }, - { from: "b7", to: "c5", flag: MOVE_FLAGS.NORMAL }, - { from: "b7", to: "d6", flag: MOVE_FLAGS.NORMAL }, - { from: "b7", to: "d8", flag: MOVE_FLAGS.NORMAL }, - ], - }, - ], + const expectedMovesW: ExpectedMoves = { exclude_g: [ { square: "g2", @@ -162,18 +169,6 @@ describe("knight moves", () => { { from: "a2", to: "b4", flag: MOVE_FLAGS.NORMAL }, ], }, - { - square: "c3", - moves: [ - { from: "c3", to: "e4", flag: MOVE_FLAGS.NORMAL }, - { from: "c3", to: "b5", flag: MOVE_FLAGS.CAPTURE }, - { from: "c3", to: "a4", flag: MOVE_FLAGS.NORMAL }, - { from: "c3", to: "a2", flag: MOVE_FLAGS.CAPTURE }, - { from: "c3", to: "b1", flag: MOVE_FLAGS.NORMAL }, - { from: "c3", to: "d1", flag: MOVE_FLAGS.NORMAL }, - { from: "c3", to: "e2", flag: MOVE_FLAGS.NORMAL }, - ], - }, ], regular: [ { @@ -192,11 +187,53 @@ describe("knight moves", () => { ], }; - runTest("n7/1n6/8/1K1kN3/8/2n5/N5N1/7N w - - 0 1", expectedMoves); + const expectedMovesB: ExpectedMoves = { + exclude_a: [ + { + square: "a8", + moves: [ + { from: "a8", to: "b6", flag: MOVE_FLAGS.NORMAL }, + { from: "a8", to: "c7", flag: MOVE_FLAGS.NORMAL }, + ], + }, + ], + exclude_b: [ + { + square: "b7", + moves: [ + { from: "b7", to: "a5", flag: MOVE_FLAGS.NORMAL }, + { from: "b7", to: "c5", flag: MOVE_FLAGS.NORMAL }, + { from: "b7", to: "d6", flag: MOVE_FLAGS.NORMAL }, + { from: "b7", to: "d8", flag: MOVE_FLAGS.NORMAL }, + ], + }, + ], + + attack: [ + { + square: "c3", + moves: [ + { from: "c3", to: "e4", flag: MOVE_FLAGS.NORMAL }, + { from: "c3", to: "b5", flag: MOVE_FLAGS.CAPTURE }, + { from: "c3", to: "a4", flag: MOVE_FLAGS.NORMAL }, + { from: "c3", to: "a2", flag: MOVE_FLAGS.CAPTURE }, + { from: "c3", to: "b1", flag: MOVE_FLAGS.NORMAL }, + { from: "c3", to: "d1", flag: MOVE_FLAGS.NORMAL }, + { from: "c3", to: "e2", flag: MOVE_FLAGS.NORMAL }, + ], + }, + ], + }; + + runTests( + "n7/1n6/8/1K1kN3/8/2n5/N5N1/7N % - - 0 1", + expectedMovesW, + expectedMovesB + ); }); describe("bishop moves", () => { - const expectedMoves: ExpectedMoves = { + const expectedMovesW: ExpectedMoves = { exclude_a: [ { square: "a2", @@ -211,20 +248,6 @@ describe("bishop moves", () => { ], }, ], - exclude_h: [ - { - square: "h8", - moves: [ - { from: "h8", to: "g7", flag: MOVE_FLAGS.NORMAL }, - { from: "h8", to: "f6", flag: MOVE_FLAGS.NORMAL }, - { from: "h8", to: "e5", flag: MOVE_FLAGS.NORMAL }, - { from: "h8", to: "d4", flag: MOVE_FLAGS.NORMAL }, - { from: "h8", to: "c3", flag: MOVE_FLAGS.NORMAL }, - { from: "h8", to: "b2", flag: MOVE_FLAGS.NORMAL }, - { from: "h8", to: "a1", flag: MOVE_FLAGS.NORMAL }, - ], - }, - ], attack: [ { square: "d2", @@ -239,6 +262,41 @@ describe("bishop moves", () => { { from: "d2", to: "g5", flag: MOVE_FLAGS.CAPTURE }, ], }, + ], + regular: [ + { + square: "c2", + moves: [ + { from: "c2", to: "b1", flag: MOVE_FLAGS.NORMAL }, + { from: "c2", to: "d1", flag: MOVE_FLAGS.NORMAL }, + { from: "c2", to: "b3", flag: MOVE_FLAGS.NORMAL }, + { from: "c2", to: "a4", flag: MOVE_FLAGS.NORMAL }, + { from: "c2", to: "d3", flag: MOVE_FLAGS.NORMAL }, + { from: "c2", to: "e4", flag: MOVE_FLAGS.NORMAL }, + { from: "c2", to: "f5", flag: MOVE_FLAGS.NORMAL }, + { from: "c2", to: "g6", flag: MOVE_FLAGS.NORMAL }, + { from: "c2", to: "h7", flag: MOVE_FLAGS.NORMAL }, + ], + }, + ], + }; + + const expectedMovesB: ExpectedMoves = { + exclude_h: [ + { + square: "h8", + moves: [ + { from: "h8", to: "g7", flag: MOVE_FLAGS.NORMAL }, + { from: "h8", to: "f6", flag: MOVE_FLAGS.NORMAL }, + { from: "h8", to: "e5", flag: MOVE_FLAGS.NORMAL }, + { from: "h8", to: "d4", flag: MOVE_FLAGS.NORMAL }, + { from: "h8", to: "c3", flag: MOVE_FLAGS.NORMAL }, + { from: "h8", to: "b2", flag: MOVE_FLAGS.NORMAL }, + { from: "h8", to: "a1", flag: MOVE_FLAGS.NORMAL }, + ], + }, + ], + attack: [ { square: "g5", moves: [ @@ -281,29 +339,17 @@ describe("bishop moves", () => { ], }, ], - regular: [ - { - square: "c2", - moves: [ - { from: "c2", to: "b1", flag: MOVE_FLAGS.NORMAL }, - { from: "c2", to: "d1", flag: MOVE_FLAGS.NORMAL }, - { from: "c2", to: "b3", flag: MOVE_FLAGS.NORMAL }, - { from: "c2", to: "a4", flag: MOVE_FLAGS.NORMAL }, - { from: "c2", to: "d3", flag: MOVE_FLAGS.NORMAL }, - { from: "c2", to: "e4", flag: MOVE_FLAGS.NORMAL }, - { from: "c2", to: "f5", flag: MOVE_FLAGS.NORMAL }, - { from: "c2", to: "g6", flag: MOVE_FLAGS.NORMAL }, - { from: "c2", to: "h7", flag: MOVE_FLAGS.NORMAL }, - ], - }, - ], }; - runTest("K6b/8/3b4/2b3b1/8/8/B1BB4/7k w - - 0 1", expectedMoves); + runTests( + "K6b/8/3b4/2b3b1/8/8/B1BB4/7k % - - 0 1", + expectedMovesW, + expectedMovesB + ); }); describe("rook moves", () => { - const expectedMoves: ExpectedMoves = { + const expectedMovesW: ExpectedMoves = { exclude_a: [ { square: "a5", @@ -325,6 +371,9 @@ describe("rook moves", () => { ], }, ], + }; + + const expectedMovesB: ExpectedMoves = { exclude_h: [ { square: "h2", @@ -348,12 +397,12 @@ describe("rook moves", () => { ], }; - runTest("8/6K1/8/R7/8/8/7r/6k1 w - - 0 1", expectedMoves); + runTests("8/6K1/8/R7/8/8/7r/6k1 % - - 0 1", expectedMovesW, expectedMovesB); }); describe("queen and king moves", () => { - const expectedMoves: ExpectedMoves = { - queen: [ + const expectedMovesW: ExpectedMoves = { + white_queen: [ { square: "h8", moves: [ @@ -380,6 +429,23 @@ describe("queen and king moves", () => { { from: "h8", to: "a1", flag: MOVE_FLAGS.NORMAL }, ], }, + ], + white_king: [ + { + square: "g3", + moves: [ + { from: "g3", to: "f3", flag: MOVE_FLAGS.NORMAL }, + { from: "g3", to: "f4", flag: MOVE_FLAGS.NORMAL }, + { from: "g3", to: "g4", flag: MOVE_FLAGS.NORMAL }, + { from: "g3", to: "h4", flag: MOVE_FLAGS.NORMAL }, + { from: "g3", to: "h3", flag: MOVE_FLAGS.NORMAL }, + ], + }, + ], + }; + + const expectedMovesB: ExpectedMoves = { + black_queen: [ { square: "a2", moves: [ @@ -407,17 +473,7 @@ describe("queen and king moves", () => { ], }, ], - king: [ - { - square: "g3", - moves: [ - { from: "g3", to: "f3", flag: MOVE_FLAGS.NORMAL }, - { from: "g3", to: "f4", flag: MOVE_FLAGS.NORMAL }, - { from: "g3", to: "g4", flag: MOVE_FLAGS.NORMAL }, - { from: "g3", to: "h4", flag: MOVE_FLAGS.NORMAL }, - { from: "g3", to: "h3", flag: MOVE_FLAGS.NORMAL }, - ], - }, + black_king: [ { square: "c7", moves: [ @@ -431,5 +487,5 @@ describe("queen and king moves", () => { ], }; - runTest("7Q/2k5/8/8/8/6K1/q7/8 w - - 0 1", expectedMoves); + runTests("7Q/2k5/8/8/8/6K1/q7/8 % - - 0 1", expectedMovesW, expectedMovesB); }); diff --git a/server/src/__test__/isSquareAttacked.test.ts b/server/src/__test__/isSquareAttacked.test.ts index 85c629a..1f9fc97 100644 --- a/server/src/__test__/isSquareAttacked.test.ts +++ b/server/src/__test__/isSquareAttacked.test.ts @@ -8,5 +8,5 @@ test("", () => { expect(chess.isSquareAttacked("e4", "b")).toBe(false); expect(chess.isSquareAttacked("e5", "w")).toBe(false); - expect(chess.isSquareAttacked("e5", "b")).toBe(false); + expect(chess.isSquareAttacked("e5", "b")).toBe(true); }); diff --git a/server/src/chess/engine.ts b/server/src/chess/engine.ts index 91af395..d7f2a9f 100644 --- a/server/src/chess/engine.ts +++ b/server/src/chess/engine.ts @@ -512,23 +512,25 @@ export default class Chess { this._halfMoves = halfMoves; this._fullMoves = fullMoves; this._computeMoves(); - this._computeAttacks(); } private _computeMoves() { - for (let i = 0; i < this._board.length; i++) - this._moves = this._moves.concat(this.getMovesForSquare(i)); - } - - private _computeAttacks() { this._attacks = new Array(64).fill(0); - this.getMoves().forEach(({ from, to }) => { - const pieceColor = (this.getPiece(from) as Piece).color; - const square = squareIndex(to); + for (let i = 0; i < this._board.length; i++) { + const { piece, moves } = this._getMovesForSquare(i); - this._attacks[square] |= COLOR_MASKS[pieceColor]; - }); + this._moves = this._moves.concat(this._processMoves(piece, moves)); + + if (piece != null) { + const pieceColor = piece.color; + + moves.forEach( + ({ to }) => + (this._attacks[squareIndex(to)] |= COLOR_MASKS[pieceColor]) + ); + } + } } static load(fen = DEFAULT_POSITION) { @@ -667,16 +669,16 @@ export default class Chess { return this._moves; } - getMovesForSquare(square: Square | number): Move[] { - if (typeof square != "number") square = squareIndex(square); - + private _getMovesForSquare(square: number) { const piece = this.getPiece(square); + const moves = + piece == null ? [] : generatePieceMoves(this._board, square, piece); - if (piece == null || piece.color != this._turn) return []; + return { piece, moves }; + } - const moves = generatePieceMoves(this._board, square, piece); - - if (piece.type != PIECE.KING) return moves; + private _processMoves(piece: Piece | null, moves: Move[]) { + if (piece?.type != PIECE.KING) return moves; const other_color = COLOR_MASKS[swapColor(piece.color)]; @@ -687,6 +689,16 @@ export default class Chess { }, this); } + getMovesForSquare(square: Square | number): Move[] { + if (typeof square != "number") square = squareIndex(square); + + if (square < 0 || square > 63) return []; + + const { piece, moves } = this._getMovesForSquare(square); + + return this._processMoves(piece, moves); + } + makeMove() {} isSquareAttacked(square: Square | number, color: Color) {