Early draft of Wildebeest + Grand
authorBenjamin Auder <benjamin.auder@somewhere>
Mon, 26 Nov 2018 15:20:05 +0000 (16:20 +0100)
committerBenjamin Auder <benjamin.auder@somewhere>
Mon, 26 Nov 2018 15:20:05 +0000 (16:20 +0100)
public/javascripts/base_rules.js
public/javascripts/variants/Antiking.js
public/javascripts/variants/Atomic.js
public/javascripts/variants/Grand.js [new file with mode: 0644]
public/javascripts/variants/Wildebeest.js [new file with mode: 0644]
public/javascripts/variants/Zen.js
variants.js

index 9cfab5a..f466624 100644 (file)
@@ -171,7 +171,6 @@ class ChessRules
                        'r': [ [-1,0],[1,0],[0,-1],[0,1] ],
                        'n': [ [-1,-2],[-1,2],[1,-2],[1,2],[-2,-1],[-2,1],[2,-1],[2,1] ],
                        'b': [ [-1,-1],[-1,1],[1,-1],[1,1] ],
-                       'q': [ [-1,0],[1,0],[0,-1],[0,1],[-1,-1],[-1,1],[1,-1],[1,1] ]
                };
        }
 
@@ -379,14 +378,17 @@ class ChessRules
        // What are the queen moves from square x,y ?
        getPotentialQueenMoves(sq)
        {
-               return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN]);
+               const V = VariantRules;
+               return this.getSlideNJumpMoves(sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
        }
 
        // What are the king moves from square x,y ?
        getPotentialKingMoves(sq)
        {
+               const V = VariantRules;
                // Initialize with normal moves
-               let moves = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+               let moves = this.getSlideNJumpMoves(sq,
+                       V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
                return moves.concat(this.getCastleMoves(sq));
        }
 
@@ -586,15 +588,17 @@ class ChessRules
        // Is square x,y attacked by queens of color c ?
        isAttackedByQueen(sq, colors)
        {
-               return this.isAttackedBySlideNJump(sq, colors,
-                       VariantRules.QUEEN, VariantRules.steps[VariantRules.QUEEN]);
+               const V = VariantRules;
+               return this.isAttackedBySlideNJump(sq, colors, V.QUEEN,
+                       V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
        }
 
        // Is square x,y attacked by king of color c ?
        isAttackedByKing(sq, colors)
        {
-               return this.isAttackedBySlideNJump(sq, colors,
-                       VariantRules.KING, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+               const V = VariantRules;
+               return this.isAttackedBySlideNJump(sq, colors, V.KING,
+                       V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
        }
 
        // Generic method for non-pawn pieces ("sliding or jumping"): is x,y attacked by piece != color ?
index 53e5518..f8c7465 100644 (file)
@@ -59,7 +59,9 @@ class AntikingRules extends ChessRules
 
        getPotentialAntikingMoves(sq)
        {
-               return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+               const V = VariantRules;
+               return this.getSlideNJumpMoves(sq,
+                       V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
        }
 
        isAttacked(sq, colors)
@@ -69,18 +71,20 @@ class AntikingRules extends ChessRules
 
        isAttackedByKing([x,y], colors)
        {
-               if (this.getPiece(x,y) == VariantRules.ANTIKING)
+               const V = VariantRules;
+               if (this.getPiece(x,y) == V.ANTIKING)
                        return false; //antiking is not attacked by king
-               return this.isAttackedBySlideNJump([x,y], colors,
-                       VariantRules.KING, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+               return this.isAttackedBySlideNJump([x,y], colors, V.KING,
+                       V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
        }
 
        isAttackedByAntiking([x,y], colors)
        {
-               if (this.getPiece(x,y) == VariantRules.KING)
+               const V = VariantRules;
+               if (this.getPiece(x,y) == V.KING)
                        return false; //king is not attacked by antiking
-               return this.isAttackedBySlideNJump([x,y], colors,
-                       VariantRules.ANTIKING, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+               return this.isAttackedBySlideNJump([x,y], colors, V.ANTIKING,
+                       V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
        }
 
        underCheck(move)
index 2b7b1ac..b86e782 100644 (file)
@@ -30,10 +30,11 @@ class AtomicRules extends ChessRules
 
        getPotentialKingMoves([x,y])
        {
+               const V = VariantRules;
                // King cannot capture:
                let moves = [];
-               let [sizeX,sizeY] = VariantRules.size;
-               const steps = VariantRules.steps[VariantRules.QUEEN];
+               let [sizeX,sizeY] = V.size;
+               const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
                for (let step of steps)
                {
                        var i = x + step[0];
diff --git a/public/javascripts/variants/Grand.js b/public/javascripts/variants/Grand.js
new file mode 100644 (file)
index 0000000..63108de
--- /dev/null
@@ -0,0 +1,93 @@
+//https://www.chessvariants.com/large.dir/freeling.html
+class GrandRules extends ChessRules
+{
+       static getPpath(b)
+       {
+               const V = VariantRules;
+               return ([V.CAMEL,V.WILDEBEEST].includes(b[1]) ? "Grand/" : "") + b;
+       }
+
+       static get MARSHALL() { return 'm'; } //rook+knight
+       static get CARDINAL() { return 'c'; } //bishop+knight
+
+       // ------> pas les règles exactes, on démarre en 2 avec 1,2 ou 3 cases + enPassant comme Wildebeest
+// TODO: IN epSquares (return array), not return singleton. Easy. Adapt
+// just here for now...
+       getEpSquare(move)
+       {
+               const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
+               if (this.getPiece(sx,sy) == VariantRules.PAWN && Math.abs(sx - ex) == 2)
+               {
+                       return {
+                               x: (sx + ex)/2,
+                               y: sy
+                       };
+               }
+               return undefined; //default
+       }
+
+       getPotentialMovesFrom([x,y])
+       {
+               switch (this.getPiece(x,y))
+               {
+                       case VariantRules.MARSHALL:
+                               return this.getPotentialMarshallMoves([x,y]);
+                       case VariantRules.CARDINAL:
+                               return this.getPotentialCardinalMoves([x,y]);
+                       default:
+                               return super.getPotentialMovesFrom([x,y])
+               }
+       }
+
+       // TODO: quite many changes! Especially promotions, only to friendly pieces already captured. ==> keep track of captured material in initVariables()......
+       getPotentialPawnMoves([x,y])
+       {
+       }
+
+       getPotentialMarshallMoves(sq)
+       {
+               const V = VariantRules;
+               return this.getSlideNJumpMoves(sq, V.steps[V.ROOK]).concat(
+                       this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"));
+       }
+
+       getPotentialCardinalMoves(sq)
+       {
+               const V = VariantRules;
+               return this.getSlideNJumpMoves(sq, V.steps[V.BISHOP]).concat(
+                       this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT], "oneStep"));
+       }
+
+       isAttacked(sq, colors)
+       {
+               return (super.isAttacked(sq, colors)
+                       || this.isAttackedByMarshall(sq, colors)
+                       || this.isAttackedByCardinal(sq, colors);
+       }
+
+       isAttackedByMarshall(sq, colors)
+       {
+               const V = VariantRules;
+               return this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.ROOK])
+                       || this.isAttackedBySlideNJump(sq, colors, V.MARSHALL, V.steps[V.KNIGHT], "oneStep");
+       }
+
+       isAttackedByCardinal(sq, colors)
+       {
+               const V = VariantRules;
+               return this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.BISHOP])
+                       || this.isAttackedBySlideNJump(sq, colors, V.CARDINAL, V.steps[V.KNIGHT], "oneStep");
+       }
+
+       static get VALUES() {
+               return Object.assign(
+                       ChessRules.VALUES,
+                       {'c': 5, 'm': 7} //experimental
+               );
+       }
+
+       // TODO:
+       static GenRandInitFen()
+       {
+       }
+}
diff --git a/public/javascripts/variants/Wildebeest.js b/public/javascripts/variants/Wildebeest.js
new file mode 100644 (file)
index 0000000..5f706cf
--- /dev/null
@@ -0,0 +1,211 @@
+//https://www.chessvariants.com/large.dir/wildebeest.html
+class GrandRules extends ChessRules
+{
+       static getPpath(b)
+       {
+               const V = VariantRules;
+               return ([V.CAMEL,V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b;
+       }
+
+       static get CAMEL() { return 'c'; }
+       static get WILDEBEEST() { return 'w'; }
+
+       static get steps() {
+               return Object.assign(
+                       ChessRules.steps, //add camel moves:
+                       {'c': [ [-3,-1],[-3,1],[-1,-3],[-1,3],[1,-3],[1,3],[3,-1],[3,1] ]}
+               );
+       }
+
+// TODO: IN epSquares (return array), not return singleton. Easy. Adapt
+// just here for now...
+       getEpSquare(move)
+       {
+               const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
+               if (this.getPiece(sx,sy) == VariantRules.PAWN && Math.abs(sx - ex) == 2)
+               {
+                       return {
+                               x: (sx + ex)/2,
+                               y: sy
+                       };
+               }
+               return undefined; //default
+       }
+
+       getPotentialMovesFrom([x,y])
+       {
+               switch (this.getPiece(x,y))
+               {
+                       case VariantRules.CAMEL:
+                               return this.getPotentialCamelMoves([x,y]);
+                       case VariantRules.WILDEBEEST:
+                               return this.getPotentialWildebeestMoves([x,y]);
+                       default:
+                               return super.getPotentialMovesFrom([x,y])
+               }
+       }
+
+       // TODO: several changes (promote to queen or wildebeest)
+       getPotentialPawnMoves([x,y])
+       {
+               const color = this.turn;
+               let moves = [];
+               const V = VariantRules;
+               const [sizeX,sizeY] = VariantRules.size;
+               const shift = (color == "w" ? -1 : 1);
+               const firstRank = (color == 'w' ? sizeY-1 : 0);
+               const startRank = (color == "w" ? sizeY-2 : 1);
+               const lastRank = (color == "w" ? 0 : sizeY-1);
+
+               if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank)
+               {
+                       // Normal moves
+                       if (this.board[x+shift][y] == V.EMPTY)
+                       {
+                               moves.push(this.getBasicMove([x,y], [x+shift,y]));
+                               // Next condition because variants with pawns on 1st rank generally allow them to jump
+                               if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
+                               {
+                                       // Two squares jump
+                                       moves.push(this.getBasicMove([x,y], [x+2*shift,y]));
+                               }
+                       }
+                       // Captures
+                       if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+                               moves.push(this.getBasicMove([x,y], [x+shift,y-1]));
+                       if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+                               moves.push(this.getBasicMove([x,y], [x+shift,y+1]));
+               }
+
+               if (x+shift == lastRank)
+               {
+                       // Promotion
+                       let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
+                       promotionPieces.forEach(p => {
+                               // Normal move
+                               if (this.board[x+shift][y] == V.EMPTY)
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p}));
+                               // Captures
+                               if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p}));
+                               if (y<sizeY-1 && this.canTake([x,y], [x+shift,y+1]) && this.board[x+shift][y+1] != V.EMPTY)
+                                       moves.push(this.getBasicMove([x,y], [x+shift,y+1], {c:color,p:p}));
+                       });
+               }
+
+               // En passant
+               const Lep = this.epSquares.length;
+               const epSquare = Lep>0 ? this.epSquares[Lep-1] : undefined;
+               if (!!epSquare && epSquare.x == x+shift && Math.abs(epSquare.y - y) == 1)
+               {
+                       let epStep = epSquare.y - y;
+                       var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]);
+                       enpassantMove.vanish.push({
+                               x: x,
+                               y: y+epStep,
+                               p: 'p',
+                               c: this.getColor(x,y+epStep)
+                       });
+                       moves.push(enpassantMove);
+               }
+
+               return moves;
+       }
+
+       getPotentialCamelMoves(sq)
+       {
+               return this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.CAMEL], "oneStep");
+       }
+
+       getPotentialWildebeestMoves(sq)
+       {
+               const V = VariantRules;
+               return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]));
+       }
+
+       // TODO: getCastleMoves, generalize a bit to include castleSquares as static variables
+       // ==> but this won't be exactly Wildebeest... think about it.
+
+       // TODO: also generalize lastRank ==> DO NOT HARDCODE 7 !!!
+
+       isAttacked(sq, colors)
+       {
+               return (super.isAttacked(sq, colors)
+                       || this.isAttackedByCamel(sq, colors)
+                       || this.isAttackedByWildebeest(sq, colors);
+       }
+
+       isAttackedByCamel(sq, colors)
+       {
+               return this.isAttackedBySlideNJump(sq, colors,
+                       VariantRules.CAMEL, VariantRules.steps[VariantRules.CAMEL]);
+       }
+
+       isAttackedByWildebeest(sq, colors)
+       {
+               const V = VariantRules;
+               return this.isAttackedBySlideNJump(sq, colors, V.WILDEBEEST,
+                       V.steps[V.KNIGHT].concat(V.steps[V.CAMEL]));
+       }
+
+       static get VALUES() {
+               return Object.assign(
+                       ChessRules.VALUES,
+                       {'c': 3, 'w': 7} //experimental
+               );
+       }
+
+       // TODO:
+       static GenRandInitFen()
+       {
+               let pieces = [new Array(8), new Array(8)];
+               // Shuffle pieces on first and last rank
+               for (let c = 0; c <= 1; c++)
+               {
+                       let positions = _.range(8);
+
+                       // Get random squares for bishops
+                       let randIndex = 2 * _.random(3);
+                       let bishop1Pos = positions[randIndex];
+                       // The second bishop must be on a square of different color
+                       let randIndex_tmp = 2 * _.random(3) + 1;
+                       let bishop2Pos = positions[randIndex_tmp];
+                       // Remove chosen squares
+                       positions.splice(Math.max(randIndex,randIndex_tmp), 1);
+                       positions.splice(Math.min(randIndex,randIndex_tmp), 1);
+
+                       // Get random squares for knights
+                       randIndex = _.random(5);
+                       let knight1Pos = positions[randIndex];
+                       positions.splice(randIndex, 1);
+                       randIndex = _.random(4);
+                       let knight2Pos = positions[randIndex];
+                       positions.splice(randIndex, 1);
+
+                       // Get random square for queen
+                       randIndex = _.random(3);
+                       let queenPos = positions[randIndex];
+                       positions.splice(randIndex, 1);
+
+                       // Rooks and king positions are now fixed, because of the ordering rook-king-rook
+                       let rook1Pos = positions[0];
+                       let kingPos = positions[1];
+                       let rook2Pos = positions[2];
+
+                       // Finally put the shuffled pieces in the board array
+                       pieces[c][rook1Pos] = 'r';
+                       pieces[c][knight1Pos] = 'n';
+                       pieces[c][bishop1Pos] = 'b';
+                       pieces[c][queenPos] = 'q';
+                       pieces[c][kingPos] = 'k';
+                       pieces[c][bishop2Pos] = 'b';
+                       pieces[c][knight2Pos] = 'n';
+                       pieces[c][rook2Pos] = 'r';
+               }
+               let fen = pieces[0].join("") +
+                       "/pppppppp/8/8/8/8/PPPPPPPP/" +
+                       pieces[1].join("").toUpperCase() +
+                       " 1111"; //add flags
+               return fen;
+       }
+}
index b7f229c..17c922d 100644 (file)
@@ -157,7 +157,8 @@ class ZenRules extends ChessRules
 
        getPotentialKnightMoves(sq)
        {
-               let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.KNIGHT], "oneStep");
+               let noCaptures = this.getSlideNJumpMoves(sq,
+                       VariantRules.steps[VariantRules.KNIGHT], "oneStep");
                let captures = this.findCaptures(sq);
                return noCaptures.concat(captures);
        }
