// 2) Check turn
if (!fenParsed.turn || !V.IsGoodTurn(fenParsed.turn))
return false;
- // 3) Check flags
+ // 3) Check moves count
+ if (!fenParsed.movesCount || !(parseInt(fenParsed.movesCount) >= 0))
+ return false;
+ // 4) Check flags
if (V.HasFlags && (!fenParsed.flags || !V.IsGoodFlags(fenParsed.flags)))
return false;
- // 4) Check enpassant
+ // 5) Check enpassant
if (V.HasEnpassant &&
(!fenParsed.enpassant || !V.IsGoodEnpassant(fenParsed.enpassant)))
{
// Argument is a move:
const move = moveOrSquare;
const [sx,sy,ex] = [move.start.x,move.start.y,move.end.x];
- if (this.getPiece(sx,sy) == V.PAWN && Math.abs(sx - ex) == 2)
+ // TODO: next conditions are first for Atomic, and third for Checkered
+ if (move.appear.length > 0 && move.appear[0].p == V.PAWN && ["w","b"].includes(move.appear[0].c) && Math.abs(sx - ex) == 2)
{
return {
x: (sx + ex)/2,
{
position: fenParts[0],
turn: fenParts[1],
+ movesCount: fenParts[2],
};
- let nextIdx = 2;
+ let nextIdx = 3;
if (V.HasFlags)
Object.assign(res, {flags: fenParts[nextIdx++]});
if (V.HasEnpassant)
// Return current fen (game state)
getFen()
{
- return this.getBaseFen() + " " + this.getTurnFen() +
+ return this.getBaseFen() + " " +
+ this.getTurnFen() + " " + this.movesCount +
(V.HasFlags ? (" " + this.getFlagsFen()) : "") +
(V.HasEnpassant ? (" " + this.getEnpassantFen()) : "");
}
// INITIALIZATION
// Fen string fully describes the game state
- constructor(fen, moves)
+ constructor(fen)
{
- this.moves = moves;
const fenParsed = V.ParseFen(fen);
this.board = V.GetBoard(fenParsed.position);
this.turn = fenParsed.turn[0]; //[0] to work with MarseilleRules
+ this.movesCount = parseInt(fenParsed.movesCount);
this.setOtherVariables(fen);
}
return (color=="w" ? "b" : "w");
}
- get lastMove()
- {
- const L = this.moves.length;
- return (L>0 ? this.moves[L-1] : null);
- }
-
// Pieces codes (for a clearer code)
static get PAWN() { return 'p'; }
static get ROOK() { return 'r'; }
// if (!ingame) this.states.push(this.getFen());
if (!!ingame)
- move.notation = [this.getNotation(move), this.getLongNotation(move)];
+ move.notation = this.getNotation(move);
if (V.HasFlags)
move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo)
this.epSquares.push( this.getEpSquare(move) );
V.PlayOnBoard(this.board, move);
this.turn = this.getOppCol(this.turn);
- this.moves.push(move);
+ this.movesCount++;
this.updateVariables(move);
if (!!ingame)
this.disaggregateFlags(JSON.parse(move.flags));
V.UndoOnBoard(this.board, move);
this.turn = this.getOppCol(this.turn);
- this.moves.pop();
+ this.movesCount--;
this.unupdateVariables(move);
// DEBUG:
///////////////
// END OF GAME
- // Check for 3 repetitions (position + flags + turn)
- checkRepetition()
- {
- if (!this.hashStates)
- this.hashStates = {};
- const startIndex =
- Object.values(this.hashStates).reduce((a,b) => { return a+b; }, 0)
- // Update this.hashStates with last move (or all moves if continuation)
- // NOTE: redundant storage, but faster and moderate size
- for (let i=startIndex; i<this.moves.length; i++)
- {
- const move = this.moves[i];
- if (!this.hashStates[move.hash])
- this.hashStates[move.hash] = 1;
- else
- this.hashStates[move.hash]++;
- }
- return Object.values(this.hashStates).some(elt => { return (elt >= 3); });
- }
-
// Is game over ? And if yes, what is the score ?
checkGameOver()
{
- if (this.checkRepetition())
- return "1/2";
-
if (this.atLeastOneMove()) // game not over
return "*";
/////////////////////////
// Context: just before move is played, turn hasn't changed
+ // TODO: un-ambiguous notation (switch on piece type, check directions...)
getNotation(move)
{
if (move.appear.length == 2 && move.appear[0].p == V.KING) //castle
}
// The score is already computed when calling this function
- getPGN(mycolor, score, fenStart, mode)
+ getPGN(moves, mycolor, score, fenStart, mode)
{
let pgn = "";
- pgn += '[Site "vchess.club"]<br>';
+ pgn += '[Site "vchess.club"]\n';
const opponent = mode=="human" ? "Anonymous" : "Computer";
- pgn += '[Variant "' + variant + '"]<br>';
- pgn += '[Date "' + getDate(new Date()) + '"]<br>';
- pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]<br>';
- pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]<br>';
- pgn += '[FenStart "' + fenStart + '"]<br>';
- pgn += '[Fen "' + this.getFen() + '"]<br>';
- pgn += '[Result "' + score + '"]<br><br>';
-
- // Standard PGN
- for (let i=0; i<this.moves.length; i++)
- {
- if (i % 2 == 0)
- pgn += ((i/2)+1) + ".";
- pgn += this.moves[i].notation[0] + " ";
- }
- pgn += "<br><br>";
-
- // "Complete moves" PGN (helping in ambiguous cases)
- for (let i=0; i<this.moves.length; i++)
+ pgn += '[Variant "' + variant + '"]\n';
+ pgn += '[Date "' + getDate(new Date()) + '"]\n';
+ // TODO: later when users are a bit less anonymous, use better names
+ const whiteName = ["human","computer"].includes(mode)
+ ? (mycolor=='w'?'Myself':opponent)
+ : "analyze";
+ const blackName = ["human","computer"].includes(mode)
+ ? (mycolor=='b'?'Myself':opponent)
+ : "analyze";
+ pgn += '[White "' + whiteName + '"]\n';
+ pgn += '[Black "' + blackName + '"]\n';
+ pgn += '[Fen "' + fenStart + '"]\n';
+ pgn += '[Result "' + score + '"]\n\n';
+
+ // Print moves
+ for (let i=0; i<moves.length; i++)
{
if (i % 2 == 0)
pgn += ((i/2)+1) + ".";
- pgn += this.moves[i].notation[1] + " ";
+ pgn += moves[i].notation + " ";
}
- return pgn;
+ return pgn + "\n";
}
}