X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FApocalypse.js;h=4587be3b49f399e4013956aaa54d3af24b826f0b;hb=af34341d92d47d14f396e7f4adb81f2a7e9d9a61;hp=96431c3513b5299380da490d7d4d7ccdee791d43;hpb=1a3cfdc05b40c8ecc79397be02529b35411f073f;p=vchess.git diff --git a/client/src/variants/Apocalypse.js b/client/src/variants/Apocalypse.js index 96431c35..4587be3b 100644 --- a/client/src/variants/Apocalypse.js +++ b/client/src/variants/Apocalypse.js @@ -29,6 +29,12 @@ export class ApocalypseRules extends ChessRules { return "byrow"; } + getPPpath(m) { + // Show the piece taken, if any, and not multiple pawns: + if (m.vanish.length == 1) return "Apocalypse/empty"; + return m.vanish[1].c + m.vanish[1].p; + } + static get PIECES() { return [V.PAWN, V.KNIGHT]; } @@ -63,12 +69,12 @@ export class ApocalypseRules extends ChessRules { // 4) 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; } @@ -112,7 +118,9 @@ export class ApocalypseRules extends ChessRules { } getFlagsFen() { - return this.penaltyFlags.join(""); + return ( + this.penaltyFlags['w'].toString() + this.penaltyFlags['b'].toString() + ); } setOtherVariables(fen) { @@ -126,7 +134,10 @@ export class ApocalypseRules extends ChessRules { } setFlags(fenflags) { - this.penaltyFlags = [0, 1].map(i => parseInt(fenflags[i])); + this.penaltyFlags = { + 'w': parseInt(fenflags[0]), + 'b': parseInt(fenflags[1]) + }; } getWhitemoveFen() { @@ -135,7 +146,8 @@ export class ApocalypseRules extends ChessRules { start: this.whiteMove.start, end: this.whiteMove.end, appear: this.whiteMove.appear, - vanish: this.whiteMove.vanish + vanish: this.whiteMove.vanish, + illegal: this.whiteMove.illegal }); } @@ -149,13 +161,17 @@ export class ApocalypseRules extends ChessRules { this.turn = V.GetOppCol(color); const oppMoves = super.getAllValidMoves(); this.turn = color; - // For each opponent's move, generate valid moves [from sq] + // For each opponent's move, generate valid moves [from sq if same color] let speculations = []; oppMoves.forEach(m => { V.PlayOnBoard(this.board, m); const newValidMoves = !!sq - ? super.getPotentialMovesFrom(sq) + ? ( + this.getColor(sq[0], sq[1]) == color + ? super.getPotentialMovesFrom(sq) + : [] + ) : super.getAllValidMoves(); newValidMoves.forEach(vm => { const mHash = "m" + vm.start.x + vm.start.y + vm.end.x + vm.end.y; @@ -190,8 +206,8 @@ export class ApocalypseRules extends ChessRules { // If 0 or 1 horsemen, promote in knight let knightCounter = 0; let emptySquares = []; - for (let i=0; i { - if (sq[0] != lastRank) - moves.push(this.getBasicMove([x1, y1], [sq[0], sq[1]])); + if (sq[0] != lastRank) { + let newMove = this.getBasicMove([x1, y1], [sq[0], sq[1]]); + if (!!capture) newMove.vanish.push(capture); + moves.push(newMove); + } }); return; } @@ -233,43 +261,83 @@ export class ApocalypseRules extends ChessRules { // White and black (partial) moves were played: merge resolveSynchroneMove(move) { - let m = [this.whiteMove, move]; - for (let i of [0, 1]) { - if (!!m[i].illegal) { - // Either an anticipated capture of something which didn't move - // (or not to the right square), or a push through blocus. - if ( + let m1 = this.whiteMove; + let m2 = move; + const movingLikeCapture = (m) => { + const shift = (m.vanish[0].c == 'w' ? -1 : 1); + return ( + m.start.x + shift == m.end.x && + Math.abs(m.end.y - m.start.y) == 1 + ); + }; + const isPossible = (m, other) => { + return ( + ( + m.vanish[0].p == V.KNIGHT && ( - // Push attempt - m[i].start.y == m[i].end.y && - (m[1-i].start.x != m[i].end.x || m[1-i].start.y != m[i].end.y) + m.vanish.length == 1 || + m.vanish[1].c != m.vanish[0].c || + // Self-capture attempt + ( + !other.illegal && + other.end.x == m.end.x && + other.end.y == m.end.y + ) ) - || + ) + || + ( + m.vanish[0].p == V.PAWN && + !other.illegal && ( - // Capture attempt - Math.abs(m[i].start.y - m[i].end.y) == 1 && - (m[1-i].end.x != m[i].end.x || m[1-i].end.y != m[i].end.y) + ( + // Promotion attempt + m.end.x == (m.vanish[0].c == "w" ? 0 : V.size.x - 1) && + other.vanish.length == 2 && + other.vanish[1].p == V.KNIGHT && + other.vanish[1].c == m.vanish[0].c + ) + || + ( + // Moving attempt + !movingLikeCapture(m) && + other.start.x == m.end.x && + other.start.y == m.end.y + ) + || + ( + // Capture attempt + movingLikeCapture(m) && + other.end.x == m.end.x && + other.end.y == m.end.y + ) ) - ) { - // Just discard the move, and add a penalty point - this.penaltyFlags[m[i].vanish[0].c]++; - m[i] = null; - } - } + ) + ); + }; + if (!!m1.illegal && !isPossible(m1, m2)) { + // Either an anticipated capture of something which didn't move + // (or not to the right square), or a push through blocus. + // ==> Just discard the move, and add a penalty point + this.penaltyFlags[m1.vanish[0].c]++; + m1.isNull = true; } - + if (!!m2.illegal && !isPossible(m2, m1)) { + this.penaltyFlags[m2.vanish[0].c]++; + m2.isNull = true; + } + if (!!m1.isNull) m1 = null; + if (!!m2.isNull) m2 = null; + // If one move is illegal, just execute the other + if (!m1 && !!m2) return m2; + if (!m2 && !!m1) return m1; // For PlayOnBoard (no need for start / end, irrelevant) let smove = { appear: [], vanish: [] }; - const m1 = m[0], - m2 = m[1]; - // If one move is illegal, just execute the other - if (!m1 && !!m2) return m2; - if (!m2 && !!m1) return m1; if (!m1 && !m2) return smove; - // Both move are now legal: + // Both moves are now legal or at least possible: smove.vanish.push(m1.vanish[0]); smove.vanish.push(m2.vanish[0]); if ((m1.end.x != m2.end.x) || (m1.end.y != m2.end.y)) { @@ -296,15 +364,27 @@ export class ApocalypseRules extends ChessRules { smove.vanish.push(m2.vanish[1]); } } else { - // Collision: both disappear except if different kinds (knight remains) + // Collision: priority to the anticipated capture, if any. + // If ex-aequo: knight wins (higher risk), or both disappears. + // Then, priority to the knight vs pawn: remains. + // Finally: both disappears. + let remain = null; const p1 = m1.vanish[0].p; const p2 = m2.vanish[0].p; - if ([p1, p2].includes(V.KNIGHT) && [p1, p2].includes(V.PAWN)) { + if (!!m1.illegal && !m2.illegal) remain = { c: 'w', p: p1 }; + else if (!!m2.illegal && !m1.illegal) remain = { c: 'b', p: p2 }; + if (!remain) { + // Either both are illegal or both are legal + if (p1 == V.KNIGHT && p2 == V.PAWN) remain = { c: 'w', p: p1 }; + else if (p2 == V.KNIGHT && p1 == V.PAWN) remain = { c: 'b', p: p2 }; + // If remain is still null: same type same risk, both disappear + } + if (!!remain) { smove.appear.push({ x: m1.end.x, y: m1.end.y, - p: V.KNIGHT, - c: (p1 == V.KNIGHT ? 'w' : 'b') + p: remain.p, + c: remain.c }); } } @@ -325,7 +405,6 @@ export class ApocalypseRules extends ChessRules { this.whiteMove = move; return; } - // A full turn just ended: const smove = this.resolveSynchroneMove(move); V.PlayOnBoard(this.board, smove); @@ -349,7 +428,7 @@ export class ApocalypseRules extends ChessRules { else this.whiteMove = move.whiteMove; } - getCheckSquares(color) { + getCheckSquares() { return []; } @@ -373,9 +452,9 @@ export class ApocalypseRules extends ChessRules { return "1-0"; //fmCount['b'] == 0 } // Check penaltyFlags: if a side has 2 or more, it loses - if (this.penaltyFlags.every(f => f == 2)) return "1/2"; - if (this.penaltyFlags[0] == 2) return "0-1"; - if (this.penaltyFlags[1] == 2) return "1-0"; + if (Object.values(this.penaltyFlags).every(v => v == 2)) return "1/2"; + if (this.penaltyFlags['w'] == 2) return "0-1"; + if (this.penaltyFlags['b'] == 2) return "1-0"; if (!this.atLeastOneMove('w') || !this.atLeastOneMove('b')) // Stalemate (should be very rare) return "1/2"; @@ -390,30 +469,42 @@ export class ApocalypseRules extends ChessRules { // 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) + let validMoves = []; + let illegalMoves = []; moves.forEach(m => { - V.PlayOnBoard(this.board, m); - m.eval = this.evalPosition(); - V.UndoOnBoard(this.board, m); + // Warning: m might be illegal! + if (!m.illegal) { + V.PlayOnBoard(this.board, m); + m.eval = this.evalPosition(); + V.UndoOnBoard(this.board, m); + validMoves.push(m); + } else illegalMoves.push(m); }); - moves.sort((a, b) => { + + const illegalRatio = illegalMoves.length / moves.length; + if (Math.random() < illegalRatio) + // Return a random illegal move + return illegalMoves[randInt(illegalMoves.length)]; + + validMoves.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++) + for ( + let i = 1; + i < validMoves.length && validMoves[i].eval == moves[0].eval; + i++ + ) { candidates.push(i); - return moves[candidates[randInt(candidates.length)]]; + } + return validMoves[candidates[randInt(candidates.length)]]; } getNotation(move) { // Basic system: piece + init + dest square return ( - move.vanish[0].p.toUpperCase() + + (move.vanish[0].p == V.KNIGHT ? "N" : "") + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end) );