@@ -171,15 +172,18 @@ class ZenRules extends ChessRules
 
        getPotentialQueenMoves(sq)
        {
-               let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN]);
+               const V = VariantRules;
+               let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.ROOK.concat(V.steps[V.BISHOP])]);
                let captures = this.findCaptures(sq);
                return noCaptures.concat(captures);
        }
 
        getPotentialKingMoves(sq)
        {
+               const V = VariantRules;
                // Initialize with normal moves
-               let noCaptures = this.getSlideNJumpMoves(sq, VariantRules.steps[VariantRules.QUEEN], "oneStep");
+               let noCaptures = this.getSlideNJumpMoves(sq,
+                       V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep");
                let captures = this.findCaptures(sq);
                return noCaptures.concat(captures).concat(this.getCastleMoves(sq));
        }
index 7aef900..175c426 100644 (file)
@@ -6,8 +6,8 @@ module.exports = [
   { "name" : "Antiking", "description" : "Keep antiking in check" },
   { "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" },
+  { "name" : "Grand", "description" : "Big board" },
+  { "name" : "Wildebeest", "description" : "Balanced sliders & leapers" },
 //  { "name" : "Loser", "description" : "Lose all pieces" },
 //  { "name" : "Crazyhouse", "description" : "Captures reborn" },
 //  { "name" : "Switching", "description" : "Exchange pieces positions" },