First draft of Magnetic chess
authorBenjamin Auder <benjamin.auder@somewhere>
Tue, 20 Nov 2018 16:01:02 +0000 (17:01 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Tue, 20 Nov 2018 16:01:02 +0000 (17:01 +0100)
public/javascripts/base_rules.js
public/javascripts/variants/Atomic.js
public/javascripts/variants/Magnetic.js [new file with mode: 0644]
variants.js

index cdd7572..7c52fc1 100644 (file)
@@ -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();
index d86ed0e..f32e21d 100644 (file)
@@ -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 (file)
index 0000000..e941532
--- /dev/null
@@ -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";
+       }
+}
index 6c2f62c..1036764 100644 (file)
@@ -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" },