X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FSynchrone.js;fp=client%2Fsrc%2Fvariants%2FSynchrone.js;h=0000000000000000000000000000000000000000;hb=9d15c433c207a2c3bb548d095939c3e08b4038fd;hp=365dd9aabc1a6ff9b7b0bcf806ed025a0bbe12c0;hpb=6cc34165a3ca6dcad86030c96df8c0f49c1fabad;p=vchess.git diff --git a/client/src/variants/Synchrone.js b/client/src/variants/Synchrone.js deleted file mode 100644 index 365dd9aa..00000000 --- a/client/src/variants/Synchrone.js +++ /dev/null @@ -1,520 +0,0 @@ -import { ChessRules } from "@/base_rules"; -import { randInt } from "@/utils/alea"; - -export class SynchroneRules extends ChessRules { - - static get CanAnalyze() { - return false; - } - - static get ShowMoves() { - return "byrow"; - } - - static get SomeHiddenMoves() { - return true; - } - - static IsGoodFen(fen) { - if (!ChessRules.IsGoodFen(fen)) return false; - const fenParsed = V.ParseFen(fen); - // 5) Check whiteMove - if ( - ( - fenParsed.turn == "b" && - // NOTE: do not check really JSON stringified move... - (!fenParsed.whiteMove || fenParsed.whiteMove == "-") - ) - || - (fenParsed.turn == "w" && fenParsed.whiteMove != "-") - ) { - return false; - } - return true; - } - - static IsGoodEnpassant(enpassant) { - const epArray = enpassant.split(","); - if (![2, 3].includes(epArray.length)) return false; - epArray.forEach(epsq => { - if (epsq != "-") { - const ep = V.SquareToCoords(epsq); - if (isNaN(ep.x) || !V.OnBoard(ep)) return false; - } - }); - return true; - } - - static ParseFen(fen) { - const fenParts = fen.split(" "); - return Object.assign( - ChessRules.ParseFen(fen), - { whiteMove: fenParts[5] } - ); - } - - static GenRandInitFen(randomness) { - return ChessRules.GenRandInitFen(randomness).slice(0, -1) + "-,- -"; - } - - getFen() { - return super.getFen() + " " + this.getWhitemoveFen(); - } - - getFenForRepeat() { - return super.getFenForRepeat() + "_" + this.getWhitemoveFen(); - } - - setOtherVariables(fen) { - const parsedFen = V.ParseFen(fen); - this.setFlags(parsedFen.flags); - const epArray = parsedFen.enpassant.split(","); - this.epSquares = []; - epArray.forEach(epsq => this.epSquares.push(this.getEpSquare(epsq))); - super.scanKings(fen); - // Also init whiteMove - this.whiteMove = - parsedFen.whiteMove != "-" - ? JSON.parse(parsedFen.whiteMove) - : null; - } - - scanKings() { - this.kingPos = { w: [-1, -1], b: [-1, -1] }; - for (let i = 0; i < V.size.x; i++) { - for (let j = 0; j < V.size.y; j++) { - if (this.getPiece(i, j) == V.KING) - this.kingPos[this.getColor(i, j)] = [i, j]; - } - } - } - - getEnpassantFen() { - const L = this.epSquares.length; - let res = ""; - const start = L - 2 - (this.turn == 'b' ? 1 : 0); - for (let i=start; i < L; i++) { - if (!this.epSquares[i]) res += "-,"; - else res += V.CoordsToSquare(this.epSquares[i]) + ","; - } - return res.slice(0, -1); - } - - getWhitemoveFen() { - if (!this.whiteMove) return "-"; - return JSON.stringify({ - start: this.whiteMove.start, - end: this.whiteMove.end, - appear: this.whiteMove.appear, - vanish: this.whiteMove.vanish - }); - } - - getPossibleMovesFrom([x, y]) { - let moves = this.filterValid(super.getPotentialMovesFrom([x, y])); - if (!this.underCheck(this.getColor(x, y))) - // Augment with potential recaptures, except if we are under check - Array.prototype.push.apply(moves, this.getRecaptures([x, y])); - return moves; - } - - // Aux function used to find opponent and self captures - getCaptures(from, to, color) { - const sliderAttack = (xx, yy, allowedSteps) => { - const deltaX = xx - to[0], - absDeltaX = Math.abs(deltaX); - const deltaY = yy - to[1], - absDeltaY = Math.abs(deltaY); - const step = [ deltaX / absDeltaX || 0, deltaY / absDeltaY || 0 ]; - if ( - // Check that the step is a priori valid: - (absDeltaX != absDeltaY && deltaX != 0 && deltaY != 0) || - allowedSteps.every(st => st[0] != step[0] || st[1] != step[1]) - ) { - return null; - } - let sq = [ to[0] + step[0], to[1] + step[1] ]; - while (sq[0] != xx || sq[1] != yy) { - // NOTE: no need to check OnBoard in this special case - if (this.board[sq[0]][sq[1]] != V.EMPTY) return null; - sq[0] += step[0]; - sq[1] += step[1]; - } - return this.getBasicMove([xx, yy], [to[0], to[1]]); - }; - // Can I take on the square 'to' ? - // If yes, return the (list of) capturing move(s) - const getTargetedCaptures = ([i, j]) => { - let move = null; - // From [i, j]: - switch (this.getPiece(i, j)) { - case V.PAWN: { - // Pushed pawns move as enemy pawns - const shift = (color == 'w' ? 1 : -1); - if (to[0] + shift == i && Math.abs(to[1] - j) == 1) - move = this.getBasicMove([i, j], to); - break; - } - case V.KNIGHT: { - const deltaX = Math.abs(i - to[0]); - const deltaY = Math.abs(j - to[1]); - if ( - deltaX + deltaY == 3 && - [1, 2].includes(deltaX) && - [1, 2].includes(deltaY) - ) { - move = this.getBasicMove([i, j], to); - } - break; - } - case V.KING: - if (Math.abs(i - to[0]) <= 1 && Math.abs(j - to[1]) <= 1) - move = this.getBasicMove([i, j], to); - break; - case V.ROOK: { - move = sliderAttack(i, j, V.steps[V.ROOK]); - break; - } - case V.BISHOP: { - move = sliderAttack(i, j, V.steps[V.BISHOP]); - break; - } - case V.QUEEN: { - move = sliderAttack(i, j, V.steps[V.ROOK].concat(V.steps[V.BISHOP])); - break; - } - } - return move; - }; - let moves = []; - if (!!from) { - const theMove = getTargetedCaptures(from); - if (!!theMove) moves.push(theMove); - } - else { - for (let i=0; i<8; i++) { - for (let j=0; j<8; j++) { - if (this.getColor(i, j) == color) { - const newMove = getTargetedCaptures([i, j]); - if (!!newMove) moves.push(newMove); - } - } - } - } - return this.filterValid(moves); - } - - getRecaptures(from) { - // 1) Generate all opponent's capturing moves - let oppCaptureMoves = []; - const color = this.turn; - const oppCol = V.GetOppCol(color); - for (let i=0; i<8; i++) { - for (let j=0; j<8; j++) { - if ( - this.getColor(i, j) == color && - // Do not consider king captures: self-captures of king are forbidden - this.getPiece(i, j) != V.KING - ) { - Array.prototype.push.apply( - oppCaptureMoves, - this.getCaptures(null, [i, j], oppCol) - ); - } - } - } - // 2) Play each opponent's capture, and see if back-captures are possible: - // Lookup table to quickly decide if a move is already in list: - let moveSet = {}; - let moves = []; - oppCaptureMoves.forEach(m => { - // If another opponent capture with same endpoint already processed, skip - const mHash = "m" + m.end.x + m.end.y; - if (!moveSet[mHash]) { - moveSet[mHash] = true; - // Just make enemy piece disappear, to clear potential path: - const justDisappear = { - appear: [], - vanish: [m.vanish[0]] - }; - V.PlayOnBoard(this.board, justDisappear); - // Can I take on [m.end.x, m.end.y] ? If yes, add to list: - this.getCaptures(from, [m.end.x, m.end.y], color) - .forEach(cm => moves.push(cm)); - V.UndoOnBoard(this.board, justDisappear); - } - }); - return moves; - } - - getAllValidMoves() { - // Return possible moves + potential recaptures - return super.getAllValidMoves().concat(this.getRecaptures()); - } - - filterValid(moves) { - if (moves.length == 0) return []; - // filterValid can be called when it's "not our turn": - const color = moves[0].vanish[0].c; - return moves.filter(m => { - 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; - }); - } - - atLeastOneMove(color) { - const curTurn = this.turn; - this.turn = color; - const res = super.atLeastOneMove(); - this.turn = curTurn; - return res; - } - - // White and black (partial) moves were played: merge - resolveSynchroneMove(move) { - const m1 = this.whiteMove; - const m2 = move; - // For PlayOnBoard (no need for start / end, irrelevant) - let smove = { - appear: [], - vanish: [ - m1.vanish[0], - m2.vanish[0] - ] - }; - if ((m1.end.x != m2.end.x) || (m1.end.y != m2.end.y)) { - // Easy case: two independant moves (which may (self-)capture) - smove.appear.push(m1.appear[0]); - smove.appear.push(m2.appear[0]); - // "Captured" pieces may have moved: - if (m1.appear.length == 2) { - // Castle - smove.appear.push(m1.appear[1]); - smove.vanish.push(m1.vanish[1]); - } else if ( - m1.vanish.length == 2 && - ( - m1.vanish[1].x != m2.start.x || - m1.vanish[1].y != m2.start.y - ) - ) { - smove.vanish.push(m1.vanish[1]); - } - if (m2.appear.length == 2) { - // Castle - smove.appear.push(m2.appear[1]); - smove.vanish.push(m2.vanish[1]); - } else if ( - m2.vanish.length == 2 && - ( - m2.vanish[1].x != m1.start.x || - m2.vanish[1].y != m1.start.y - ) - ) { - smove.vanish.push(m2.vanish[1]); - } - } else { - // Collision: - if (m1.vanish.length == 1 && m2.vanish.length == 1) { - // Easy case: both disappear except if one is a king - const p1 = m1.vanish[0].p; - const p2 = m2.vanish[0].p; - if ([p1, p2].includes(V.KING)) { - smove.appear.push({ - x: m1.end.x, - y: m1.end.y, - p: V.KING, - c: (p1 == V.KING ? 'w' : 'b') - }); - } - } else { - // One move is a self-capture and the other a normal capture: - // only the self-capture appears - const selfCaptureMove = - m1.vanish[1].c == m1.vanish[0].c - ? m1 - : m2; - smove.appear.push({ - x: m1.end.x, - y: m1.end.y, - p: selfCaptureMove.appear[0].p, - c: selfCaptureMove.vanish[0].c - }); - smove.vanish.push({ - x: m1.end.x, - y: m1.end.y, - p: selfCaptureMove.vanish[1].p, - c: selfCaptureMove.vanish[0].c - }); - } - } - return smove; - } - - play(move) { - move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo) - this.epSquares.push(this.getEpSquare(move)); - // Do not play on board (would reveal the move...) - this.turn = V.GetOppCol(this.turn); - this.movesCount++; - this.postPlay(move); - } - - updateCastleFlags(move) { - const firstRank = { 'w': V.size.x - 1, 'b': 0 }; - move.appear.concat(move.vanish).forEach(av => { - for (let c of ['w', 'b']) { - if (av.x == firstRank[c] && this.castleFlags[c].includes(av.y)) { - const flagIdx = (av.y == this.castleFlags[c][0] ? 0 : 1); - this.castleFlags[c][flagIdx] = 8; - } - } - }); - } - - postPlay(move) { - if (this.turn == 'b') { - // NOTE: whiteMove is used read-only, so no need to copy - this.whiteMove = move; - return; - } - - // A full turn just ended: - const smove = this.resolveSynchroneMove(move); - 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= 0 && this.underCheck('w')) - res.push(JSON.parse(JSON.stringify(this.kingPos['w']))); - if (this.kingPos['b'][0] >= 0 && this.underCheck('b')) - res.push(JSON.parse(JSON.stringify(this.kingPos['b']))); - if (color == 'b') this.play(lastMove); - return res; - } - - getCurrentScore() { - if (this.turn == 'b') - // 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"; - const whiteCanMove = this.atLeastOneMove('w'); - const blackCanMove = this.atLeastOneMove('b'); - if (whiteCanMove && blackCanMove) return "*"; - // Game over - const whiteInCheck = this.underCheck('w'); - const blackInCheck = this.underCheck('b'); - if ( - (whiteCanMove && !this.underCheck('b')) || - (blackCanMove && !this.underCheck('w')) - ) { - return "1/2"; - } - // Checkmate: could be mutual - if (!whiteCanMove && !blackCanMove) return "1/2"; - return (whiteCanMove ? "1-0" : "0-1"); - } - - getComputerMove() { - const maxeval = V.INFINITY; - const color = this.turn; - let moves = this.getAllValidMoves(); - if (moves.length == 0) - // TODO: this situation should not happen - return null; - - if (Math.random() < 0.5) - // Return a random move - return moves[randInt(moves.length)]; - - // Rank moves at depth 1: - // try to capture something (not re-capturing) - moves.forEach(m => { - V.PlayOnBoard(this.board, m); - m.eval = this.evalPosition(); - V.UndoOnBoard(this.board, m); - }); - moves.sort((a, b) => { - return (color == "w" ? 1 : -1) * (b.eval - a.eval); - }); - let candidates = [0]; - for (let i = 1; i < moves.length && moves[i].eval == moves[0].eval; i++) - candidates.push(i); - return moves[candidates[randInt(candidates.length)]]; - } - - getNotation(move) { - if (move.appear.length == 2 && move.appear[0].p == V.KING) - // Castle - return move.end.y < move.start.y ? "0-0-0" : "0-0"; - // Basic system: piece + init + dest square - return ( - move.vanish[0].p.toUpperCase() + - V.CoordsToSquare(move.start) + - V.CoordsToSquare(move.end) - ); - } - -};