From ceeac4e82346ffba2d87d763df3cffcddaec912a Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Mon, 23 Jan 2023 20:08:42 +0100 Subject: [PATCH] Checkered: fixing attempt (untested) + refactor/generalize oppCol oppTurn logic --- base_rules.js | 94 ++++++++++++++++++------------------- variants.js | 2 +- variants/Checkered/class.js | 80 ++++++++++++++++--------------- 3 files changed, 89 insertions(+), 87 deletions(-) diff --git a/base_rules.js b/base_rules.js index a0defd1..aa499f8 100644 --- a/base_rules.js +++ b/base_rules.js @@ -470,7 +470,7 @@ export default class ChessRules { } getRankInReserve(c, p) { - const pieces = Object.keys(this.pieces()); + const pieces = Object.keys(this.pieces(c, c, p)); const lastIndex = pieces.findIndex(pp => pp == p) let toTest = pieces.slice(0, lastIndex); return toTest.reduce( @@ -519,7 +519,6 @@ export default class ChessRules { re_drawBoardElements() { const board = this.getSvgChessboard(); - const oppCol = C.GetOppCol(this.playerColor); const container = document.getElementById(this.containerId); const rc = container.getBoundingClientRect(); let chessboard = container.querySelector(".chessboard"); @@ -1065,7 +1064,8 @@ export default class ChessRules { // 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; + // TODO: (0, 0) is wrong, would need to place an attacker here... + const steps = this.pieces(this.playerColor, 0, 0)["p"].attack[0].steps; for (let step of steps) { const x = this.epSquare.x - step[0], y = this.getY(this.epSquare.y - step[1]); @@ -1141,7 +1141,7 @@ export default class ChessRules { getPieceType(x, y, p) { if (!p) p = this.getPiece(x, y); - return this.pieces()[p].moveas || p; + return this.pieces(this.getColor(x, y), x, y)[p].moveas || p; } isKing(x, y, p) { @@ -1152,8 +1152,12 @@ export default class ChessRules { return !!C.CannibalKings[p]; } - // Get opponent color - static GetOppCol(color) { + static GetOppTurn(color) { + return (color == 'w' ? 'b' : 'w'); + } + + // Get opponent color(s): may differ from turn (e.g. Checkered) + getOppCols(color) { return (color == "w" ? "b" : "w"); } @@ -1340,7 +1344,7 @@ export default class ChessRules { if (!this.options["madrasi"]) return false; const color = this.getColor(x, y); - const oppCol = C.GetOppCol(color); + const oppCols = this.getOppCols(color); const piece = this.getPieceType(x, y); const stepSpec = this.getStepSpec(color, x, y, piece); const attacks = stepSpec.both.concat(stepSpec.attack); @@ -1356,7 +1360,7 @@ export default class ChessRules { } if ( this.onBoard(i, j) && - this.getColor(i, j) == oppCol && + oppCols.includes(this.getColor(i, j)) && this.getPieceType(i, j) == piece ) { return true; @@ -1368,7 +1372,6 @@ export default class ChessRules { // Stop at the first capture found atLeastOneCapture(color) { - const oppCol = C.GetOppCol(color); const allowed = (sq1, sq2) => { return ( // NOTE: canTake is reversed for Zen. @@ -1504,19 +1507,19 @@ export default class ChessRules { if (moves.length == 0) return []; const color = this.getColor(moves[0].start.x, moves[0].start.y); - const oppCol = C.GetOppCol(color); + const oppCols = this.getOppCols(color); if (this.options["capture"] && this.atLeastOneCapture(color)) - moves = this.capturePostProcess(moves, oppCol); + moves = this.capturePostProcess(moves, oppCols); if (this.options["atomic"]) - moves = this.atomicPostProcess(moves, color, oppCol); + moves = this.atomicPostProcess(moves, color, oppCols); if ( moves.length > 0 && this.getPieceType(moves[0].start.x, moves[0].start.y) == "p" ) { - moves = this.pawnPostProcess(moves, color, oppCol); + moves = this.pawnPostProcess(moves, color, oppCols); } if (this.options["cannibal"] && this.options["rifle"]) @@ -1526,22 +1529,22 @@ export default class ChessRules { return moves; } - capturePostProcess(moves, oppCol) { + capturePostProcess(moves, oppCols) { // Filter out non-capturing moves (not using m.vanish because of // self captures of Recycle and Teleport). return moves.filter(m => { return ( this.board[m.end.x][m.end.y] != "" && - this.getColor(m.end.x, m.end.y) == oppCol + oppCols.includes(this.getColor(m.end.x, m.end.y)) ); }); } - atomicPostProcess(moves, color, oppCol) { + atomicPostProcess(moves, color, oppCols) { moves.forEach(m => { if ( this.board[m.end.x][m.end.y] != "" && - this.getColor(m.end.x, m.end.y) == oppCol + oppCols.includes(this.getColor(m.end.x, m.end.y)) ) { // Explosion! let steps = [ @@ -1596,7 +1599,7 @@ export default class ChessRules { return moves; } - pawnPostProcess(moves, color, oppCol) { + pawnPostProcess(moves, color, oppCols) { let moreMoves = []; const lastRank = (color == "w" ? 0 : this.size.x - 1); const initPiece = this.getPiece(moves[0].start.x, moves[0].start.y); @@ -1617,7 +1620,7 @@ export default class ChessRules { if ( this.options["cannibal"] && this.board[x2][y2] != "" && - this.getColor(x2, y2) == oppCol + oppCols.includes(this.getColor(x2, y2)) ) { finalPieces = [this.getPieceType(x2, y2)]; } @@ -1819,7 +1822,7 @@ export default class ChessRules { // Search for enemy (or not) pieces attacking [x, y] findCapturesOn([x, y], o, allowed) { if (!o.byCol) - o.byCol = [C.GetOppCol(this.getColor(x, y) || this.turn)]; + o.byCol = this.getOppCols(this.getColor(x, y) || this.turn); let res = []; for (let i=0; i oppCol.includes(this.getColor(i2, j2)) + ([i1, j1], [i2, j2]) => oppCols.includes(this.getColor(i2, j2)) ) ) ); } // Argument is (very generally) an array of squares (= arrays) - underCheck(square_s, oppCol) { + underCheck(square_s, oppCols) { if (this.options["taking"] || this.options["dark"]) return false; - return square_s.some(sq => this.underAttack(sq, oppCol)); + return square_s.some(sq => this.underAttack(sq, oppCols)); } // Scan board for king(s) @@ -2164,7 +2165,7 @@ export default class ChessRules { filterValid(moves, color) { if (!color) color = this.turn; - const oppCol = C.GetOppCol(color); + const oppCols = this.getOppCols(color); let kingPos = this.searchKingPos(color); let filtered = {}; //avoid re-checking similar moves (promotions...) return moves.filter(m => { @@ -2188,7 +2189,7 @@ export default class ChessRules { else res = false; //king vanished } - res &&= !this.underCheck(kingPos, oppCol); + res &&= !this.underCheck(kingPos, oppCols); if (oldKingPP && newKingPP) kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y]; this.undoOnBoard(m); @@ -2327,7 +2328,7 @@ export default class ChessRules { tryChangeTurn(move) { if (this.isLastMove(move)) { - this.turn = C.GetOppCol(this.turn); + this.turn = (this.turn == 'w' ? 'b' : 'w'); this.movesCount++; this.subTurn = 1; } @@ -2339,8 +2340,8 @@ export default class ChessRules { if (move.next) return false; const color = this.turn; - const oppKingPos = this.searchKingPos(C.GetOppCol(color)); - if (oppKingPos.length == 0 || this.underCheck(oppKingPos, color)) + const oppKingPos = this.searchKingPos(C.GetOppTurn(color)); + if (oppKingPos.length == 0 || this.underCheck(oppKingPos, [color])) return true; return ( ( @@ -2391,25 +2392,24 @@ export default class ChessRules { // Shortcut in case the score was computed before: if (move.result) return move.result; - const color = this.turn; - const oppCol = C.GetOppCol(color); + const oppTurn = C.GetOppTurn(this.turn); const kingPos = { - [color]: this.searchKingPos(color), - [oppCol]: this.searchKingPos(oppCol) + w: this.searchKingPos('w'), + b: this.searchKingPos('b') }; - if (kingPos[color].length == 0 && kingPos[oppCol].length == 0) + if (kingPos[this.turn].length == 0 && kingPos[oppTurn].length == 0) return "1/2"; - if (kingPos[color].length == 0) + if (kingPos[this.turn].length == 0) return (color == "w" ? "0-1" : "1-0"); - if (kingPos[oppCol].length == 0) + if (kingPos[oppTurn].length == 0) return (color == "w" ? "1-0" : "0-1"); - if (this.atLeastOneMove(color)) + if (this.atLeastOneMove(this.turn)) return "*"; // No valid move: stalemate or checkmate? - if (!this.underCheck(kingPos[color], oppCol)) + if (!this.underCheck(kingPos[this.turn], this.getOppCols(this.turn))) return "1/2"; // OK, checkmate - return (color == "w" ? "0-1" : "1-0"); + return (this.turn == "w" ? "0-1" : "1-0"); } playVisual(move, r) { diff --git a/variants.js b/variants.js index cd5e55a..ef297b6 100644 --- a/variants.js +++ b/variants.js @@ -26,7 +26,7 @@ const variants = [ {name: 'Capablanca', desc: 'Capablanca Chess', disp: 'Capablanca'}, {name: 'Capture', desc: 'Mandatory captures'}, {name: 'Chakart', desc: 'Capture the princess'}, -// {name: 'Checkered', desc: 'Shared pieces'}, + {name: 'Checkered', desc: 'Shared pieces'}, // {name: 'Checkless', desc: 'No-check mode'}, {name: 'Chess960', disp: "Chess 960", desc: "Standard rules"}, // {name: 'Circular', desc: 'Run forward'}, diff --git a/variants/Checkered/class.js b/variants/Checkered/class.js index 9d9bf24..9c78243 100644 --- a/variants/Checkered/class.js +++ b/variants/Checkered/class.js @@ -61,10 +61,13 @@ export default class CheckeredRules extends ChessRules { } getPartFen(o) { - let parts = super.getPartFen(o); - parts["cmove"] = this.getCmoveFen(); - parts["stage"] = this.getStageFen(); - return parts; + return Object.assign( + { + "cmove": o.init ? "-" : this.getCmoveFen(), + "stage": o.init ? "1" : this.getStageFen() + }, + super.getPartFen(o) + ); } getCmoveFen() { @@ -82,9 +85,10 @@ export default class CheckeredRules extends ChessRules { getFlagsFen() { let fen = super.getFlagsFen(); // Add pawns flags - for (let c of ["w", "b"]) + for (let c of ["w", "b"]) { for (let i = 0; i < 8; i++) fen += (this.pawnFlags[c][i] ? "1" : "0"); + } return fen; } @@ -92,6 +96,15 @@ export default class CheckeredRules extends ChessRules { return super.getPawnShift(color == 'c' ? this.turn : color); } + getOppCols(color) { + if (this.stage == 1) + return super.getOppCols(color).concat(['c']); + // Stage 2: depends if color is w+b or checkered + if (color == this.sideCheckered) + return ['w', 'b']; + return ['c']; + } + pieces(color, x, y) { let baseRes = super.pieces(color, x, y); if ( @@ -157,15 +170,6 @@ export default class CheckeredRules extends ChessRules { } } - aggregateFlags() { - return [this.castleFlags, this.pawnFlags]; - } - - disaggregateFlags(flags) { - this.castleFlags = flags[0]; - this.pawnFlags = flags[1]; - } - getEpSquare(moveOrSquare) { // At stage 2, all pawns can be captured en-passant if ( @@ -208,21 +212,19 @@ export default class CheckeredRules extends ChessRules { // Apply "checkerization" of standard moves const lastRank = (color == "w" ? 0 : 7); const [x, y] = [moves[0].start.x, moves[0].start.y]; - let moves = []; const piece = this.getPiece(x, y); // King is treated differently: it never turn checkered - if (piece == 'k') { + if (piece == 'k' && this.stage == 1) { // If at least one checkered piece, allow switching: if ( this.options["withswitch"] && this.board.some(b => b.some(cell => cell[0] == 'c')) ) { - const oppCol = C.GetOppCol(color); - const oppKingPos = this.searchKingPos(oppCol)[0]; + const oppKingPos = this.searchKingPos(C.GetOppTurn(this.turn))[0]; moves.push( new Move({ start: { x: x, y: y }, - end: { x: oppKingPos[0], y: oppKingPos[1] }, + end: {x: oppKingPos[0], y: oppKingPos[1]}, appear: [], vanish: [] }) @@ -233,7 +235,7 @@ export default class CheckeredRules extends ChessRules { if (piece == 'p') { // Filter out forbidden pawn moves moves = moves.filter(m => { - if (m.vanish[0].p == 'p') { + if (m.vanish.length > 0 && m.vanish[0].p == 'p') { if ( Math.abs(m.end.x - m.start.x) == 2 && !this.pawnFlags[this.turn][m.start.y] @@ -253,7 +255,7 @@ export default class CheckeredRules extends ChessRules { } let extraMoves = []; moves.forEach(m => { - if (m.vanish.length == 2 && m.appear.length == 1) + if (m.vanish.length == 2 && m.appear.length == 1) { // A capture occured m.appear[0].c = "c"; if ( @@ -271,7 +273,7 @@ export default class CheckeredRules extends ChessRules { return moves.concat(extraMoves); } - canIplay(side, [x, y]) { + canIplay(x, y) { if (this.stage == 2) { const color = this.getColor(x, y); return ( @@ -280,7 +282,10 @@ export default class CheckeredRules extends ChessRules { : ['w', 'b'].includes(color) ); } - return side == this.turn && [side, "c"].includes(this.getColor(x, y)); + return ( + this.playerColor == this.turn && + [this.turn, "c"].includes(this.getColor(x, y)) + ); } // Does m2 un-do m1 ? (to disallow undoing checkered moves) @@ -303,32 +308,31 @@ export default class CheckeredRules extends ChessRules { // Checkered cannot be under check (no king) return moves; let kingPos = super.searchKingPos(color); - const oppCol = C.GetOppCol(color); if (this.stage == 2) // Must consider both kings (attacked by checkered side) - kingPos = [kingPos, super.searchKingPos(oppCol)]; + kingPos = [kingPos, super.searchKingPos(C.GetOppTurn(this.turn))]; + const oppCols = this.getOppCols(color); return moves.filter(m => { this.playOnBoard(m); let res = true; if (stage == 1) res = !this.oppositeMoves(this.cmove, m); if (res && m.appear.length > 0) - // NOTE: oppCol might be inaccurate; fixed in underCheck() - res = !this.underCheck(kingPos, oppCol); + res = !this.underCheck(kingPos, oppCols); this.undoOnBoard(m); return res; }); } atLeastOneMove(color) { - const oppCol = C.GetOppCol(color); + const myCols = [color, 'c']; for (let i = 0; i < this.size.x; i++) { for (let j = 0; j < this.size.y; j++) { const colIJ = this.getColor(i, j); if ( this.board[i][j] != "" && ( - (this.stage == 1 && colIJ != oppCol) || + (this.stage == 1 && myCols.includes(colIJ)) || (this.stage == 2 && ( (this.sideCheckered == color && colIJ == 'c') || @@ -346,12 +350,10 @@ export default class CheckeredRules extends ChessRules { return false; } - underCheck(square_s, oppCol) { - if (this.stage == 1) - return super.underAttack(square_s, [oppCol, 'c']); - if (oppCol != this.sideCheckered) + underCheck(square_s, oppCols) { + if (this.stage == 2 && oppCol != this.sideCheckered) return false; //checkered pieces is me, I'm not under check - return super.underAttack(square_s, 'c'); + return super.underAttack(square_s, oppCols); } prePlay(move) { @@ -389,11 +391,11 @@ export default class CheckeredRules extends ChessRules { if (this.atLeastOneMove(color)) return "*"; // Artifically change turn, for checkered pawns - const oppCol = C.GetOppCol(color); - this.turn = oppCol; + const oppTurn = C.GetOppTurn(color); + this.turn = oppTurn; const kingPos = super.searchKingPos(color)[0]; let res = "1/2"; - if (super.underAttack(kingPos, [oppCol, 'c'])) + if (super.underAttack(kingPos, [oppTurn, 'c'])) res = (color == "w" ? "0-1" : "1-0"); this.turn = color; return res; @@ -410,9 +412,9 @@ export default class CheckeredRules extends ChessRules { } if (this.atLeastOneMove(color)) return "*"; - let res = super.underAttack(super.searchKingPos(color)[0], 'c'); + let res = super.underAttack(super.searchKingPos(color)[0], ['c']); if (!res) - res = super.underAttack(super.searchKingPos(oppCol)[0], 'c'); + res = super.underAttack(super.searchKingPos(oppCol)[0], ['c']); if (res) return (color == 'w' ? "0-1" : "1-0"); return "1/2"; -- 2.44.0