X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FDoublemove2.js;h=f9d5e4f1ad1e7be005f29e2a16387e87ab6cd55c;hb=737a5dafb39740ebe304b8d0a82df85070def571;hp=d1afff8371db3fc353a3e6d2adf18edda5966d55;hpb=5d75c82c70bcd4bcc43b65571231f0ba1b532b79;p=vchess.git diff --git a/client/src/variants/Doublemove2.js b/client/src/variants/Doublemove2.js index d1afff83..f9d5e4f1 100644 --- a/client/src/variants/Doublemove2.js +++ b/client/src/variants/Doublemove2.js @@ -79,40 +79,23 @@ export class Doublemove2Rules extends ChessRules { return moves; } - isAttacked(sq, color, castling) { - const singleMoveAttack = super.isAttacked(sq, color); - if (singleMoveAttack) return true; - if (!!castling) { - if (this.subTurn == 1) - // Castling at move 1 could be done into check - return false; - return singleMoveAttack; - } - // Double-move allowed: - const curTurn = this.turn; - this.turn = color; - const moves1 = super.getAllPotentialMoves(); - this.turn = curTurn; - for (let move of moves1) { - this.play(move); - const res = super.isAttacked(sq, color); - this.undo(move); - if (res) return res; - } + isAttacked() { + // Goal is king capture => no checks return false; } filterValid(moves) { - if (this.subTurn == 1) { - return moves.filter(m1 => { - this.play(m1); - // NOTE: no recursion because next call will see subTurn == 2 - const res = super.atLeastOneMove(); - this.undo(m1); - return res; - }); - } - return super.filterValid(moves); + return moves; + } + + getCheckSquares() { + return []; + } + + getCurrentScore() { + const color = this.turn; + if (this.kingPos[color][0] < 0) return (color == 'w' ? "0-1" : "1-0"); + return "*"; } play(move) { @@ -127,7 +110,13 @@ export class Doublemove2Rules extends ChessRules { else { this.epSquares.push([epSq]); this.movesCount++; - if (this.movesCount == 1) this.turn = "b"; + if ( + this.movesCount == 1 || + // King is captured at subTurn 1? + (move.vanish.length == 2 && move.vanish[1].p == V.KING) + ) { + this.turn = "b"; + } } if (this.movesCount > 1) this.subTurn = 3 - this.subTurn; this.postPlay(move); @@ -138,13 +127,17 @@ export class Doublemove2Rules extends ChessRules { const piece = move.vanish[0].p; const firstRank = c == "w" ? V.size.x - 1 : 0; - if (piece == V.KING && move.appear.length > 0) { - this.kingPos[c][0] = move.appear[0].x; - this.kingPos[c][1] = move.appear[0].y; + if (piece == V.KING) { + this.kingPos[c] = [move.appear[0].x, move.appear[0].y]; this.castleFlags[c] = [V.size.y, V.size.y]; return; } const oppCol = V.GetOppCol(c); + if (move.vanish.length == 2 && move.vanish[1].p == V.KING) { + // Opponent's king is captured, game over + this.kingPos[oppCol] = [-1, -1]; + move.captureKing = true; //for undo + } const oppFirstRank = V.size.x - 1 - firstRank; if ( move.start.x == firstRank && //our rook moves? @@ -152,7 +145,8 @@ export class Doublemove2Rules extends ChessRules { ) { const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1); this.castleFlags[c][flagIdx] = V.size.y; - } else if ( + } + if ( move.end.x == oppFirstRank && //we took opponent rook? this.castleFlags[oppCol].includes(move.end.y) ) { @@ -164,7 +158,7 @@ export class Doublemove2Rules extends ChessRules { undo(move) { this.disaggregateFlags(JSON.parse(move.flags)); V.UndoOnBoard(this.board, move); - if (this.subTurn == 2 || this.movesCount == 1) { + if (this.subTurn == 2 || this.movesCount == 1 || !!move.captureKing) { this.epSquares.pop(); this.movesCount--; if (this.movesCount == 0) this.turn = "w"; @@ -175,55 +169,69 @@ export class Doublemove2Rules extends ChessRules { this.turn = V.GetOppCol(this.turn); } if (this.movesCount > 0) this.subTurn = 3 - this.subTurn; - super.postUndo(move); + this.postUndo(move); } - static get VALUES() { - return { - p: 1, - r: 5, - n: 3, - b: 3, - q: 7, //slightly less than in orthodox game - k: 1000 - }; + postUndo(move) { + if (move.vanish.length == 2 && move.vanish[1].p == V.KING) + // Opponent's king was captured + this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y]; + super.postUndo(move); } - // No alpha-beta here, just adapted min-max at depth 1(+1) + // No alpha-beta here, just adapted min-max at depth 2(+1) getComputerMove() { + const maxeval = V.INFINITY; const color = this.turn; + const oppCol = V.GetOppCol(this.turn); + + // Search best (half) move for opponent turn + const getBestMoveEval = () => { + let score = this.getCurrentScore(); + if (score != "*") return maxeval * (score == "1-0" ? 1 : -1); + let moves = this.getAllValidMoves(); + let res = oppCol == "w" ? -maxeval : maxeval; + for (let m of moves) { + this.play(m); + score = this.getCurrentScore(); + if (score != "*") { + // King captured + this.undo(m); + return maxeval * (score == "1-0" ? 1 : -1); + } + const evalPos = this.evalPosition(); + res = oppCol == "w" ? Math.max(res, evalPos) : Math.min(res, evalPos); + this.undo(m); + } + return res; + }; + const moves11 = this.getAllValidMoves(); if (this.movesCount == 0) // First white move at random: return moves11[randInt(moves11.length)]; - - let doubleMoves = []; + let doubleMove = null; + let bestEval = Number.POSITIVE_INFINITY * (color == 'w' ? -1 : 1); // Rank moves using a min-max at depth 2 for (let i = 0; i < moves11.length; i++) { this.play(moves11[i]); const moves12 = this.getAllValidMoves(); for (let j = 0; j < moves12.length; j++) { this.play(moves12[j]); - doubleMoves.push({ - moves: [moves11[i], moves12[j]], - eval: this.evalPosition() - }); + // Small fluctuations to uniformize play a little + const evalM = getBestMoveEval() + 0.05 - Math.random() / 10 + if ( + (color == 'w' && evalM > bestEval) || + (color == 'b' && evalM < bestEval) + ) { + doubleMove = [moves11[i], moves12[j]]; + bestEval = evalM; + } this.undo(moves12[j]); } this.undo(moves11[i]); } - - doubleMoves.sort((a, b) => { - return (color == "w" ? 1 : -1) * (b.eval - a.eval); - }); - let candidates = [0]; //indices of candidates moves - for ( - let i = 1; - i < doubleMoves.length && doubleMoves[i].eval == doubleMoves[0].eval; - i++ - ) { - candidates.push(i); - } - return doubleMoves[randInt(candidates.length)].moves; + // TODO: not always the best move played (why ???) + return doubleMove; } };