From c9ab034035a3cac65e4ac9f48a946ecef5ed111e Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Sun, 1 May 2022 16:03:32 +0200 Subject: [PATCH] After base_rules refactoring. Still a few issues (Dark...) --- base_rules.js | 711 +++++++++++++++-------------------- common.css | 4 + variants/Absorption/class.js | 44 +-- variants/Atomic/class.js | 2 +- variants/Balanced/class.js | 2 +- variants/Benedict/class.js | 32 +- variants/Cannibal/class.js | 2 +- variants/Capture/class.js | 2 +- variants/Crazyhouse/class.js | 2 +- variants/Cylinder/class.js | 2 +- variants/Dark/class.js | 2 +- variants/Doublemove/class.js | 2 +- variants/Zen/class.js | 2 +- 13 files changed, 344 insertions(+), 465 deletions(-) diff --git a/base_rules.js b/base_rules.js index 53393df..efeb078 100644 --- a/base_rules.js +++ b/base_rules.js @@ -19,7 +19,6 @@ export default class ChessRules { // Users can generally select a randomness level from 0 to 2. static get Options() { return { - // NOTE: some options are required for FEN generation, some aren't. select: [{ label: "Randomness", variable: "randomness", @@ -62,18 +61,8 @@ export default class ChessRules { }; } - // Pawns specifications - get pawnSpecs() { - return { - directions: {w: -1, b: 1}, - initShift: {w: 1, b: 1}, - twoSquares: true, - threeSquares: false, - canCapture: true, - captureBackward: false, - bidirectional: false, - promotions: ['r', 'n', 'b', 'q'] - }; + get pawnPromotions() { + return ['q', 'r', 'n', 'b']; } // Some variants don't have flags: @@ -399,13 +388,12 @@ export default class ChessRules { ////////////////// // INITIALIZATION - // Fen string fully describes the game state constructor(o) { this.options = o.options; this.playerColor = o.color; this.afterPlay = o.afterPlay; - // FEN-related: + // Fen string fully describes the game state if (!o.fen) o.fen = this.genRandInitFen(o.seed); const fenParsed = this.parseFen(o.fen); @@ -456,46 +444,35 @@ export default class ChessRules { this.captured = null; } if (this.options["dark"]) { - this.enlightened = ArrayFun.init(this.size.x, this.size.y); // Setup enlightened: squares reachable by player side - this.updateEnlightened(false); + this.enlightened = ArrayFun.init(this.size.x, this.size.y, false); + this.updateEnlightened(); } } - updateEnlightened(withGraphics) { - let newEnlightened = ArrayFun.init(this.size.x, this.size.y, false); - const pawnShift = { w: -1, b: 1 }; + updateEnlightened() { + this.oldEnlightened = this.enlightened; + this.enlightened = ArrayFun.init(this.size.x, this.size.y, false); // Add pieces positions + all squares reachable by moves (includes Zen): - // (watch out special pawns case) for (let x=0; x { - const [i, j] = [x + step[0], this.computeY(y + step[1])]; - if (this.onBoard(i, j) && this.board[i][j] == "") - newEnlightened[i][j] = true; - }); - } + this.enlightened[x][y] = true; this.getPotentialMovesFrom([x, y]).forEach(m => { - newEnlightened[m.end.x][m.end.y] = true; + this.enlightened[m.end.x][m.end.y] = true; }); } } } if (this.epSquare) - this.enlightEnpassant(newEnlightened); - if (withGraphics) - this.graphUpdateEnlightened(newEnlightened); - this.enlightened = newEnlightened; + this.enlightEnpassant(); } - // Include en-passant capturing square if any: - enlightEnpassant(newEnlightened) { - const steps = this.pieces(this.playerColor)["p"].attack; + // Include square of the en-passant capturing square: + enlightEnpassant() { + // NOTE: shortcut, pawn has only one attack type, doesn't depend on square + const steps = this.pieces(this.playerColor)["p"].attack[0].steps; for (let step of steps) { const x = this.epSquare.x - step[0], y = this.computeY(this.epSquare.y - step[1]); @@ -504,62 +481,47 @@ export default class ChessRules { this.getColor(x, y) == this.playerColor && this.getPieceType(x, y) == "p" ) { - newEnlightened[x][this.epSquare.y] = true; + this.enlightened[x][this.epSquare.y] = true; break; } } } - // Apply diff this.enlightened --> newEnlightened on board - graphUpdateEnlightened(newEnlightened) { + // Apply diff this.enlightened --> oldEnlightened on board + graphUpdateEnlightened() { let chessboard = document.getElementById(this.containerId).querySelector(".chessboard"); const r = chessboard.getBoundingClientRect(); const pieceWidth = this.getPieceWidth(r.width); for (let x=0; x this.g_pieces[x][y].classList.add(cl)); - this.g_pieces[x][y].style.width = pieceWidth + "px"; - this.g_pieces[x][y].style.height = pieceWidth + "px"; - const [ip, jp] = this.getPixelPosition(x, y, r); - this.g_pieces[x][y].style.transform = - `translate(${ip}px,${jp}px)`; - chessboard.appendChild(this.g_pieces[x][y]); - } + if (this.g_pieces[x][y]) + this.g_pieces[x][y].classList.remove("hidden"); } } } } - // ordering p,r,n,b,q,k (most general + count in base 30 if needed) + // ordering as in pieces() p,r,n,b,q,k (+ count in base 30 if needed) initReserves(reserveStr) { const counts = reserveStr.split("").map(c => parseInt(c, 30)); this.reserve = { w: {}, b: {} }; - const pieceName = Object.keys(this.pieces()); - for (let i of ArrayFun.range(12)) { - if (i < 6) + const pieceName = ['p', 'r', 'n', 'b', 'q', 'k']; + const L = pieceName.length; + for (let i of ArrayFun.range(2 * L)) { + if (i < L) this.reserve['w'][pieceName[i]] = counts[i]; else - this.reserve['b'][pieceName[i-6]] = counts[i]; + this.reserve['b'][pieceName[i-L]] = counts[i]; } } @@ -712,10 +674,7 @@ export default class ChessRules { const pieceWidth = this.getPieceWidth(r.width); for (let i=0; i < this.size.x; i++) { for (let j=0; j < this.size.y; j++) { - if ( - this.board[i][j] != "" && - (!this.options["dark"] || this.enlightened[i][j]) - ) { + if (this.board[i][j] != "") { const color = this.getColor(i, j); const piece = this.getPiece(i, j); this.g_pieces[i][j] = document.createElement("piece"); @@ -725,6 +684,8 @@ export default class ChessRules { this.g_pieces[i][j].style.height = pieceWidth + "px"; const [ip, jp] = this.getPixelPosition(i, j, r); this.g_pieces[i][j].style.transform = `translate(${ip}px,${jp}px)`; + if (this.enlightened && !this.enlightened[i][j]) + this.g_pieces[i][j].classList.add("hidden"); chessboard.appendChild(this.g_pieces[i][j]); } } @@ -787,7 +748,7 @@ export default class ChessRules { r_cell.style.height = sqResSize + "px"; rcontainer.appendChild(r_cell); let piece = document.createElement("piece"); - const pieceSpec = this.pieces(c)[p]; + const pieceSpec = this.pieces()[p]; piece.classList.add(pieceSpec["class"]); piece.classList.add(c == 'w' ? "white" : "black"); piece.style.width = "100%"; @@ -848,7 +809,7 @@ export default class ChessRules { const pieceWidth = this.getPieceWidth(newWidth); for (let i=0; i < this.size.x; i++) { for (let j=0; j < this.size.y; j++) { - if (this.board[i][j] != "") { + if (this.g_pieces[i][j]) { // NOTE: could also use CSS transform "scale" this.g_pieces[i][j].style.width = pieceWidth + "px"; this.g_pieces[i][j].style.height = pieceWidth + "px"; @@ -1051,7 +1012,7 @@ export default class ChessRules { choice.style.backgroundColor = "lightyellow"; choice.onclick = () => callback(moves[i]); const piece = document.createElement("piece"); - const pieceSpec = this.pieces(color)[moves[i].appear[0].p]; + const pieceSpec = this.pieces()[moves[i].appear[0].p]; piece.classList.add(pieceSpec["class"]); piece.classList.add(color == 'w' ? "white" : "black"); piece.style.width = "100%"; @@ -1065,7 +1026,7 @@ export default class ChessRules { // BASIC UTILS get size() { - return { "x": 8, "y": 8 }; + return {"x": 8, "y": 8}; } // Color of thing on square (i,j). 'undefined' if square is empty @@ -1089,15 +1050,9 @@ export default class ChessRules { return (color == "w" ? "b" : "w"); } - // Can thing on square1 take thing on square2 + // Can thing on square1 capture (no return) thing on square2? canTake([x1, y1], [x2, y2]) { - return ( - (this.getColor(x1, y1) !== this.getColor(x2, y2)) || - ( - (this.options["recycle"] || this.options["teleport"]) && - this.getPieceType(x2, y2) != "k" - ) - ); + return (this.getColor(x1, y1) !== this.getColor(x2, y2)); } // Is (x,y) on the chessboard? @@ -1106,7 +1061,7 @@ export default class ChessRules { y >= 0 && y < this.size.y); } - // Used in interface: 'side' arg == player color + // Used in interface canIplay(x, y) { return ( this.playerColor == this.turn && @@ -1120,57 +1075,83 @@ export default class ChessRules { //////////////////////// // PIECES SPECIFICATIONS - pieces(color) { + pieces(color, x, y) { const pawnShift = (color == "w" ? -1 : 1); + const initRank = ((color == 'w' && x == 6) || (color == 'b' && x == 1)); return { 'p': { "class": "pawn", - steps: [[pawnShift, 0]], - range: 1, - attack: [[pawnShift, 1], [pawnShift, -1]] + moves: [ + { + steps: [[pawnShift, 0]], + range: (initRank ? 2 : 1) + } + ], + attack: [ + { + steps: [[pawnShift, 1], [pawnShift, -1]], + range: 1 + } + ] }, // rook 'r': { "class": "rook", - steps: [[0, 1], [0, -1], [1, 0], [-1, 0]] + moves: [ + {steps: [[0, 1], [0, -1], [1, 0], [-1, 0]]} + ] }, // knight 'n': { "class": "knight", - steps: [ - [1, 2], [1, -2], [-1, 2], [-1, -2], - [2, 1], [-2, 1], [2, -1], [-2, -1] - ], - range: 1 + moves: [ + { + steps: [ + [1, 2], [1, -2], [-1, 2], [-1, -2], + [2, 1], [-2, 1], [2, -1], [-2, -1] + ], + range: 1 + } + ] }, // bishop 'b': { "class": "bishop", - steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]] + moves: [ + {steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]]} + ] }, // queen 'q': { "class": "queen", - steps: [ - [0, 1], [0, -1], [1, 0], [-1, 0], - [1, 1], [1, -1], [-1, 1], [-1, -1] + moves: [ + { + steps: [ + [0, 1], [0, -1], [1, 0], [-1, 0], + [1, 1], [1, -1], [-1, 1], [-1, -1] + ] + } ] }, // king 'k': { "class": "king", - steps: [ - [0, 1], [0, -1], [1, 0], [-1, 0], - [1, 1], [1, -1], [-1, 1], [-1, -1] - ], - range: 1 + moves: [ + { + steps: [ + [0, 1], [0, -1], [1, 0], [-1, 0], + [1, 1], [1, -1], [-1, 1], [-1, -1] + ], + range: 1 + } + ] }, // Cannibal kings: - 's': { "class": "king-pawn" }, - 'u': { "class": "king-rook" }, - 'o': { "class": "king-knight" }, - 'c': { "class": "king-bishop" }, - 't': { "class": "king-queen" } + '!': {"class": "king-pawn", moveas: "p"}, + '#': {"class": "king-rook", moveas: "r"}, + '$': {"class": "king-knight", moveas: "n"}, + '%': {"class": "king-bishop", moveas: "b"}, + '*': {"class": "king-queen", moveas: "q"} }; } @@ -1194,25 +1175,28 @@ export default class ChessRules { for (let i = 0; i < this.size.x; i++) { for (let j = 0; j < this.size.y; j++) { if (this.board[i][j] != "" && this.getColor(i, j) == color) { - const specs = this.pieces(color)[this.getPieceType(i, j)]; - const steps = specs.attack || specs.steps; - outerLoop: for (let step of steps) { - let [ii, jj] = [i + step[0], this.computeY(j + step[1])]; - let stepCounter = 1; - while (this.onBoard(ii, jj) && this.board[ii][jj] == "") { - if (specs.range <= stepCounter++) - continue outerLoop; - ii += step[0]; - jj = this.computeY(jj + step[1]); - } - if ( - this.onBoard(ii, jj) && - this.getColor(ii, jj) == oppCol && - this.filterValid( - [this.getBasicMove([i, j], [ii, jj])] - ).length >= 1 - ) { - return true; + const allSpecs = this.pieces(color, i, j) + let specs = allSpecs[this.getPieceType(i, j)]; + const attacks = specs.attack || specs.moves; + for (let a of attacks) { + outerLoop: for (let step of a.steps) { + let [ii, jj] = [i + step[0], this.computeY(j + step[1])]; + let stepCounter = 1; + while (this.onBoard(ii, jj) && this.board[ii][jj] == "") { + if (a.range <= stepCounter++) + continue outerLoop; + ii += step[0]; + jj = this.computeY(jj + step[1]); + } + if ( + this.onBoard(ii, jj) && + this.getColor(ii, jj) == oppCol && + this.filterValid( + [this.getBasicMove([i, j], [ii, jj])] + ).length >= 1 + ) { + return true; + } } } } @@ -1229,10 +1213,9 @@ export default class ChessRules { let moves = []; for (let i=0; i 0 && + this.getPieceType(moves[0].start.x, moves[0].start.y) == "p" + ) { + let moreMoves = []; + const lastRank = (color == "w" ? 0 : this.size.x - 1); + const initPiece = this.getPiece(moves[0].start.x, moves[0].start.y); + moves.forEach(m => { + let finalPieces = ["p"]; + const [x1, y1] = [m.start.x, m.start.y]; + const [x2, y2] = [m.end.x, m.end.y]; + const promotionOk = ( + x2 == lastRank && + (!this.options["rifle"] || this.board[x2][y2] == "") + ); + if (!promotionOk) + return; //nothing to do + if (!this.options["pawnfall"]) { + if ( + this.options["cannibal"] && + this.board[x2][y2] != "" && + this.getColor(x2, y2) == oppCol + ) { + finalPieces = [this.getPieceType(x2, y2)]; + } + else + finalPieces = this.pawnPromotions; + } + m.appear[0].p = finalPieces[0]; + if (initPiece == "!") //cannibal king-pawn + m.appear[0].p = C.CannibalKingCode[finalPieces[0]]; + for (let i=1; i { + for (let s of stepArray) { + outerLoop: for (let step of s.steps) { + let [i, j] = [x + step[0], this.computeY(y + step[1])]; + let stepCounter = 1; + while (this.onBoard(i, j) && this.board[i][j] == "") { + if (type != "attack" && !explored[i + "." + j]) { + explored[i + "." + j] = true; + moves.push(this.getBasicMove([x, y], [i, j])); + } + if (s.range <= stepCounter++) + continue outerLoop; + i += step[0]; + j = this.computeY(j + step[1]); + } + if (!this.onBoard(i, j)) + continue; + const pieceIJ = this.getPieceType(i, j); + if ( + type != "moveonly" && + !explored[i + "." + j] && + ( + !this.options["zen"] || + pieceIJ == "k" + ) && + ( + this.canTake([x, y], [i, j]) || + ( + (this.options["recycle"] || this.options["teleport"]) && + pieceIJ != "k" + ) + ) + ) { + explored[i + "." + j] = true; + moves.push(this.getBasicMove([x, y], [i, j])); + } + } } - } + }; + + const specialAttack = !!stepSpec.attack; + if (specialAttack) + findAddMoves("attack", stepSpec.attack); + findAddMoves(specialAttack ? "moveonly" : "all", stepSpec.moves); if (this.options["zen"]) - Array.prototype.push.apply(moves, this.getZenCaptures(x, y)); + Array.prototype.push.apply(moves, this.findCapturesOn([x, y], true)); return moves; } - getZenCaptures(x, y) { + findCapturesOn([x, y], zen) { let moves = []; // Find reverse captures (opponent takes) const color = this.getColor(x, y); const pieceType = this.getPieceType(x, y); const oppCol = C.GetOppCol(color); - const pieces = this.pieces(oppCol); - Object.keys(pieces).forEach(p => { - if ( - p == "k" || - (this.options["cannibal"] && C.CannibalKings[p]) - ) { - return; //king isn't captured this way - } - const steps = pieces[p].attack || pieces[p].steps; - if (!steps) - return; //cannibal king for example (TODO...) - const range = pieces[p].range; - steps.forEach(s => { - // From x,y: revert step - let [i, j] = [x - s[0], this.computeY(y - s[1])]; - let stepCounter = 1; - while (this.onBoard(i, j) && this.board[i][j] == "") { - if (range <= stepCounter++) - return; - i -= s[0]; - j = this.computeY(j - s[1]); - } - if ( - this.onBoard(i, j) && - this.getPieceType(i, j) == p && - this.getColor(i, j) == oppCol && //condition for Recycle & Teleport - this.canTake([i, j], [x, y]) - ) { - if (pieceType != "p") - moves.push(this.getBasicMove([x, y], [i, j])); - else - this.addPawnMoves([x, y], [i, j], moves); + for (let i=0; i 1e-7) + continue; + distance = Math.round(distance); //in case of (numerical...) + if (a.range < distance) + continue; + // Finally verify that nothing stand in-between + let [ii, jj] = [i + s[0], this.computeY(j + s[1])]; + let stepCounter = 1; + while (this.onBoard(ii, jj) && this.board[ii][jj] == "") { + ii += s[0]; + jj = this.computeY(jj + s[1]); + } + if (ii == x && jj == y) { + moves.push(this.getBasicMove([x, y], [i, j])); + if (!zen) + return moves; //test for underCheck + } + } + } } - }); - }); + } + } return moves; } @@ -1611,8 +1673,9 @@ export default class ChessRules { } // Special case of en-passant captures: treated separately - getEnpassantCaptures([x, y], shiftX) { + getEnpassantCaptures([x, y]) { const color = this.getColor(x, y); + const shiftX = (color == 'w' ? -1 : 1); const oppCol = C.GetOppCol(color); let enpassantMove = null; if ( @@ -1631,144 +1694,6 @@ export default class ChessRules { return !!enpassantMove ? [enpassantMove] : []; } - // Consider all potential promotions. - // NOTE: "promotions" arg = special override for Hiddenqueen variant - addPawnMoves([x1, y1], [x2, y2], moves, promotions) { - let finalPieces = ["p"]; - const color = this.getColor(x1, y1); - const oppCol = C.GetOppCol(color); - const lastRank = (color == "w" ? 0 : this.size.x - 1); - const promotionOk = - x2 == lastRank && (!this.options["rifle"] || this.board[x2][y2] == ""); - if (promotionOk && !this.options["pawnfall"]) { - if ( - this.options["cannibal"] && - this.board[x2][y2] != "" && - this.getColor(x2, y2) == oppCol - ) { - finalPieces = [this.getPieceType(x2, y2)]; - } - else if (promotions) - finalPieces = promotions; - else if (this.pawnSpecs.promotions) - finalPieces = this.pawnSpecs.promotions; - } - for (let piece of finalPieces) { - const tr = !this.options["pawnfall"] && piece != "p" - ? { c: color, p: piece } - : null; - let newMove = this.getBasicMove([x1, y1], [x2, y2], tr); - if (promotionOk && this.options["pawnfall"]) { - newMove.appear.shift(); - newMove.pawnfall = true; //required in prePlay() - } - moves.push(newMove); - } - } - - // What are the pawn moves from square x,y ? - getPotentialPawnMoves([x, y], promotions) { - const color = this.getColor(x, y); //this.turn doesn't work for Dark mode - const [sizeX, sizeY] = [this.size.x, this.size.y]; - const pawnShiftX = this.pawnSpecs.directions[color]; - const firstRank = (color == "w" ? sizeX - 1 : 0); - const forward = (color == 'w' ? -1 : 1); - - // Pawn movements in shiftX direction: - const getPawnMoves = (shiftX) => { - let moves = []; - // NOTE: next condition is generally true (no pawn on last rank) - if (x + shiftX >= 0 && x + shiftX < sizeX) { - if (this.board[x + shiftX][y] == "") { - // One square forward (or backward) - this.addPawnMoves([x, y], [x + shiftX, y], moves, promotions); - // Next condition because pawns on 1st rank can generally jump - if ( - this.pawnSpecs.twoSquares && - ( - ( - color == 'w' && - x >= this.size.x - 1 - this.pawnSpecs.initShift['w'] - ) - || - (color == 'b' && x <= this.pawnSpecs.initShift['b']) - ) - ) { - if ( - shiftX == forward && - this.board[x + 2 * shiftX][y] == "" - ) { - // Two squares jump - moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y])); - if ( - this.pawnSpecs.threeSquares && - this.board[x + 3 * shiftX, y] == "" - ) { - // Three squares jump - moves.push(this.getBasicMove([x, y], [x + 3 * shiftX, y])); - } - } - } - } - // Captures - if (this.pawnSpecs.canCapture) { - for (let shiftY of [-1, 1]) { - const yCoord = this.computeY(y + shiftY); - if (yCoord >= 0 && yCoord < sizeY) { - if ( - this.board[x + shiftX][yCoord] != "" && - this.canTake([x, y], [x + shiftX, yCoord]) && - ( - !this.options["zen"] || - this.getPieceType(x + shiftX, yCoord) == "k" - ) - ) { - this.addPawnMoves( - [x, y], [x + shiftX, yCoord], - moves, promotions - ); - } - if ( - this.pawnSpecs.captureBackward && shiftX == forward && - x - shiftX >= 0 && x - shiftX < this.size.x && - this.board[x - shiftX][yCoord] != "" && - this.canTake([x, y], [x - shiftX, yCoord]) && - ( - !this.options["zen"] || - this.getPieceType(x + shiftX, yCoord) == "k" - ) - ) { - this.addPawnMoves( - [x, y], [x - shiftX, yCoord], - moves, promotions - ); - } - } - } - } - } - return moves; - } - - let pMoves = getPawnMoves(pawnShiftX); - if (this.pawnSpecs.bidirectional) - pMoves = pMoves.concat(getPawnMoves(-pawnShiftX)); - - if (this.hasEnpassant) { - // NOTE: backward en-passant captures are not considered - // because no rules define them (for now). - Array.prototype.push.apply( - pMoves, - this.getEnpassantCaptures([x, y], pawnShiftX) - ); - } - - if (this.options["zen"]) - Array.prototype.push.apply(pMoves, this.getZenCaptures(x, y)); - - return pMoves; - } - // "castleInCheck" arg to let some variants castle under check getCastleMoves([x, y], finalSquares, castleInCheck, castleWith) { const c = this.getColor(x, y); @@ -1862,8 +1787,8 @@ export default class ChessRules { ], end: Math.abs(y - rookPos) <= 2 - ? { x: x, y: rookPos } - : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) } + ? {x: x, y: rookPos} + : {x: x, y: y + 2 * (castleSide == 0 ? -1 : 1)} }) ); } @@ -1878,45 +1803,7 @@ export default class ChessRules { underCheck([x, y], color) { if (this.options["taking"] || this.options["dark"]) return false; - color = color || C.GetOppCol(this.getColor(x, y)); - const pieces = this.pieces(color); - return Object.keys(pieces).some(p => { - return this.isAttackedBy([x, y], p, color, pieces[p]); - }); - } - - isAttackedBy([x, y], piece, color, stepSpec) { - const steps = stepSpec.attack || stepSpec.steps; - if (!steps) - return false; //cannibal king, for example - const range = stepSpec.range; - let explored = {}; //for Cylinder mode - outerLoop: for (let step of steps) { - let rx = x - step[0], - ry = this.computeY(y - step[1]); - let stepCounter = 1; - while ( - this.onBoard(rx, ry) && - this.board[rx][ry] == "" && - !explored[rx + "." + ry] - ) { - explored[rx + "." + ry] = true; - if (range <= stepCounter++) - continue outerLoop; - rx -= step[0]; - ry = this.computeY(ry - step[1]); - } - if ( - this.onBoard(rx, ry) && - this.board[rx][ry] != "" && - this.getPieceType(rx, ry) == piece && - this.getColor(rx, ry) == color && - (!this.options["madrasi"] || !this.isImmobilized([rx, ry])) - ) { - return true; - } - } - return false; + return (this.findCapturesOn([x, y]).length >= 1); } // Stop at first king found (TODO: multi-kings) @@ -2097,7 +1984,7 @@ export default class ChessRules { const color = this.turn; const oppCol = C.GetOppCol(color); if (this.options["dark"]) - this.updateEnlightened(true); + this.updateEnlightened(); if (this.options["teleport"]) { if ( this.subTurnTeleport == 1 && @@ -2181,7 +2068,7 @@ export default class ChessRules { if (this.atLeastOneMove()) return "*"; // No valid move: stalemate or checkmate? - if (!this.underCheck(kingPos, color)) + if (!this.underCheck(kingPos[0], color)) return "1/2"; // OK, checkmate return (color == "w" ? "0-1" : "1-0"); @@ -2190,12 +2077,10 @@ export default class ChessRules { // NOTE: quite suboptimal for eg. Benedict (not a big deal I think) playVisual(move, r) { move.vanish.forEach(v => { - if (!this.enlightened || this.enlightened[v.x][v.y]) { - // TODO: next "if" shouldn't be required - if (this.g_pieces[v.x][v.y]) - this.g_pieces[v.x][v.y].remove(); - this.g_pieces[v.x][v.y] = null; - } + // TODO: next "if" shouldn't be required + if (this.g_pieces[v.x][v.y]) + this.g_pieces[v.x][v.y].remove(); + this.g_pieces[v.x][v.y] = null; }); let chessboard = document.getElementById(this.containerId).querySelector(".chessboard"); @@ -2203,8 +2088,6 @@ export default class ChessRules { r = chessboard.getBoundingClientRect(); const pieceWidth = this.getPieceWidth(r.width); move.appear.forEach(a => { - if (this.enlightened && !this.enlightened[a.x][a.y]) - return; this.g_pieces[a.x][a.y] = document.createElement("piece"); this.g_pieces[a.x][a.y].classList.add(this.pieces()[a.p]["class"]); this.g_pieces[a.x][a.y].classList.add(a.c == "w" ? "white" : "black"); @@ -2212,13 +2095,17 @@ export default class ChessRules { this.g_pieces[a.x][a.y].style.height = pieceWidth + "px"; const [ip, jp] = this.getPixelPosition(a.x, a.y, r); this.g_pieces[a.x][a.y].style.transform = `translate(${ip}px,${jp}px)`; + if (this.enlightened && !this.enlightened[a.x][a.y]) + this.g_pieces[a.x][a.y].classList.add("hidden"); chessboard.appendChild(this.g_pieces[a.x][a.y]); }); + if (this.options["dark"]) + this.graphUpdateEnlightened(); } playPlusVisual(move, r) { - this.playVisual(move, r); this.play(move); + this.playVisual(move, r); this.afterPlay(move); //user method } diff --git a/common.css b/common.css index 91fd2a6..6f8472c 100644 --- a/common.css +++ b/common.css @@ -293,6 +293,10 @@ piece { pointer-events: none; } +piece.hidden { + display: none; +} + /* Drawing of the board */ .chessboard_SVG { width: 100%; diff --git a/variants/Absorption/class.js b/variants/Absorption/class.js index ac3445b..9869414 100644 --- a/variants/Absorption/class.js +++ b/variants/Absorption/class.js @@ -14,7 +14,7 @@ export default class AbsorptionRules extends ChessRules { "doublemove", "progressive", "recycle", - "rifle", //TODO? absorb powers from afar? + "rifle", "teleport", "zen" ] @@ -22,22 +22,25 @@ export default class AbsorptionRules extends ChessRules { } pieces(color) { - const fusions = {{ + let fusions = { // amazon 'a': { "class": "amazon", - steps: [ - [0, 1], [0, -1], [1, 0], [-1, 0], - [1, 1], [1, -1], [-1, 1], [-1, -1] + moves: [ + { + steps: [ + [0, 1], [0, -1], [1, 0], [-1, 0], + [1, 1], [1, -1], [-1, 1], [-1, -1] + ] + }, + { + steps: [ + [1, 2], [1, -2], [-1, 2], [-1, -2], + [2, 1], [-2, 1], [2, -1], [-2, -1] + ], + range: 1 + } ] - -//TODO: steps object avec range + steps... "moving"? - - steps: [ - [1, 2], [1, -2], [-1, 2], [-1, -2], - [2, 1], [-2, 1], [2, -1], [-2, -1] - ], - steps: [[0, 1], [0, -1], [1, 0], [-1, 0]] }, // empress 'e': { @@ -51,18 +54,9 @@ export default class AbsorptionRules extends ChessRules { 'b': { "class": "bishop", steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]] - }, - // queen - 'q': { - "class": "queen", - }, - - }, - return ( - Object.assign( - super.pieces(color) - ) - ); + } + }; + return Object.assign(fusions, super.pieces(color)); } static get MergeComposed() { diff --git a/variants/Atomic/class.js b/variants/Atomic/class.js index ad2e9a1..b0662d3 100644 --- a/variants/Atomic/class.js +++ b/variants/Atomic/class.js @@ -24,8 +24,8 @@ export default class AtomicRules extends ChessRules { } constructor(o) { + o.options["atomic"] = true; super(o); - this.options["atomic"] = true; } canIplay(x, y) { diff --git a/variants/Balanced/class.js b/variants/Balanced/class.js index f466f08..5550b0d 100644 --- a/variants/Balanced/class.js +++ b/variants/Balanced/class.js @@ -11,8 +11,8 @@ export default class BalancedRules extends ChessRules { } constructor(o) { + o.options["balance"] = true; super(o); - this.options.balance = true; } }; diff --git a/variants/Benedict/class.js b/variants/Benedict/class.js index 12ef3c8..7c77e8b 100644 --- a/variants/Benedict/class.js +++ b/variants/Benedict/class.js @@ -22,14 +22,6 @@ export default class BenedictRules extends ChessRules { return false; } - get pawnSpecs() { - return Object.assign( - {}, - super.pawnSpecs, - { canCapture: false } - ); - } - canTake() { return false; } @@ -40,18 +32,20 @@ export default class BenedictRules extends ChessRules { const [color, piece] = [this.getColor(x, y), this.getPiece(x, y)]; const oppCol = C.GetOppCol(color); let squares = {}; - const specs = this.pieces(color)[piece]; - const steps = specs.attack || specs.steps; - outerLoop: for (let step of steps) { - let [i, j] = [x + step[0], this.computeY(y + step[1])]; - let nbSteps = 1; - while (this.onBoard(i, j) && this.board[i][j] == "") { - if (specs.range <= nbSteps++) continue outerLoop; - i += step[0]; - j = this.computeY(j + step[1]); + const specs = this.pieces(color, x, y)[piece]; + const attacks = specs.attack || specs.moves; + for (let a of attacks) { + outerLoop: for (let step of a.steps) { + let [i, j] = [x + step[0], this.computeY(y + step[1])]; + let nbSteps = 1; + while (this.onBoard(i, j) && this.board[i][j] == "") { + if (a.range <= nbSteps++) continue outerLoop; + i += step[0]; + j = this.computeY(j + step[1]); + } + if (this.onBoard(i, j) && this.getColor(i, j) == oppCol) + squares[C.CoordsToSquare({x: i, y: j})] = true; } - if (this.onBoard(i, j) && this.getColor(i, j) == oppCol) - squares[C.CoordsToSquare({x: i, y: j})] = true; } return Object.keys(squares); } diff --git a/variants/Cannibal/class.js b/variants/Cannibal/class.js index 9ac2161..e75aa20 100644 --- a/variants/Cannibal/class.js +++ b/variants/Cannibal/class.js @@ -11,8 +11,8 @@ export default class CannibalRules extends ChessRules { } constructor(o) { + o.options["cannibal"] = true; super(o); - this.options.cannibal = true; } }; diff --git a/variants/Capture/class.js b/variants/Capture/class.js index 5e69452..31264a1 100644 --- a/variants/Capture/class.js +++ b/variants/Capture/class.js @@ -11,8 +11,8 @@ export default class CaptureRules extends ChessRules { } constructor(o) { + o.options["capture"] = true; super(o); - this.options.capture = true; } }; diff --git a/variants/Crazyhouse/class.js b/variants/Crazyhouse/class.js index 0e1b14c..f01bd0c 100644 --- a/variants/Crazyhouse/class.js +++ b/variants/Crazyhouse/class.js @@ -11,8 +11,8 @@ export default class CrazyhouseRules extends ChessRules { } constructor(o) { + o.options["crazyhouse"] = true; super(o); - this.options.crazyhouse = true; } }; diff --git a/variants/Cylinder/class.js b/variants/Cylinder/class.js index cc86084..c35c346 100644 --- a/variants/Cylinder/class.js +++ b/variants/Cylinder/class.js @@ -11,8 +11,8 @@ export default class CylinderRules extends ChessRules { } constructor(o) { + o.options["cylinder"] = true; super(o); - this.options.cylinder = true; } }; diff --git a/variants/Dark/class.js b/variants/Dark/class.js index fb27c44..ad23cbc 100644 --- a/variants/Dark/class.js +++ b/variants/Dark/class.js @@ -11,8 +11,8 @@ export default class DarkRules extends ChessRules { } constructor(o) { + o.options["dark"] = true; super(o); - this.options.dark = true; } }; diff --git a/variants/Doublemove/class.js b/variants/Doublemove/class.js index fed88d8..551ae09 100644 --- a/variants/Doublemove/class.js +++ b/variants/Doublemove/class.js @@ -11,8 +11,8 @@ export default class DoublemoveRules extends ChessRules { } constructor(o) { + o.options["doublemove"] = true; super(o); - this.options.doublemove = true; } }; diff --git a/variants/Zen/class.js b/variants/Zen/class.js index b5c2bab..424452d 100644 --- a/variants/Zen/class.js +++ b/variants/Zen/class.js @@ -11,8 +11,8 @@ export default class ZenRules extends ChessRules { } constructor(o) { + o.options["zen"] = true; super(o); - this.options.zen = true; } }; -- 2.44.0