X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fbase_rules.js;h=69549525408d51fa349cac05fb2254db758c216b;hb=4a2093139089632727de4f510127ef186cab528e;hp=d219f78ab6c470b16ce7dfa8e5ce912b86e5721a;hpb=e50a802531b99829c533f22ecd21e359e7e1e049;p=vchess.git diff --git a/client/src/base_rules.js b/client/src/base_rules.js index d219f78a..69549525 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -30,6 +30,7 @@ export const Move = class Move { // NOTE: x coords = top to bottom; y = left to right // (from white player perspective) export const ChessRules = class ChessRules { + ////////////// // MISC UTILS @@ -111,6 +112,11 @@ export const ChessRules = class ChessRules { return false; } + // Some games are drawn unusually (bottom right corner is black) + static get DarkBottomRight() { + return false; + } + // Some variants require lines drawing static get Lines() { if (V.Monochrome) { @@ -125,6 +131,11 @@ export const ChessRules = class ChessRules { return null; } + // In some variants, the player who repeat a position loses + static get LoseOnRepetition() { + return false; + } + // Some variants use click infos: doClick() { return null; @@ -186,7 +197,7 @@ export const ChessRules = class ChessRules { if (V.PIECES.includes(row[i].toLowerCase())) sumElts++; else { const num = parseInt(row[i], 10); - if (isNaN(num)) return false; + if (isNaN(num) || num <= 0) return false; sumElts += num; } } @@ -264,7 +275,7 @@ export const ChessRules = class ChessRules { // En-passant square, if any getEpSquare(moveOrSquare) { - if (!moveOrSquare) return undefined; + if (!moveOrSquare) return undefined; //TODO: necessary line?! if (typeof moveOrSquare === "string") { const square = moveOrSquare; if (square == "-") return undefined; @@ -518,7 +529,6 @@ export const ChessRules = class ChessRules { // Scan board for kings positions scanKings(fen) { - this.INIT_COL_KING = { w: -1, b: -1 }; // Squares of white and black king: this.kingPos = { w: [-1, -1], b: [-1, -1] }; const fenRows = V.ParseFen(fen).position.split("/"); @@ -529,11 +539,9 @@ export const ChessRules = class ChessRules { switch (fenRows[i].charAt(j)) { case "k": this.kingPos["b"] = [i, k]; - this.INIT_COL_KING["b"] = k; break; case "K": this.kingPos["w"] = [i, k]; - this.INIT_COL_KING["w"] = k; break; default: { const num = parseInt(fenRows[i].charAt(j), 10); @@ -645,20 +653,14 @@ export const ChessRules = class ChessRules { // MOVES GENERATION // All possible moves from selected square - getPotentialMovesFrom([x, y]) { - switch (this.getPiece(x, y)) { - case V.PAWN: - return this.getPotentialPawnMoves([x, y]); - case V.ROOK: - return this.getPotentialRookMoves([x, y]); - case V.KNIGHT: - return this.getPotentialKnightMoves([x, y]); - case V.BISHOP: - return this.getPotentialBishopMoves([x, y]); - case V.QUEEN: - return this.getPotentialQueenMoves([x, y]); - case V.KING: - return this.getPotentialKingMoves([x, y]); + getPotentialMovesFrom(sq) { + switch (this.getPiece(sq[0], sq[1])) { + case V.PAWN: return this.getPotentialPawnMoves(sq); + case V.ROOK: return this.getPotentialRookMoves(sq); + case V.KNIGHT: return this.getPotentialKnightMoves(sq); + case V.BISHOP: return this.getPotentialBishopMoves(sq); + case V.QUEEN: return this.getPotentialQueenMoves(sq); + case V.KING: return this.getPotentialKingMoves(sq); } return []; //never reached } @@ -667,14 +669,14 @@ export const ChessRules = class ChessRules { // tr: transformation getBasicMove([sx, sy], [ex, ey], tr) { const initColor = this.getColor(sx, sy); - const initPiece = this.getPiece(sx, sy); + const initPiece = this.board[sx][sy].charAt(1); let mv = new Move({ appear: [ new PiPo({ x: ex, y: ey, - c: tr ? tr.c : initColor, - p: tr ? tr.p : initPiece + c: !!tr ? tr.c : initColor, + p: !!tr ? tr.p : initPiece }) ], vanish: [ @@ -694,7 +696,7 @@ export const ChessRules = class ChessRules { x: ex, y: ey, c: this.getColor(ex, ey), - p: this.getPiece(ex, ey) + p: this.board[ex][ey].charAt(1) }) ); } @@ -735,8 +737,7 @@ export const ChessRules = class ChessRules { enpassantMove.vanish.push({ x: x, y: epSquare.y, - // Captured piece is usually a pawn, but next line seems harmless - p: this.getPiece(x, epSquare.y), + p: this.board[x][epSquare.y].charAt(1), c: this.getColor(x, epSquare.y) }); } @@ -879,25 +880,22 @@ export const ChessRules = class ChessRules { V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep" ); - if (V.HasCastle) moves = moves.concat(this.getCastleMoves(sq)); + if (V.HasCastle && this.castleFlags[this.turn].some(v => v < V.size.y)) + moves = moves.concat(this.getCastleMoves(sq)); return moves; } // "castleInCheck" arg to let some variants castle under check - getCastleMoves([x, y], castleInCheck) { + getCastleMoves([x, y], finalSquares, castleInCheck, castleWith) { const c = this.getColor(x, y); - if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c]) - return []; //x isn't first rank, or king has moved (shortcut) // Castling ? const oppCol = V.GetOppCol(c); let moves = []; let i = 0; // King, then rook: - const finalSquares = [ - [2, 3], - [V.size.y - 2, V.size.y - 3] - ]; + finalSquares = finalSquares || [ [2, 3], [V.size.y - 2, V.size.y - 3] ]; + const castlingKing = this.board[x][y].charAt(1); castlingCheck: for ( let castleSide = 0; castleSide < 2; @@ -908,25 +906,28 @@ export const ChessRules = class ChessRules { // NOTE: in some variants this is not a rook const rookPos = this.castleFlags[c][castleSide]; - if (this.board[x][rookPos] == V.EMPTY || this.getColor(x, rookPos) != c) + const castlingPiece = this.board[x][rookPos].charAt(1); + if ( + this.board[x][rookPos] == V.EMPTY || + this.getColor(x, rookPos) != c || + (!!castleWith && !castleWith.includes(castlingPiece)) + ) { // Rook is not here, or changed color (see Benedict) continue; + } // Nothing on the path of the king ? (and no checks) - const castlingPiece = this.getPiece(x, rookPos); const finDist = finalSquares[castleSide][0] - y; let step = finDist / Math.max(1, Math.abs(finDist)); i = y; do { if ( - // NOTE: "castling" arg is used by some variants (Monster), - // where "isAttacked" is overloaded in an infinite-recursive way. - // TODO: not used anymore (Monster + Doublemove2 are simplified). - (!castleInCheck && this.isAttacked([x, i], oppCol, "castling")) || - (this.board[x][i] != V.EMPTY && + (!castleInCheck && this.isAttacked([x, i], oppCol)) || + ( + this.board[x][i] != V.EMPTY && // NOTE: next check is enough, because of chessboard constraints - (this.getColor(x, i) != c || - ![V.KING, castlingPiece].includes(this.getPiece(x, i)))) + (this.getColor(x, i) != c || ![y, rookPos].includes(i)) + ) ) { continue castlingCheck; } @@ -945,7 +946,7 @@ export const ChessRules = class ChessRules { finalSquares[castleSide][i] != rookPos && this.board[x][finalSquares[castleSide][i]] != V.EMPTY && ( - this.getPiece(x, finalSquares[castleSide][i]) != V.KING || + finalSquares[castleSide][i] != y || this.getColor(x, finalSquares[castleSide][i]) != c ) ) { @@ -960,7 +961,7 @@ export const ChessRules = class ChessRules { new PiPo({ x: x, y: finalSquares[castleSide][0], - p: V.KING, + p: castlingKing, c: c }), new PiPo({ @@ -971,7 +972,8 @@ export const ChessRules = class ChessRules { }) ], vanish: [ - new PiPo({ x: x, y: y, p: V.KING, c: c }), + // King might be initially disguised (Titan...) + new PiPo({ x: x, y: y, p: castlingKing, c: c }), new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c }) ], end: @@ -1069,8 +1071,10 @@ export const ChessRules = class ChessRules { } if ( V.OnBoard(rx, ry) && + this.board[rx][ry] != V.EMPTY && this.getPiece(rx, ry) == piece && - this.getColor(rx, ry) == color + this.getColor(rx, ry) == color && + this.canTake([rx, ry], [x, y]) ) { return true; } @@ -1169,8 +1173,8 @@ export const ChessRules = class ChessRules { this.postPlay(move); } - updateCastleFlags(move, piece) { - const c = V.GetOppCol(this.turn); + updateCastleFlags(move, piece, color) { + const c = color || V.GetOppCol(this.turn); const firstRank = (c == "w" ? V.size.x - 1 : 0); // Update castling flags if rooks are moved const oppCol = this.turn; @@ -1474,4 +1478,5 @@ export const ChessRules = class ChessRules { ) ); } + };