From 1af36beb39aa5d59735483e915fd1040f93eecca Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Tue, 20 Nov 2018 17:01:02 +0100 Subject: [PATCH] First draft of Magnetic chess --- public/javascripts/base_rules.js | 16 +- public/javascripts/variants/Atomic.js | 5 +- public/javascripts/variants/Magnetic.js | 197 ++++++++++++++++++++++++ variants.js | 2 +- 4 files changed, 211 insertions(+), 9 deletions(-) create mode 100644 public/javascripts/variants/Magnetic.js diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js index cdd75727..7c52fc1c 100644 --- a/public/javascripts/base_rules.js +++ b/public/javascripts/base_rules.js @@ -710,7 +710,7 @@ class ChessRules ////////////// // END OF GAME - checkGameOver() + checkRepetition() { // Check for 3 repetitions if (this.moves.length >= 8) @@ -722,15 +722,19 @@ class ChessRules _.isEqual(this.moves[L-3], this.moves[L-7]) && _.isEqual(this.moves[L-4], this.moves[L-8])) { - return "1/2 (repetition)"; + return true; } } + return false; + } - if (this.atLeastOneMove()) - { - // game not over + checkGameOver() + { + if (this.checkRepetition()) + return "1/2"; + + if (this.atLeastOneMove()) // game not over return "*"; - } // Game over return this.checkGameEnd(); diff --git a/public/javascripts/variants/Atomic.js b/public/javascripts/variants/Atomic.js index d86ed0e1..f32e21d1 100644 --- a/public/javascripts/variants/Atomic.js +++ b/public/javascripts/variants/Atomic.js @@ -134,13 +134,14 @@ class AtomicRules extends ChessRules getCheckSquares(move) { const c = this.getOppCol(this.turn); - const saveKingPos = this.kingPos[c]; //king might explode + // King might explode: + const saveKingPos = JSON.parse(JSON.stringify(this.kingPos[c])); this.play(move); let res = [ ]; if (this.kingPos[c][0] < 0) res = [saveKingPos]; else if (this.isAttacked(this.kingPos[c], this.getOppCol(c))) - res = [ this.kingPos[c] ] + res = [ JSON.parse(JSON.stringify(this.kingPos[c])) ] this.undo(move); return res; } diff --git a/public/javascripts/variants/Magnetic.js b/public/javascripts/variants/Magnetic.js new file mode 100644 index 00000000..e9415322 --- /dev/null +++ b/public/javascripts/variants/Magnetic.js @@ -0,0 +1,197 @@ +class MagneticRules +{ + getEpSquare(move) + { + return undefined; //no en-passant + } + + // Complete a move with magnetic actions + applyMagneticLaws([x,y], move) + { + // TODO + } + + getBasicMove([sx,sy], [ex,ey], tr) + { + var mv = new Move({ + appear: [ + new PiPo({ + x: ex, + y: ey, + c: !!tr ? tr.c : this.getColor(sx,sy), + p: !!tr ? tr.p : this.getPiece(sx,sy) + }) + ], + vanish: [ + new PiPo({ + x: sx, + y: sy, + c: this.getColor(sx,sy), + p: this.getPiece(sx,sy) + }) + ] + }); + + if (this.board[ex][ey] != VariantRules.EMPTY) + { + mv.vanish.push( + new PiPo({ + x: ex, + y: ey, + c: this.getColor(ex,ey), + p: this.getPiece(ex,ey) + }) + ); + } + this.applyMagneticLaws([ex,ey], mv); + return mv; + } + + getCastleMoves([x,y]) + { + const c = this.getColor(x,y); + if (x != (c=="w" ? 7 : 0) || y != this.INIT_COL_KING[c]) + return []; //x isn't first rank, or king has moved (shortcut) + + const V = VariantRules; + + // Castling ? + const oppCol = this.getOppCol(c); + let moves = []; + let i = 0; + const finalSquares = [ [2,3], [6,5] ]; //king, then rook + castlingCheck: + for (let castleSide=0; castleSide < 2; castleSide++) //large, then small + { + if (!this.flags[c][castleSide]) + continue; + // If this code is reached, rooks and king are on initial position + + // Nothing on the path of the king (and no checks; OK also if y==finalSquare)? + let step = finalSquares[castleSide][0] < y ? -1 : 1; + for (i=y; i!=finalSquares[castleSide][0]; i+=step) + { + if (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,V.ROOK].includes(this.getPiece(x,i))))) + { + continue castlingCheck; + } + } + + // Nothing on the path to the rook? + step = castleSide == 0 ? -1 : 1; + for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) + { + if (this.board[x][i] != V.EMPTY) + continue castlingCheck; + } + const rookPos = this.INIT_COL_ROOK[c][castleSide]; + + // Nothing on final squares, except maybe king and castling rook? + for (i=0; i<2; i++) + { + if (this.board[x][finalSquares[castleSide][i]] != V.EMPTY && + this.getPiece(x,finalSquares[castleSide][i]) != V.KING && + finalSquares[castleSide][i] != rookPos) + { + continue castlingCheck; + } + } + + // If this code is reached, castle is valid + let cmove = new Move({ + appear: [ + new PiPo({x:x,y:finalSquares[castleSide][0],p:V.KING,c:c}), + new PiPo({x:x,y:finalSquares[castleSide][1],p:V.ROOK,c:c})], + vanish: [ + new PiPo({x:x,y:y,p:V.KING,c:c}), + new PiPo({x:x,y:rookPos,p:V.ROOK,c:c})], + end: Math.abs(y - rookPos) <= 2 + ? {x:x, y:rookPos} + : {x:x, y:y + 2 * (castleSide==0 ? -1 : 1)} + }); + this.applyMagneticLaws([x,finalSquares[castleSide][1]], cmove); + moves.push(cmove); + } + + return moves; + } + + // TODO: verify this assertion +// atLeastOneMove() +// { +// return true; //always at least one possible move +// } + + underCheck(move) + { + return false; //there is no check + } + + getCheckSquares(move) + { + const c = this.getOppCol(this.turn); //opponent + const saveKingPos = this.kingPos[c]; //king might be taken + this.play(move); + // The only way to be "under check" is to have lost the king (thus game over) + let res = this.kingPos[c][0] < 0; + ? [ JSON.parse(JSON.stringify(saveKingPos)) ] + : [ ]; + this.undo(move); + return res; + } + + updateVariables(move) + { + super.updateVariables(move); + const c = this.getColor(move.start.x,move.start.y); + if (c != this.getColor(move.end.x,move.end.y) + && this.board[move.end.x][move.end.y] != VariantRules.EMPTY + && this.getPiece(move.end.x,move.end.y) == VariantRules.KING) + { + // We took opponent king ! + const oppCol = this.getOppCol(c); + this.kingPos[oppCol] = [-1,-1]; + this.flags[oppCol] = [false,false]; + } + } + + unupdateVariables(move) + { + super.unupdateVariables(move); + const c = this.getColor(move.start.x,move.start.y); + const oppCol = this.getOppCol(c); + if (this.kingPos[oppCol][0] < 0) + { + // Last move took opponent's king + for (let psq of move.vanish) + { + if (psq.p == 'k') + { + this.kingPos[oppCol] = [psq.x, psq.y]; + break; + } + } + } + } + + checkGameOver() + { + if (this.checkRepetition()) + return "1/2"; + + const color = this.turn; + // TODO: do we need "atLeastOneMove()"? + if (this.atLeastOneMove() && this.kingPos[color][0] >= 0) + return "*"; + + return this.checkGameEnd(); + } + + checkGameEnd() + { + // No valid move: our king disappeared + return this.turn == "w" ? "0-1" : "1-0"; + } +} diff --git a/variants.js b/variants.js index 6c2f62cf..10367645 100644 --- a/variants.js +++ b/variants.js @@ -4,7 +4,7 @@ module.exports = [ { "name" : "Atomic", "description" : "Explosive captures" }, { "name" : "Chess960", "description" : "Standard rules" }, { "name" : "Antiking", "description" : "Keep antiking in check" }, -// { "name" : "Magnetic", "description" : "Laws of attraction" }, + { "name" : "Magnetic", "description" : "Laws of attraction" }, // { "name" : "Alice", "description" : "Both sides of the mirror" }, // { "name" : "Grand", "description" : "Big board" }, // { "name" : "Wildebeest", "description" : "Balanced sliders & leapers" }, -- 2.44.0