X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FSynchrone2.js;h=17b0be325643ac0a8fcc1894ba1f5f75d32f8da5;hb=4313762da3237b04f204e121a20cab3ba7bb5dd2;hp=7f623982d0f5ca7283e27b7ab430b57e12ea3ba6;hpb=9d15c433c207a2c3bb548d095939c3e08b4038fd;p=vchess.git diff --git a/client/src/variants/Synchrone2.js b/client/src/variants/Synchrone2.js index 7f623982..17b0be32 100644 --- a/client/src/variants/Synchrone2.js +++ b/client/src/variants/Synchrone2.js @@ -1,11 +1,11 @@ -import { ChessRules } from "@/base_rules"; +import { ChessRules, Move } from "@/base_rules"; import { Synchrone1Rules } from "@/variants/Synchrone1"; import { randInt } from "@/utils/alea"; export class Synchrone2Rules extends Synchrone1Rules { static get CanAnalyze() { - return true;//false; + return false; } static get HasEnpassant() { @@ -16,7 +16,7 @@ export class Synchrone2Rules extends Synchrone1Rules { if (!Synchrone1Rules.IsGoodFen(fen)) return false; const fenParsed = V.ParseFen(fen); // 5) Check initFen (not really... TODO?) - if (!fenParsed.initFen || fenParsed.initFen == "-") return false; + if (!fenParsed.initFen) return false; return true; } @@ -32,27 +32,25 @@ export class Synchrone2Rules extends Synchrone1Rules { } getInitfenFen() { - if (!this.whiteMove) return "-"; - return JSON.stringify({ - start: this.whiteMove.start, - end: this.whiteMove.end, - appear: this.whiteMove.appear, - vanish: this.whiteMove.vanish - }); + const L = this.initfenStack.length; + return L > 0 ? this.initfenStack[L-1] : "-"; } getFen() { return ( - super.getFen() + " " + + super.getBaseFen() + " " + + super.getTurnFen() + " " + + this.movesCount + " " + + super.getFlagsFen() + " " + this.getInitfenFen() + " " + this.getWhitemoveFen() ); } - static GenRandInitFen(randomness) { - const res = ChessRules.GenRandInitFen(randomness); + static GenRandInitFen(options) { + const res = ChessRules.GenRandInitFen(options); // Add initFen field: - return res.slice(0, -1) + " " + res.split(' ')[1] + " -"; + return res.slice(0, -1) + res.split(' ')[0] + " -"; } setOtherVariables(fen) { @@ -64,44 +62,176 @@ export class Synchrone2Rules extends Synchrone1Rules { parsedFen.whiteMove != "-" ? JSON.parse(parsedFen.whiteMove) : null; - // And initFen (not empty) - this.initFen = parsedFen.initFen; + // And initFen (could be empty) + this.initfenStack = []; + if (parsedFen.initFen != "-") this.initfenStack.push(parsedFen.initFen); } getPotentialMovesFrom([x, y]) { if (this.movesCount % 4 <= 1) return super.getPotentialMovesFrom([x, y]); - // TODO: either add a "blackMove' field in FEN (bof...), - // or write an helper function to detect from diff positions, - // which piece moved (if not disappeared!), which moves are valid. - // + do not forget pass move (king 2 king): always possible at stage 2. - return []; + // Diff current and old board to know which pieces have moved, + // and to deduce possible moves at stage 2. + const L = this.initfenStack.length; + let initBoard = V.GetBoard(this.initfenStack[L-1]); + let appeared = []; + const c = this.turn; + const oppCol = V.GetOppCol(c); + for (let i=0; i<8; i++) { + for (let j=0; j<8; j++) { + if (this.board[i][j] != initBoard[i][j]) { + if (this.board[i][j] != V.EMPTY) { + const color = this.board[i][j].charAt(0); + appeared.push({ c: color, x: i, y: j }); + // Pawns capture in diagonal => the following fix. + // (Other way would be to redefine getPotentialPawnMoves()...) + if (color == oppCol) initBoard[i][j] = this.board[i][j]; + } + } + } + } + const saveBoard = this.board; + this.board = initBoard; + const movesInit = super.getPotentialMovesFrom([x, y]); + this.board = saveBoard; + const target = appeared.find(a => a.c == oppCol) || { x: -1, y: -1 }; + let movesNow = super.getPotentialMovesFrom([x, y]).filter(m => { + return ( + m.end.x == target.x && + m.end.y == target.y && + movesInit.some(mi => mi.end.x == m.end.x && mi.end.y == m.end.y) + ); + }); + const passTarget = + (x != this.kingPos[c][0] || y != this.kingPos[c][1]) ? c : oppCol; + movesNow.push( + new Move({ + start: { x: x, y: y }, + end: { + x: this.kingPos[passTarget][0], + y: this.kingPos[passTarget][1] + }, + appear: [], + vanish: [] + }) + ); + return movesNow; + } + + filterValid(moves) { + const nonEmptyMove = moves.find(m => m.vanish.length > 0); + if (!nonEmptyMove) return moves; + // filterValid can be called when it's "not our turn": + const color = nonEmptyMove.vanish[0].c; + return moves.filter(m => { + if (m.vanish.length == 0) return true; + const piece = m.vanish[0].p; + if (piece == V.KING) { + this.kingPos[color][0] = m.appear[0].x; + this.kingPos[color][1] = m.appear[0].y; + } + V.PlayOnBoard(this.board, m); + let res = !this.underCheck(color); + V.UndoOnBoard(this.board, m); + if (piece == V.KING) this.kingPos[color] = [m.start.x, m.start.y]; + return res; + }); + } + + getPossibleMovesFrom([x, y]) { + return this.filterValid(this.getPotentialMovesFrom([x, y])); } - play(move) { - move.flags = JSON.stringify(this.aggregateFlags()); + getAllValidMoves() { + const moves = this.filterValid(super.getAllPotentialMoves()); + if (this.movesCount % 4 <= 1) return moves; + const emptyIdx = moves.findIndex(m => m.vanish.length == 0); + if (emptyIdx >= 0) + // Keep only one pass move (for computer play) + return moves.filter((m, i) => m.vanish.length > 0 || i == emptyIdx); + return moves; + } + + play(move, noFlag) { + if (this.movesCount % 4 == 0) this.initfenStack.push(this.getBaseFen()); + if (!noFlag) move.flags = JSON.stringify(this.aggregateFlags()); // Do not play on board (would reveal the move...) this.turn = V.GetOppCol(this.turn); this.movesCount++; - this.postPlay(move); + if ([0, 3].includes(this.movesCount % 4)) this.postPlay(move); + else super.postPlay(move); //resolve synchrone move } - undo(move) { - this.disaggregateFlags(JSON.parse(move.flags)); + postPlay(move) { + if (this.turn == 'b') { + // NOTE: whiteMove is used read-only, so no need to copy + this.whiteMove = move; + return; + } + + // A full "deterministic" turn just ended: no need to resolve + const smove = { + appear: this.whiteMove.appear.concat(move.appear), + vanish: this.whiteMove.vanish.concat(move.vanish) + }; + V.PlayOnBoard(this.board, smove); + move.whiteMove = this.whiteMove; //for undo + this.whiteMove = null; + + // Update king position + flags + let kingAppear = { 'w': false, 'b': false }; + for (let i=0; i < smove.appear.length; i++) { + if (smove.appear[i].p == V.KING) { + const c = smove.appear[i].c; + kingAppear[c] = true; + this.kingPos[c][0] = smove.appear[i].x; + this.kingPos[c][1] = smove.appear[i].y; + } + } + for (let i = 0; i < smove.vanish.length; i++) { + if (smove.vanish[i].p == V.KING) { + const c = smove.vanish[i].c; + if (!kingAppear[c]) { + this.kingPos[c][0] = -1; + this.kingPos[c][1] = -1; + } + break; + } + } + super.updateCastleFlags(smove); + move.smove = smove; + } + + undo(move, noFlag) { + if (!noFlag) this.disaggregateFlags(JSON.parse(move.flags)); if (this.turn == 'w') // Back to the middle of the move V.UndoOnBoard(this.board, move.smove); this.turn = V.GetOppCol(this.turn); this.movesCount--; + if (this.movesCount % 4 == 0) this.initfenStack.pop(); this.postUndo(move); } + postUndo(move) { + if (this.turn == 'w') { + // Reset king positions: scan board (TODO: could be more efficient) + if (move.vanish.length > 0) this.scanKings(); + // Also reset whiteMove + this.whiteMove = null; + } + else this.whiteMove = move.whiteMove; + } + getCurrentScore() { - if (this.movesCount % 4 != 0) - // Turn (2 x white + black) not over yet + if (this.movesCount % 2 != 0) + // Turn [white + black] not over yet return "*"; // Was a king captured? if (this.kingPos['w'][0] < 0) return "0-1"; if (this.kingPos['b'][0] < 0) return "1-0"; + if (this.movesCount % 4 == 2) + // Turn (2 x [white + black]) not over yet + return "*"; const whiteCanMove = this.atLeastOneMove('w'); const blackCanMove = this.atLeastOneMove('b'); if (whiteCanMove && blackCanMove) return "*"; @@ -119,4 +249,15 @@ export class Synchrone2Rules extends Synchrone1Rules { return (whiteCanMove ? "1-0" : "0-1"); } + getComputerMove() { + if (this.movesCount % 4 <= 1) return super.getComputerMove(); + const moves = this.getAllValidMoves(); + return moves[randInt(moves.length)]; + } + + getNotation(move) { + if (move.vanish.length == 0) return "pass"; + return super.getNotation(move); + } + };