'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] ]
};
}
// 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));
}
// 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 ?
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)
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)
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];
--- /dev/null
+//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()
+ {
+ }
+}
--- /dev/null
+//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;
+ }
+}
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);
}
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));
}
{ "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" },