/////////////////
// INITIALIZATION
- // fen = "position flags epSquare movesCount"
+ // fen == "position flags"
constructor(fen, moves)
{
this.moves = moves;
// Use fen string to initialize variables, flags and board
- this.initVariables(fen);
- this.flags = VariantRules.GetFlags(fen);
this.board = VariantRules.GetBoard(fen);
+ this.flags = VariantRules.GetFlags(fen);
+ this.initVariables(fen);
}
initVariables(fen)
j++;
}
}
- let epSq = undefined;
- if (fenParts[2] != "-")
- {
- const digits = fenParts[2].split(","); //3,2 ...
- epSq = { x:Number.parseInt(digits[0]), y:Number.parseInt(digits[1]) };
- }
+ const epSq = this.moves.length > 0 ? this.getEpSquare(this.lastMove) : undefined;
this.epSquares = [ epSq ];
- this.movesCount = Number.parseInt(fenParts[3]);
}
// Turn diagram fen into double array ["wb","wp","bk",...]
return L>0 ? this.moves[L-1] : null;
}
get turn() {
- return this.movesCount%2==0 ? 'w' : 'b';
+ return this.moves.length%2==0 ? 'w' : 'b';
}
// Pieces codes
canIplay(color, sq)
{
- return ((color=='w' && this.movesCount%2==0)
- || (color=='b' && this.movesCount%2==1))
+ return ((color=='w' && this.moves.length%2==0)
+ || (color=='b' && this.moves.length%2==1))
&& this.getColor(sq[0], sq[1]) == color;
}
// No: if happen on last 1/2 move, could lead to forbidden moves, wrong evals
return this.filterValid(potentialMoves);
}
+
+ // Stop at the first move found
+ atLeastOneMove(color)
+ {
+ const oppCol = this.getOppCol(color);
+ let [sizeX,sizeY] = VariantRules.size;
+ for (var i=0; i<sizeX; i++)
+ {
+ for (var j=0; j<sizeY; j++)
+ {
+ if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) != oppCol)
+ {
+ const moves = this.getPotentialMovesFrom([i,j]);
+ if (moves.length > 0)
+ {
+ for (let i=0; i<moves.length; i++)
+ {
+ if (this.filterValid([moves[i]]).length > 0)
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
// Check if pieces of color 'color' are attacking square x,y
isAttacked(sq, color)
return false;
}
+ // Is color c under check after move ?
underCheck(move, c)
{
this.play(move);
return res;
}
+ // On which squares is color c under check (after move) ?
+ getCheckSquares(move, c)
+ {
+ this.play(move);
+ let res = this.isAttacked(this.kingPos[c], this.getOppCol(c))
+ ? [ JSON.parse(JSON.stringify(this.kingPos[c])) ] //need to duplicate!
+ : [ ];
+ this.undo(move);
+ return res;
+ }
+
// Apply a move on board
static PlayOnBoard(board, move)
{
board[psq.x][psq.y] = psq.c + psq.p;
}
- // Before move is played:
+ // Before move is played, update variables + flags
updateVariables(move)
{
const piece = this.getPiece(move.start.x,move.start.y);
}
}
- play(move, ingame)
+ unupdateVariables(move)
{
- // Save flags (for undo)
- move.flags = JSON.stringify(this.flags); //TODO: less costly
- this.updateVariables(move);
+ // (Potentially) Reset king position
+ const c = this.getColor(move.start.x,move.start.y);
+ if (this.getPiece(move.start.x,move.start.y) == VariantRules.KING)
+ this.kingPos[c] = [move.start.x, move.start.y];
+ }
+ play(move, ingame)
+ {
if (!!ingame)
- {
move.notation = this.getNotation(move);
- this.moves.push(move);
- }
+ // Save flags (for undo)
+ move.flags = JSON.stringify(this.flags); //TODO: less costly?
+ this.updateVariables(move);
+ this.moves.push(move);
this.epSquares.push( this.getEpSquare(move) );
VariantRules.PlayOnBoard(this.board, move);
- this.movesCount++;
}
undo(move)
{
VariantRules.UndoOnBoard(this.board, move);
this.epSquares.pop();
- this.movesCount--;
-
- // Update king position, and reset stored/computed flags
- const c = this.getColor(move.start.x,move.start.y);
- if (this.getPiece(move.start.x,move.start.y) == VariantRules.KING)
- this.kingPos[c] = [move.start.x, move.start.y];
-
+ this.moves.pop();
+ this.unupdateVariables(move);
this.flags = JSON.parse(move.flags);
}
}
}
- // TODO: not required to generate ALL: just need one (callback ? hook ? ...)
- if (this.getAllValidMoves(color).length > 0)
+ if (this.atLeastOneMove(color))
{
// game not over
return "*";
moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
// TODO: show current analyzed move for depth 3, allow stopping eval (return moves1[0])
-// for (let i=0; i<moves1.length; i++)
-// {
-// 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(oppCol, color, 2, -1000, 1000);
-// this.undo(moves1[i]);
-// }
-// moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
+ for (let i=0; i<moves1.length; i++)
+ {
+ 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(oppCol, color, 2, -1000, 1000);
+ this.undo(moves1[i]);
+ }
+ moves1.sort( (a,b) => { return (color=="w" ? 1 : -1) * (b.eval - a.eval); });
let candidates = [0]; //indices of candidates moves
for (let j=1; j<moves1.length && moves1[j].eval == moves1[0].eval; j++)
alphabeta(color, oppCol, depth, alpha, beta)
{
- let moves = this.getAllValidMoves(color);
- if (moves.length == 0)
+ if (!this.atLeastOneMove(color))
{
switch (this.checkGameEnd(color))
{
}
if (depth == 0)
return this.evalPosition();
+ const moves = this.getAllValidMoves(color);
let v = color=="w" ? -1000 : 1000;
if (color == "w")
{
let fen = pieces[0].join("") +
"/pppppppp/8/8/8/8/PPPPPPPP/" +
pieces[1].join("").toUpperCase() +
- " 1111 - 0"; //flags + enPassant + movesCount
+ " 1111"; //add flags
return fen;
}
// Return current fen according to pieces+colors state
getFen()
{
- const L = this.epSquares.length;
- const epSq = this.epSquares[L-1]===undefined
- ? "-"
- : this.epSquares[L-1].x+","+this.epSquares[L-1].y;
- return this.getBaseFen() + " " + this.getFlagsFen()
- + " " + epSq + " " + this.movesCount;
+ return this.getBaseFen() + " " + this.getFlagsFen();
}
getBaseFen()
}
// The score is already computed when calling this function
- getPGN(mycolor, score, fenStart)
+ getPGN(mycolor, score, fenStart, mode)
{
let pgn = "";
pgn += '[Site "vchess.club"]<br>';
const d = new Date();
+ const opponent = this.mode=="human" ? "Anonymous" : "Computer";
pgn += '[Date "' + d.getFullYear() + '-' + d.getMonth() + '-' + d.getDate() + '"]<br>';
- pgn += '[White "' + (mycolor=='w'?'Myself':'Anonymous') + '"]<br>';
- pgn += '[Black "' + (mycolor=='b'?'Myself':'Anonymous') + '"]<br>';
+ pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]<br>';
+ pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]<br>';
pgn += '[Fen "' + fenStart + '"]<br>';
pgn += '[Result "' + score + '"]<br><br>';