'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] ],
'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] ]
- if (i>=0 && i<8 && j>=0 && j<8 && this.canTake([x,y], [i,j]))
+ if (i>=0 && i<sizeX && j>=0 && j<sizeY && this.canTake([x,y], [i,j]))
moves.push(this.getBasicMove([x,y], [i,j]));
}
return moves;
moves.push(this.getBasicMove([x,y], [i,j]));
}
return moves;
// What are the queen moves from square x,y ?
getPotentialQueenMoves(sq)
{
// What are the queen moves from square x,y ?
getPotentialQueenMoves(sq)
{
}
// What are the king moves from square x,y ?
getPotentialKingMoves(sq)
{
}
// What are the king moves from square x,y ?
getPotentialKingMoves(sq)
{
- 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));
}
getCastleMoves([x,y])
{
const c = this.getColor(x,y);
return moves.concat(this.getCastleMoves(sq));
}
getCastleMoves([x,y])
{
const c = this.getColor(x,y);
- const finalSquares = [ [2,3], [6,5] ]; //king, then rook
+ const finalSquares = [ [2,3], [sizeY-2,sizeY-3] ]; //king, then rook
- var potentialMoves = [];
- let [sizeX,sizeY] = VariantRules.size;
+ let potentialMoves = [];
+ const [sizeX,sizeY] = VariantRules.size;
// No: if happen on last 1/2 move, could lead to forbidden moves, wrong evals
return this.filterValid(potentialMoves);
}
// No: if happen on last 1/2 move, could lead to forbidden moves, wrong evals
return this.filterValid(potentialMoves);
}
- let [sizeX,sizeY] = VariantRules.size;
- for (var i=0; i<sizeX; i++)
+ const [sizeX,sizeY] = VariantRules.size;
+ for (let i=0; i<sizeX; i++)
{
if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) != oppCol)
{
const moves = this.getPotentialMovesFrom([i,j]);
if (moves.length > 0)
{
{
if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) != oppCol)
{
const moves = this.getPotentialMovesFrom([i,j]);
if (moves.length > 0)
{
// Is square x,y attacked by pawns of color c ?
isAttackedByPawn([x,y], colors)
{
// Is square x,y attacked by pawns of color c ?
isAttackedByPawn([x,y], colors)
{
// Is square x,y attacked by queens of color c ?
isAttackedByQueen(sq, colors)
{
// 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)
{
}
// 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 ?
isAttackedBySlideNJump([x,y], colors, piece, steps, oneStep)
{
}
// Generic method for non-pawn pieces ("sliding or jumping"): is x,y attacked by piece != color ?
isAttackedBySlideNJump([x,y], colors, piece, steps, oneStep)
{
{
const piece = this.getPiece(move.start.x,move.start.y);
const c = this.getColor(move.start.x,move.start.y);
{
const piece = this.getPiece(move.start.x,move.start.y);
const c = this.getColor(move.start.x,move.start.y);
{
const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1);
this.castleFlags[oppCol][flagIdx] = false;
{
const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1);
this.castleFlags[oppCol][flagIdx] = false;
+ static get INFINITY() {
+ return 9999; //"checkmate" (unreachable eval)
+ }
+
+ static get THRESHOLD_MATE() {
+ // At this value or above, the game is over
+ return VariantRules.INFINITY;
+ }
+
+ static get SEARCH_DEPTH() {
+ return 3; //2 for high branching factor, 4 for small (Loser chess)
+ }
+
- moves1[i].eval = (color=="w" ? -1 : 1) * 1000; //very low, I'm checkmated
- let eval2 = (color=="w" ? 1 : -1) * 1000; //initialized with very high (checkmate) value
+ moves1[i].eval = (color=="w" ? -1 : 1) * maxeval; //very low, I'm checkmated
+ let eval2 = (color=="w" ? 1 : -1) * maxeval; //initialized with checkmate value
- // TODO: show current analyzed move for depth 3, allow stopping eval (return moves1[0])
- for (let i=0; i<moves1.length; i++)
+ let candidates = [0]; //indices of candidates moves
+ for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
+ candidates.push(j);
+ let currentBest = moves1[_.sample(candidates, 1)];
+
+ // Skip depth 3 if we found a checkmate (or if we are checkmated in 1...)
+ if (VariantRules.SEARCH_DEPTH >= 3
+ && Math.abs(moves1[0].eval) < VariantRules.THRESHOLD_MATE)
- this.play(moves1[i]);
- // 0.1 * oldEval : heuristic to avoid some bad moves (not all...)
- moves1[i].eval = 0.1*moves1[i].eval + this.alphabeta(2, -1000, 1000);
- this.undo(moves1[i]);
+ // TODO: show current analyzed move for depth 3, allow stopping eval (return moves1[0])
+ for (let i=0; i<moves1.length; i++)
+ {
+ if (this.shouldReturn)
+ return currentBest; //depth-2, minimum
+ this.play(moves1[i]);
+ // 0.1 * oldEval : heuristic to avoid some bad moves (not all...)
+ moves1[i].eval = 0.1*moves1[i].eval +
+ this.alphabeta(VariantRules.SEARCH_DEPTH-1, -maxeval, maxeval);
+ this.undo(moves1[i]);
+ }
+ moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
return moves1[_.sample(candidates, 1)];
}
alphabeta(depth, alpha, beta)
{
return moves1[_.sample(candidates, 1)];
}
alphabeta(depth, alpha, beta)
{
pgn += '[Date "' + d.getFullYear() + '-' + (d.getMonth()+1) + '-' + d.getDate() + '"]<br>';
pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]<br>';
pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]<br>';
pgn += '[Date "' + d.getFullYear() + '-' + (d.getMonth()+1) + '-' + d.getDate() + '"]<br>';
pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]<br>';
pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]<br>';