-class MagneticRules
+class MagneticRules extends ChessRules
{
- getEpSquare(move)
- {
- return undefined; //no en-passant
- }
-
- // Complete a move with magnetic actions
- applyMagneticLaws([x,y], move)
- {
- // TODO
- }
+ static get HasEnpassant() { return false; }
- getBasicMove([sx,sy], [ex,ey], tr)
+ getPotentialMovesFrom([x,y])
{
- 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)
- })
- ]
+ let standardMoves = super.getPotentialMovesFrom([x,y]);
+ let moves = [];
+ standardMoves.forEach(m => {
+ let newMove_s = this.applyMagneticLaws(m);
+ if (newMove_s.length == 1)
+ moves.push(newMove_s[0]);
+ else //promotion
+ moves = moves.concat(newMove_s);
});
-
- 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;
+ return moves;
}
- getCastleMoves([x,y])
+ // Complete a move with magnetic actions
+ // TODO: job is done multiple times for (normal) promotions.
+ applyMagneticLaws(move)
{
- 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 (move.appear[0].p == V.KING && move.appear.length==1)
+ return [move]; //kings are not charged
+ const aIdx = (move.appear[0].p != V.KING ? 0 : 1); //if castling, rook is charged
+ const [x,y] = [move.appear[aIdx].x, move.appear[aIdx].y];
+ const color = this.turn;
+ const lastRank = (color=="w" ? 0 : 7);
+ const standardMove = JSON.parse(JSON.stringify(move));
+ this.play(standardMove);
+ for (let step of [[-1,0],[1,0],[0,-1],[0,1]])
{
- 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)
+ let [i,j] = [x+step[0],y+step[1]];
+ while (V.OnBoard(i,j))
{
- 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)))))
+ if (this.board[i][j] != V.EMPTY)
{
- continue castlingCheck;
+ // Found something. Same color or not?
+ if (this.getColor(i,j) != color)
+ {
+ // Attraction
+ if ((Math.abs(i-x)>=2 || Math.abs(j-y)>=2) && this.getPiece(i,j) != V.KING)
+ {
+ move.vanish.push(
+ new PiPo({
+ p:this.getPiece(i,j),
+ c:this.getColor(i,j),
+ x:i,
+ y:j
+ })
+ );
+ move.appear.push(
+ new PiPo({
+ p:this.getPiece(i,j),
+ c:this.getColor(i,j),
+ x:x+step[0],
+ y:y+step[1]
+ })
+ );
+ }
+ }
+ else
+ {
+ // Repulsion
+ if (this.getPiece(i,j) != V.KING)
+ {
+ // Push it until we meet an obstacle or edge of the board
+ let [ii,jj] = [i+step[0],j+step[1]];
+ while (V.OnBoard(ii,jj))
+ {
+ if (this.board[ii][jj] != V.EMPTY)
+ break;
+ ii += step[0];
+ jj += step[1];
+ }
+ ii -= step[0];
+ jj -= step[1];
+ if (Math.abs(ii-i)>=1 || Math.abs(jj-j)>=1)
+ {
+ move.vanish.push(
+ new PiPo({
+ p:this.getPiece(i,j),
+ c:this.getColor(i,j),
+ x:i,
+ y:j
+ })
+ );
+ move.appear.push(
+ new PiPo({
+ p:this.getPiece(i,j),
+ c:this.getColor(i,j),
+ x:ii,
+ y:jj
+ })
+ );
+ }
+ }
+ }
+ break;
}
+ i += step[0];
+ j += step[1];
}
-
- // 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++)
+ }
+ this.undo(standardMove);
+ let moves = [];
+ // 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 (this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
- this.getPiece(x,finalSquares[castleSide][i]) != V.KING &&
- finalSquares[castleSide][i] != rookPos)
+ move.appear[i].p = V.ROOK;
+ moves.push(move);
+ for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN])
{
- continue castlingCheck;
+ let cmove = JSON.parse(JSON.stringify(move));
+ cmove.appear[i].p = piece;
+ moves.push(cmove);
}
+ // Swap appear[i] and appear[0] for moves presentation (TODO: this is awkward)
+ moves.forEach(m => {
+ let tmp = m.appear[0];
+ m.appear[0] = m.appear[i];
+ m.appear[i] = tmp;
+ });
+ break;
}
-
- // 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);
}
-
+ if (moves.length == 0) //no pawn on 8th rank
+ moves.push(move);
return moves;
}
- // TODO: verify this assertion
-// atLeastOneMove()
-// {
-// return true; //always at least one possible move
-// }
+ atLeastOneMove()
+ {
+ if (this.kingPos[this.turn][0] < 0)
+ return false;
+ return true; //TODO: is it right?
+ }
- underCheck(move)
+ underCheck(color)
{
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;
+ return [];
}
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)
+ const c = move.vanish[0].c;
+ if (move.vanish.length >= 2 && move.vanish[1].p == V.KING)
{
// We took opponent king !
const oppCol = this.getOppCol(c);
this.kingPos[oppCol] = [-1,-1];
- this.flags[oppCol] = [false,false];
+ this.castleFlags[oppCol] = [false,false];
}
+ // Did we magnetically move our (init) rooks or opponents' ones ?
+ const firstRank = (c == "w" ? 7 : 0);
+ const oppFirstRank = 7 - firstRank;
+ const oppCol = this.getOppCol(c);
+ move.vanish.forEach(psq => {
+ if (psq.x == firstRank && this.INIT_COL_ROOK[c].includes(psq.y))
+ this.castleFlags[c][psq.y==this.INIT_COL_ROOK[c][0] ? 0 : 1] = false;
+ else if (psq.x == oppFirstRank && this.INIT_COL_ROOK[oppCol].includes(psq.y))
+ this.castleFlags[oppCol][psq.y==this.INIT_COL_ROOK[oppCol][0] ? 0 : 1] = false;
+ });
}
unupdateVariables(move)
{
super.unupdateVariables(move);
- const c = this.getColor(move.start.x,move.start.y);
+ const c = move.vanish[0].c;
const oppCol = this.getOppCol(c);
if (this.kingPos[oppCol][0] < 0)
{
}
}
- 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";
}
+
+ static get THRESHOLD_MATE()
+ {
+ return 500; //checkmates evals may be slightly below 1000
+ }
}
+
+const VariantRules = MagneticRules;