(But it's really just for Magnetic chess)
setInterval "CRON" task in sockets.js to check connected clients
(every 1hour maybe, or more)
+Systematically show init+dest squares in PGN, maybe after short notation
+(2 moves list, second for de-ambiguification)
{
let i = x + step[0];
let j = y + step[1];
- while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == VariantRules.EMPTY)
+ while (i>=0 && i<sizeX && j>=0 && j<sizeY
+ && this.board[i][j] == VariantRules.EMPTY)
{
moves.push(this.getBasicMove([x,y], [i,j]));
if (oneStep !== undefined)
const color = this.turn;
let moves = [];
const V = VariantRules;
- const [sizeX,sizeY] = VariantRules.size;
+ const [sizeX,sizeY] = V.size;
const shift = (color == "w" ? -1 : 1);
const firstRank = (color == 'w' ? sizeX-1 : 0);
const startRank = (color == "w" ? sizeX-2 : 1);
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
+ // Next condition because variants with pawns on 1st rank allow them to jump
if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY)
{
// Two squares jump
}
}
// Captures
- if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY)
+ 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)
+ }
+ 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)
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)
+ 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)
+ }
+ 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}));
+ }
});
}
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 ?
+ // Generic method for non-pawn pieces ("sliding or jumping"):
+ // is x,y attacked by piece !of color in colors?
isAttackedBySlideNJump([x,y], colors, piece, steps, oneStep)
{
const [sizeX,sizeY] = VariantRules.size;
);
}
elementArray.push(gameDiv);
- // if (!!vr.reserve)
- // {
- // let reserve = h('div',
- // {'class':{'game':true}}, [
- // h('div',
- // { 'class': { 'row': true }},
- // [
- // h('div',
- // {'class':{'board':true}},
- // [h('img',{'class':{"piece":true},attrs:{"src":"/images/pieces/wb.svg"}})]
- // )
- // ]
- // )
- // ],
- // );
- // elementArray.push(reserve);
- // }
+ if (!!this.vr.reserve) //TODO: table, show counts for reserve pieces
+ //<tr style="padding:0">
+ // <td style="padding:0;font-size:10px">3</td>
+ {
+ let reservePiecesArray = [];
+ for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
+ {
+ reservePiecesArray.push(h('img',
+ {
+ 'class': {"piece":true},
+ attrs: {
+ "src": "/images/pieces/" +
+ this.vr.getReservePpath(this.mycolor,i) + ".svg",
+ id: this.getSquareId({x:sizeX,y:i}),
+ }
+ })
+ );
+ }
+ let reserve = h('div',
+ {'class':{'game':true}}, [
+ h('div',
+ { 'class': { 'row': true }},
+ [
+ h('div',
+ {'class':{'board':true, ['board'+sizeY]:true}},
+ reservePiecesArray
+ )
+ ]
+ )
+ ],
+ );
+ elementArray.push(reserve);
+ }
const eogMessage = this.getEndgameMessage(this.score);
const modalEog = [
h('input',
{
initVariables(fen)
{
- super.initVariables();
+ super.initVariables(fen);
// Also init reserves (used by the interface to show landing pieces)
const V = VariantRules;
this.reserve =
[V.QUEEN]: 0,
}
};
- // It may be a continuation: adjust numbers of pieces according to captures + rebirths
- // TODO
+ // May be a continuation: adjust numbers of pieces according to captures + rebirths
+ this.moves.forEach(m => {
+ if (m.vanish.length == 2)
+ this.reserve[m.appear[0].c][m.vanish[1].p]++;
+ else if (m.vanish.length == 0)
+ this.reserve[m.appear[0].c][m.appear[0].p]--;
+ });
}
// Used by the interface:
- getReservePieces(color)
+ getReservePpath(color, index)
{
- return {
- [color+V.PAWN]: this.reserve[color][V.PAWN],
- [color+V.ROOK]: this.reserve[color][V.ROOK],
- [color+V.KNIGHT]: this.reserve[color][V.KNIGHT],
- [color+V.BISHOP]: this.reserve[color][V.BISHOP],
- [color+V.QUEEN]: this.reserve[color][V.QUEEN],
- };
+ return color + VariantRules.RESERVE_PIECES[index];
}
- getPotentialMovesFrom([x,y])
+ // Put an ordering on reserve pieces
+ static get RESERVE_PIECES() {
+ const V = VariantRules;
+ return [V.PAWN,V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
+ }
+
+ getReserveMoves([x,y])
{
- let moves = super.getPotentialMovesFrom([x,y]);
- // Add landing moves:
const color = this.turn;
- Object.keys(this.reserve[color]).forEach(p => {
-
- moves.push(...); //concat... just appear
- });
+ const p = VariantRules.RESERVE_PIECES[y];
+ if (this.reserve[color][p] == 0)
+ return [];
+ let moves = [];
+ for (let i=0; i<sizeX; i++)
+ {
+ for (let j=0; j<sizeY; j++)
+ {
+ if (this.board[i][j] != VariantRules.EMPTY)
+ {
+ let mv = new Move({
+ appear: [
+ new PiPo({
+ x: i,
+ y: j,
+ c: color,
+ p: p
+ })
+ ]
+ });
+ moves.push(mv);
+ }
+ }
+ }
return moves;
}
- // TODO: condition "if this is reserve" --> special square !!! coordinates ??
- getPossibleMovesFrom(sq)
+ getPotentialMovesFrom([x,y])
{
- // Assuming color is right (already checked)
- return this.filterValid( this.getPotentialMovesFrom(sq) );
+ const sizeX = VariantRules.size[0];
+ if (x < sizeX)
+ return super.getPotentialMovesFrom([x,y]);
+ // Reserves, outside of board: x == sizeX
+ return this.getReserveMoves([x,y]);
}
- // TODO: add reserve moves
getAllValidMoves()
{
-
+ let moves = super.getAllValidMoves();
+ const color = this.turn;
+ const sizeX = VariantRules.size[0];
+ for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
+ moves = moves.concat(this.getReserveMoves([sizeX,i]));
+ return this.filterValid(moves);
}
- // TODO: also
atLeastOneMove()
{
-
+ if (!super.atLeastOneMove())
+ {
+ const sizeX = VariantRules.size[0];
+ // Scan for reserve moves
+ for (let i=0; i<VariantRules.RESERVE_PIECES.length; i++)
+ {
+ let moves = this.filterValid(this.getReserveMoves([sizeX,i]));
+ if (moves.length > 0)
+ return true;
+ }
+ return false;
+ }
+ return true;
}
- // TODO: update reserve
updateVariables(move)
{
+ super.updateVariables(move);
+ const color = this.turn;
+ if (move.vanish.length==2)
+ this.reserve[color][move.appear[0].p]++;
+ if (move.vanish.length==0)
+ this.reserve[color][move.appear[0].p]--;
}
+
unupdateVariables(move)
{
+ super.unupdateVariables(move);
+ const color = this.turn;
+ if (move.vanish.length==2)
+ this.reserve[color][move.appear[0].p]--;
+ if (move.vanish.length==0)
+ this.reserve[color][move.appear[0].p]++;
}
static get SEARCH_DEPTH() { return 2; } //high branching factor
};
}
+ getPotentialPawnMoves([x,y])
+ {
+ let moves = super.getPotentialPawnMoves([x,y]);
+ // Add potential promotions into king
+ const color = this.turn;
+ const V = VariantRules;
+ const [sizeX,sizeY] = V.size;
+ const shift = (color == "w" ? -1 : 1);
+ const lastRank = (color == "w" ? 0 : sizeX-1);
+
+ if (x+shift == lastRank)
+ {
+ // Normal move
+ if (this.board[x+shift][y] == V.EMPTY)
+ moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:V.KING}));
+ // 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:V.KING}));
+ }
+ 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:V.KING}));
+ }
+ }
+
+ return moves;
+ }
+
// TODO: verify this assertion
atLeastOneMove()
{
updateVariables(move)
{
super.updateVariables(move);
- if (move.vanish.length==2 && move.appear.length==1)
+ // Treat the promotion case: (not the capture part)
+ if (move.appear[0].p != move.vanish[0].p)
+ {
+ this.material[move.appear[0].c][move.appear[0].p]++;
+ this.material[move.appear[0].c][VariantRules.PAWN]--;
+ }
+ if (move.vanish.length==2 && move.appear.length==1) //capture
this.material[move.vanish[1].c][move.vanish[1].p]--;
}
unupdateVariables(move)
{
super.unupdateVariables(move);
+ if (move.appear[0].p != move.vanish[0].p)
+ {
+ this.material[move.appear[0].c][move.appear[0].p]--;
+ this.material[move.appear[0].c][VariantRules.PAWN]++;
+ }
if (move.vanish.length==2 && move.appear.length==1)
this.material[move.vanish[1].c][move.vanish[1].p]++;
}
return "*";
}
- return this.checkGameEnd();
+ return this.checkGameEnd(); //NOTE: currently unreachable...
+ }
+
+ checkGameEnd()
+ {
+ return this.turn == "w" ? "0-1" : "1-0";
}
// Very negative (resp. positive) if white (reps. black) pieces set is incomplete
evalPosition()
{
- if (this.missAkind())
- return (this.turn=="w"?-1:1) * VariantRules.INFINITY;
+ const color = this.turn;
+ if (Object.keys(this.material[color]).some(
+ p => { return this.material[color][p] == 0; }))
+ {
+ return (color=="w"?-1:1) * VariantRules.INFINITY;
+ }
return super.evalPosition();
}
}
// Scan move for pawn (max 1) on 8th rank
for (let i=1; i<move.appear.length; i++)
{
- if (move.appear[i].p==V.PAWN && move.appear[i].c==color && move.appear[i].x==lastRank)
+ if (move.appear[i].p==V.PAWN && move.appear[i].c==color
+ && move.appear[i].x==lastRank)
{
move.appear[i].p = V.ROOK;
moves.push(move);
-//https://www.chessvariants.com/diffmove.dir/switching.html
class SwitchingRules extends ChessRules
{
- //TODO:
- // Move completion: promote switched pawns (as in Magnetic)
+ // Build switch move between squares x1,y1 and x2,y2
+ getSwitchMove_s([x1,y1],[x2,y2])
+ {
- // To every piece potential moves: add switchings
+ const c = this.getColor(x1,y1); //same as color at square 2
+ const p1 = this.getPiece(x1,y1);
+ const p2 = this.getPiece(x2,y2);
+ let move = new Move({
+ appear: [
+ new PiPo({x:x2,y:y2,c:c,p:p1}),
+ new PiPo({x:x1,y:y1,c:c,p:p2})
+ ],
+ vanish: [
+ new PiPo({x:x1,y:y1,c:c,p:p1}),
+ new PiPo({x:x2,y:y2,c:c,p:p2})
+ ],
+ start: {x:x1,y:y1},
+ end: {x:x2,y:y2}
+ });
+ // Move completion: promote switched pawns (as in Magnetic)
+ const sizeX = VariantRules.size[0];
+ const lastRank = (c == "w" ? 0 : sizeX-1);
+ const V = VariantRules;
+ let moves = [];
+ if (p1==V.PAWN && x2==lastRank) //TODO: also the case p2==V.PAWN and x1==lastRank! see Magnetic chess
+ {
+ move.appear[0].p = V.ROOK;
+ moves.push(move);
+ for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN])
+ {
+ let cmove = JSON.parse(JSON.stringify(move));
+ cmove.appear[0].p = piece;
+ moves.push(cmove);
+ }
+ }
+ else //other cases
+ moves.push(move);
+ return moves;
+ }
- // Prevent king switching if under check
+ getPotentialMovesFrom([x,y])
+ {
+ let moves = super.getPotentialMovesFrom([x,y]);
+ // Add switches:
+ const V = VariantRules;
+ const color = this.turn;
+ const piece = this.getPiece(x,y);
+ const [sizeX,sizeY] = V.size;
+ const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
+ const kp = this.kingPos[color];
+ const oppCol = this.getOppCol(color);
+ for (let step of steps)
+ {
+ let [i,j] = [x+step[0],y+step[1]];
+ if (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j]!=V.EMPTY
+ && this.getColor(i,j)==color && this.getPiece(i,j)!=piece
+ // No switching under check (theoretically non-king pieces could, but not)
+ && !this.isAttacked(kp, [oppCol]))
+ {
+ let switchMove_s = this.getSwitchMove_s([x,y],[i,j]);
+ if (switchMove_s.length == 1)
+ moves.push(switchMove_s[0]);
+ else //promotion
+ moves = moves.concat(switchMove_s);
+ }
+ }
+ return moves;
+ }
+
+ static get SEARCH_DEPTH() { return 2; } //branching factor is quite high
}
getSlideNJumpMoves([x,y], steps, oneStep)
{
const color = this.getColor(x,y);
- var moves = [];
- let [sizeX,sizeY] = VariantRules.size;
+ let moves = [];
+ const [sizeX,sizeY] = VariantRules.size;
outerLoop:
- for (var loop=0; loop<steps.length; loop++)
+ for (let loop=0; loop<steps.length; loop++)
{
- var step = steps[loop];
- var i = x + step[0];
- var j = y + step[1];
+ const step = steps[loop];
+ let i = x + step[0];
+ let j = y + step[1];
while (i>=0 && i<sizeX && j>=0 && j<sizeY
&& this.board[i][j] == VariantRules.EMPTY)
{
{
const color = this.getColor(x,y);
var moves = [];
- var V = VariantRules;
- var steps = asA != V.PAWN
- ? V.steps[asA]
+ const V = VariantRules;
+ const steps = asA != V.PAWN
+ ? (asA==V.QUEEN ? V.steps[V.ROOK].concat(V.steps[V.BISHOP]) : V.steps[asA])
: color=='w' ? [[-1,-1],[-1,1]] : [[1,-1],[1,1]];
- var oneStep = (asA==V.KNIGHT || asA==V.PAWN); //we don't capture king
- let [sizeX,sizeY] = V.size;
- let lastRank = (color == 'w' ? 0 : sizeY-1);
- let promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
+ const oneStep = (asA==V.KNIGHT || asA==V.PAWN); //we don't capture king
+ const [sizeX,sizeY] = V.size;
+ const lastRank = (color == 'w' ? 0 : sizeY-1);
+ const promotionPieces = [V.ROOK,V.KNIGHT,V.BISHOP,V.QUEEN];
outerLoop:
- for (var loop=0; loop<steps.length; loop++)
+ for (let loop=0; loop<steps.length; loop++)
{
- var step = steps[loop];
- var i = x + step[0];
- var j = y + step[1];
- while (i>=0 && i<sizeX && j>=0 && j<sizeY
- && this.board[i][j] == V.EMPTY)
+ const step = steps[loop];
+ let i = x + step[0];
+ let j = y + step[1];
+ while (i>=0 && i<sizeX && j>=0 && j<sizeY && this.board[i][j] == V.EMPTY)
{
if (oneStep)
continue outerLoop;
getPotentialQueenMoves(sq)
{
const V = VariantRules;
- let noCaptures = this.getSlideNJumpMoves(sq, V.steps[V.ROOK.concat(V.steps[V.BISHOP])]);
+ let noCaptures =
+ this.getSlideNJumpMoves(sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]));
let captures = this.findCaptures(sq);
return noCaptures.concat(captures);
}
--- /dev/null
+p.boxed
+ | Every captured piece can be re-used by the capturer, landing it anywhere instead of moving a piece.
+
+h3 Specifications
+
+ul
+ li Chessboard: standard.
+ li Material: standard.
+ li Non-capturing moves: standard.
+ li Special moves: standard + rebirth.
+ li Captures: standard.
+ li End of game: standard.
+
+h3 Basics
+
+p
+ | Orthodox rules apply, with only one change:
+ | every time you capture a piece, your "reserve" of waiting pieces is augmented
+ | with this figure. At every move, you may choose to land one of your reserve
+ | pieces anywhere on the board (except last rank for pawns),
+ | instead of playing a regular move.
+
+p.
+ Note: when a promoted pawn is captured, capturing it put a pawn in the reserve,
+ not the promoted piece. This is to allow to gain material on last rank without
+ fear of giving a queen to the opponent.
+
+h3 Credits
+
+p
+ | This variant is very popular, a possible starting point is
+ a(href="https://www.chessvariants.com/other.dir/crazyhouse.html") lichess.org
+ | .
--- /dev/null
+p.boxed
+ | Win by eliminating all opponent pieces of the same type.
+
+h3 Specifications
+
+ul
+ li Chessboard: standard.
+ li Material: standard.
+ li Non-capturing moves: standard.
+ li Special moves: standard.
+ li Captures: standard.
+ li End of game: pieces extinction.
+
+h3 Basics
+
+p
+ | Standard rules apply, but the game ends when all pieces of a kind disappeared.
+ | Kings are treated as normal pieces (no royal power), but may castle -
+ | without any concern about checks.
+ | Pawns may promote into king.
+ | If all pieces of a kind disappear, the game is lost; except it's a pawns extinction
+ | which results in a non-pawn extinction by capturing and promoting on the last rank.
+
+h3 End of game
+
+p.
+ Win by eliminating all enemy pawns, or rooks, or knights, or bishops, or queen(s),
+ or king(s) (there may be several if promotions happened).
+
+h3 Credits
+
+p
+ a(href="https://www.chessvariants.com/winning.dir/extinction.html") Extinction chess
+ | on chessvariants.com.
--- /dev/null
+p.boxed
+ | In addition to standard moves, a piece can be exchanged with an adjacent friendly unit.
+
+h3 Specifications
+
+ul
+ li Chessboard: standard.
+ li Material: standard.
+ li Non-capturing moves: standard + switch.
+ li Special moves: standard.
+ li Captures: standard.
+ li End of game: standard.
+
+h3 Basics
+
+p
+ | Instead of a normal move, a piece may be exchanged with an adjacent friendly one.
+ | Switching can move pawns until rank 1 or final rank. In the first case a 2-squares,
+ | jump is possible, and in the second a promotion occurs.
+ | Switching must involves two different units.
+ | Switching while the king is under check is not allowed.
+
+p.
+ Note: if the king and rook are on two adjacent squares, castling and switching
+ from the king are triggered in the same way. Castling takes priority:
+ if you wanna switch, use the rook.
+
+h3 Credits
+
+p
+ a(href="https://www.chessvariants.com/diffmove.dir/switching.html") Switching chess
+ | on chessvariants.com.