return undefined; //no en-passant
}
+ getPotentialMovesFrom([x,y])
+ {
+ 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);
+ });
+ return moves;
+ }
+
// Complete a move with magnetic actions
- applyMagneticLaws([x,y], move)
+ // TODO: job is done multiple times for (normal) promotions.
+ applyMagneticLaws(move)
{
+ const V = VariantRules;
+ 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);
- const color = this.getColor(x,y);
- const [sizeX,sizeY] = VariantRules.size;
+ const [sizeX,sizeY] = V.size;
for (let step of [[-1,0],[1,0],[0,-1],[0,1]])
{
let [i,j] = [x+step[0],y+step[1]];
while (i>=0 && i<sizeX && j>=0 && j<sizeY)
{
- if (this.board[i][j] != VariantRules.EMPTY)
+ if (this.board[i][j] != V.EMPTY)
{
// 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) != VariantRules.KING)
+ if ((Math.abs(i-x)>=2 || Math.abs(j-y)>=2) && this.getPiece(i,j) != V.KING)
{
move.vanish.push(
new PiPo({
else
{
// Repulsion
- if (this.getPiece(i,j) != VariantRules.KING)
+ 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 (ii>=0 && ii<sizeX && jj>=0 && jj<sizeY)
{
- if (this.board[ii][jj] != VariantRules.EMPTY)
+ if (this.board[ii][jj] != V.EMPTY)
break;
ii += step[0];
jj += step[1];
}
}
this.undo(standardMove);
- }
-
- // TODO: when pawn is pushed to 8th rank, apply promotions (similar change as in Checkered)
- 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;
- }
-
- getPotentialPawnMoves([x,y])
- {
- const color = this.getColor(x,y);
- var moves = [];
- var V = VariantRules;
- const [sizeX,sizeY] = VariantRules.size;
- let shift = (color == "w" ? -1 : 1);
- let startRank = (color == "w" ? sizeY-2 : 1);
- let firstRank = (color == 'w' ? sizeY-1 : 0);
- let 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]));
- 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}));
- });
- }
-
- // No en passant
-
- return moves;
- }
-
- 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
+ // Scan move for pawn (max 1) on 8th rank
+ for (let i=1; i<move.appear.length; i++)
{
- 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 (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()
+ {
+ return true; //always at least one possible move
+ }
underCheck(move)
{
// We took opponent king !
const oppCol = this.getOppCol(c);
this.kingPos[oppCol] = [-1,-1];
- this.flags[oppCol] = [false,false];
+ this.castleFlags[oppCol] = [false,false];
}
}
// 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
+ }
}