X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FSynchrone.js;h=c8f2be575e97d392f93f8374ca885a59f6c920bd;hb=af34341d92d47d14f396e7f4adb81f2a7e9d9a61;hp=372bc1eea2c306f7ba30ad347c96e4817da72e34;hpb=d54f6261c9e30f4eabb402ad301dd5c5e40fb656;p=vchess.git diff --git a/client/src/variants/Synchrone.js b/client/src/variants/Synchrone.js index 372bc1ee..c8f2be57 100644 --- a/client/src/variants/Synchrone.js +++ b/client/src/variants/Synchrone.js @@ -1,11 +1,9 @@ -// TODO: debug, and forbid self-capture of king. - import { ChessRules } from "@/base_rules"; import { randInt } from "@/utils/alea"; export class SynchroneRules extends ChessRules { static get CanAnalyze() { - return true; //false; + return false; } static get ShowMoves() { @@ -18,12 +16,12 @@ export class SynchroneRules extends ChessRules { // 5) Check whiteMove if ( ( - fenParsed.turn == "w" && + fenParsed.turn == "b" && // NOTE: do not check really JSON stringified move... (!fenParsed.whiteMove || fenParsed.whiteMove == "-") ) || - (fenParsed.turn == "b" && fenParsed.whiteMove != "-") + (fenParsed.turn == "w" && fenParsed.whiteMove != "-") ) { return false; } @@ -108,20 +106,20 @@ export class SynchroneRules extends ChessRules { }); } - // NOTE: lazy unefficient implementation (for now. TODO?) getPossibleMovesFrom([x, y]) { - const moves = this.getAllValidMoves(); - return moves.filter(m => { - return m.start.x == x && m.start.y == 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; } - getCaptures(x, y) { - const color = this.turn; + // Aux function used to find opponent and self captures + getCaptures(from, to, color) { const sliderAttack = (xx, yy, allowedSteps) => { - const deltaX = xx - x, + const deltaX = xx - to[0], absDeltaX = Math.abs(deltaX); - const deltaY = yy - y, + const deltaY = yy - to[1], absDeltaY = Math.abs(deltaY); const step = [ deltaX / absDeltaX || 0, deltaY / absDeltaY || 0 ]; if ( @@ -131,61 +129,70 @@ export class SynchroneRules extends ChessRules { ) { return null; } - let sq = [ x + step[0], y + step[1] ]; + 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], [x, y]); + return this.getBasicMove([xx, yy], [to[0], to[1]]); }; - // Can I take on the square [x, y] ? + // 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 = []; - for (let i=0; i<8; i++) { - for (let j=0; j<8; j++) { - if (this.getColor(i, j) == color) { - switch (this.getPiece(i, j)) { - case V.PAWN: { - // Pushed pawns move as enemy pawns - const shift = (color == 'w' ? 1 : -1); - if (x + shift == i && Math.abs(y - j) == 1) - moves.push(this.getBasicMove([i, j], [x, y])); - break; - } - case V.KNIGHT: { - const deltaX = Math.abs(i - x); - const deltaY = Math.abs(j - y); - if ( - deltaX + deltaY == 3 && - [1, 2].includes(deltaX) && - [1, 2].includes(deltaY) - ) { - moves.push(this.getBasicMove([i, j], [x, y])); - } - break; - } - case V.KING: - if (Math.abs(i - x) <= 1 && Math.abs(j - y) <= 1) - moves.push(this.getBasicMove([i, j], [x, y])); - break; - case V.ROOK: { - const mv = sliderAttack(i, j, V.steps[V.ROOK]); - if (!!mv) moves.push(mv); - break; - } - case V.BISHOP: { - const mv = sliderAttack(i, j, V.steps[V.BISHOP]); - if (!!mv) moves.push(mv); - break; - } - case V.QUEEN: { - const mv = sliderAttack( - i, j, V.steps[V.ROOK].concat(V.steps[V.BISHOP])); - if (!!mv) moves.push(mv); - break; - } + 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); } } } @@ -193,46 +200,52 @@ export class SynchroneRules extends ChessRules { return this.filterValid(moves); } - getAllValidMoves() { + getRecaptures(from) { + // 1) Generate all opponent's capturing moves + let oppCaptureMoves = []; const color = this.turn; - // 0) Generate our possible moves - let myMoves = super.getAllValidMoves(); + 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 = {}; - const getMoveHash = (move) => { - return ( - "m" + move.start.x + move.start.y + - move.end.x + move.end.y + - // Also use m.appear[0].p for pawn promotions - move.appear[0].p - ); - }; - myMoves.forEach(m => moveSet[getMoveHash(m)] = true); - // 1) Generate all opponent's moves - this.turn = V.GetOppCol(color); - const oppMoves = super.getAllValidMoves(); - this.turn = color; - // 2) Play each opponent's move, and see if captures are possible: - // --> capturing moving unit only (otherwise some issues) - oppMoves.forEach(m => { - V.PlayOnBoard(this.board, m); - // Can I take on [m.end.x, m.end.y] ? - // If yes and not already in list, add it (without the capturing part) - let capturingMoves = this.getCaptures(m.end.x, m.end.y); - capturingMoves.forEach(cm => { - const cmHash = getMoveHash(cm); - if (!moveSet[cmHash]) { - // The captured unit hasn't moved yet, so temporarily cancel capture - cm.vanish.pop(); - // If m is itself a capturing move: then replace by self-capture - if (m.vanish.length == 2) cm.vanish.push(m.vanish[1]); - myMoves.push(cm); - moveSet[cmHash] = true; - } - }); - V.UndoOnBoard(this.board, m); + 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 myMoves; + return moves; + } + + getAllValidMoves() { + // Return possible moves + potential recaptures + return super.getAllValidMoves().concat(this.getRecaptures()); } filterValid(moves) { @@ -278,20 +291,28 @@ export class SynchroneRules extends ChessRules { smove.appear.push(m1.appear[0]); smove.appear.push(m2.appear[0]); // "Captured" pieces may have moved: - if ( + if (m1.appear.length == 2) { + // Castle + smove.appear.push(m1.appear[1]); + smove.vanish.push(m1.vanish[1]); + } else if ( m1.vanish.length == 2 && ( - m2.end.x != m1.vanish[1].x || - m2.end.y != m1.vanish[1].y + m1.vanish[1].x != m2.start.x || + m1.vanish[1].y != m2.start.y ) ) { smove.vanish.push(m1.vanish[1]); } - if ( + if (m2.appear.length == 2) { + // Castle + smove.appear.push(m2.appear[1]); + smove.vanish.push(m2.vanish[1]); + } else if ( m2.vanish.length == 2 && ( - m1.end.x != m2.vanish[1].x || - m1.end.y != m2.vanish[1].y + m2.vanish[1].x != m1.start.x || + m2.vanish[1].y != m1.start.y ) ) { smove.vanish.push(m2.vanish[1]); @@ -313,8 +334,6 @@ export class SynchroneRules extends ChessRules { } else { // One move is a self-capture and the other a normal capture: // only the self-capture appears - console.log(m1); - console.log(m2); const selfCaptureMove = m1.vanish[1].c == m1.vanish[0].c ? m1 @@ -325,6 +344,12 @@ export class SynchroneRules extends ChessRules { 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; @@ -361,6 +386,7 @@ export class SynchroneRules extends ChessRules { // 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 @@ -399,18 +425,27 @@ export class SynchroneRules extends ChessRules { } postUndo(move) { - if (this.turn == 'w') + if (this.turn == 'w') { // Reset king positions: scan board this.scanKings(); + // Also reset whiteMove + this.whiteMove = null; + } else this.whiteMove = move.whiteMove; } - getCheckSquares(color) { - if (color == 'b') return []; + getCheckSquares() { + const color = this.turn; + if (color == 'b') { + // kingPos must be reset for appropriate highlighting: + var lastMove = JSON.parse(JSON.stringify(this.whiteMove)); + this.undo(lastMove); //will erase whiteMove, thus saved above + } let res = []; - if (this.underCheck('w')) + if (this.kingPos['w'][0] >= 0 && this.underCheck('w')) res.push(JSON.parse(JSON.stringify(this.kingPos['w']))); - if (this.underCheck('b')) + 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; } @@ -465,4 +500,16 @@ export class SynchroneRules extends ChessRules { 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) + ); + } };