X-Git-Url: https://git.auder.net/js/rpsls.js?a=blobdiff_plain;f=client%2Fsrc%2Fbase_rules.js;h=4c32fe69ea34454f7bec628b2afa53e5ca38e7c9;hb=afbf3ca7151ef15a9e579b0f913683ab212396c4;hp=b39fe9c457aa51fefad719cae5292e69fa066471;hpb=f9c36b2da005b596ad656f4b6cc4e09ef3c656f1;p=vchess.git diff --git a/client/src/base_rules.js b/client/src/base_rules.js index b39fe9c4..4c32fe69 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -197,14 +197,10 @@ export const ChessRules = class ChessRules { const move = moveOrSquare; const s = move.start, e = move.end; - // NOTE: next conditions are first for Crazyhouse, and last for Checkered - // TODO: Checkered exceptions are too weird and should move in its own file. if ( - move.vanish.length > 0 && Math.abs(s.x - e.x) == 2 && s.y == e.y && - move.vanish[0].p == V.PAWN && - ["w", "b"].includes(move.appear[0].c) + move.appear[0].p == V.PAWN ) { return { x: (s.x + e.x) / 2, @@ -432,6 +428,7 @@ export const ChessRules = class ChessRules { this.INIT_COL_ROOK = { w: [-1, -1], b: [-1, -1] }; this.kingPos = { w: [-1, -1], b: [-1, -1] }; //squares of white and black king const fenRows = V.ParseFen(fen).position.split("/"); + const startRow = { 'w': V.size.x - 1, 'b': 0 }; for (let i = 0; i < fenRows.length; i++) { let k = 0; //column index on board for (let j = 0; j < fenRows[i].length; j++) { @@ -445,12 +442,16 @@ export const ChessRules = class ChessRules { this.INIT_COL_KING["w"] = k; break; case "r": - if (this.INIT_COL_ROOK["b"][0] < 0) this.INIT_COL_ROOK["b"][0] = k; - else this.INIT_COL_ROOK["b"][1] = k; + if (i == startRow['b']) { + if (this.INIT_COL_ROOK["b"][0] < 0) this.INIT_COL_ROOK["b"][0] = k; + else this.INIT_COL_ROOK["b"][1] = k; + } break; case "R": - if (this.INIT_COL_ROOK["w"][0] < 0) this.INIT_COL_ROOK["w"][0] = k; - else this.INIT_COL_ROOK["w"][1] = k; + if (i == startRow['w']) { + if (this.INIT_COL_ROOK["w"][0] < 0) this.INIT_COL_ROOK["w"][0] = k; + else this.INIT_COL_ROOK["w"][1] = k; + } break; default: { const num = parseInt(fenRows[i].charAt(j)); @@ -645,7 +646,6 @@ export const ChessRules = class ChessRules { const firstRank = color == "w" ? sizeX - 1 : 0; const startRank = color == "w" ? sizeX - 2 : 1; const lastRank = color == "w" ? 0 : sizeX - 1; - const pawnColor = this.getColor(x, y); //can be different for checkered // NOTE: next condition is generally true (no pawn on last rank) if (x + shiftX >= 0 && x + shiftX < sizeX) { @@ -658,7 +658,7 @@ export const ChessRules = class ChessRules { for (let piece of finalPieces) { moves.push( this.getBasicMove([x, y], [x + shiftX, y], { - c: pawnColor, + c: color, p: piece }) ); @@ -683,7 +683,7 @@ export const ChessRules = class ChessRules { for (let piece of finalPieces) { moves.push( this.getBasicMove([x, y], [x + shiftX, y + shiftY], { - c: pawnColor, + c: color, p: piece }) ); @@ -1001,7 +1001,7 @@ export const ChessRules = class ChessRules { // After move is played, update variables + flags updateVariables(move) { let piece = undefined; - // TODO: update variables before move is played, and just use this.turn ? + // TODO: update variables before move is played, and just use this.turn? // (doesn't work in general, think MarseilleChess) let c = undefined; if (move.vanish.length >= 1) { @@ -1034,13 +1034,13 @@ export const ChessRules = class ChessRules { move.start.x == firstRank && //our rook moves? this.INIT_COL_ROOK[c].includes(move.start.y) ) { - const flagIdx = move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1; + const flagIdx = (move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1); this.castleFlags[c][flagIdx] = false; } else if ( move.end.x == oppFirstRank && //we took opponent rook? this.INIT_COL_ROOK[oppCol].includes(move.end.y) ) { - const flagIdx = move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1; + const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1); this.castleFlags[oppCol][flagIdx] = false; } } @@ -1058,7 +1058,7 @@ export const ChessRules = class ChessRules { play(move) { // DEBUG: // if (!this.states) this.states = []; -// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); +// const stateFen = this.getBaseFen() + this.getTurnFen();// + this.getFlagsFen(); // this.states.push(stateFen); if (V.HasFlags) move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo) @@ -1078,7 +1078,7 @@ export const ChessRules = class ChessRules { this.unupdateVariables(move); // DEBUG: -// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); +// const stateFen = this.getBaseFen() + this.getTurnFen();// + this.getFlagsFen(); // if (stateFen != this.states[this.states.length-1]) debugger; // this.states.pop(); } @@ -1133,50 +1133,56 @@ export const ChessRules = class ChessRules { getComputerMove() { const maxeval = V.INFINITY; const color = this.turn; - // Some variants may show a bigger moves list to the human (Switching), - // thus the argument "computer" below (which is generally ignored) let moves1 = this.getAllValidMoves(); if (moves1.length == 0) // TODO: this situation should not happen return null; - // Rank moves using a min-max at depth 2 + // Rank moves using a min-max at depth 2 (if search_depth >= 2!) for (let i = 0; i < moves1.length; i++) { - // Initial self evaluation is very low: "I'm checkmated" - moves1[i].eval = (color == "w" ? -1 : 1) * maxeval; this.play(moves1[i]); const score1 = this.getCurrentScore(); - let eval2 = undefined; - if (score1 == "*") { - // Initial enemy evaluation is very low too, for him - eval2 = (color == "w" ? 1 : -1) * maxeval; - // Second half-move: - let moves2 = this.getAllValidMoves(); - for (let j = 0; j < moves2.length; j++) { - this.play(moves2[j]); - const score2 = this.getCurrentScore(); - let evalPos = 0; //1/2 value - switch (score2) { - case "*": - evalPos = this.evalPosition(); - break; - case "1-0": - evalPos = maxeval; - break; - case "0-1": - evalPos = -maxeval; - break; - } - if ( - (color == "w" && evalPos < eval2) || - (color == "b" && evalPos > eval2) - ) { - eval2 = evalPos; - } - this.undo(moves2[j]); + if (score1 != "*") { + moves1[i].eval = + score1 == "1/2" + ? 0 + : (score1 == "1-0" ? 1 : -1) * maxeval; + } + if (V.SEARCH_DEPTH == 1 || score1 != "*") { + if (!moves1[i].eval) moves1[i].eval = this.evalPosition(); + this.undo(moves1[i]); + continue; + } + // Initial self evaluation is very low: "I'm checkmated" + moves1[i].eval = (color == "w" ? -1 : 1) * maxeval; + // Initial enemy evaluation is very low too, for him + let eval2 = (color == "w" ? 1 : -1) * maxeval; + // Second half-move: + let moves2 = this.getAllValidMoves(); + for (let j = 0; j < moves2.length; j++) { + this.play(moves2[j]); + const score2 = this.getCurrentScore(); + let evalPos = 0; //1/2 value + switch (score2) { + case "*": + evalPos = this.evalPosition(); + break; + case "1-0": + evalPos = maxeval; + break; + case "0-1": + evalPos = -maxeval; + break; + } + if ( + (color == "w" && evalPos < eval2) || + (color == "b" && evalPos > eval2) + ) { + eval2 = evalPos; } - } else eval2 = score1 == "1/2" ? 0 : (score1 == "1-0" ? 1 : -1) * maxeval; + this.undo(moves2[j]); + } if ( (color == "w" && eval2 > moves1[i].eval) || (color == "b" && eval2 < moves1[i].eval) @@ -1190,19 +1196,9 @@ export const ChessRules = class ChessRules { }); // console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; })); - let candidates = [0]; //indices of candidates moves - for (let j = 1; j < moves1.length && moves1[j].eval == moves1[0].eval; j++) - candidates.push(j); - let currentBest = moves1[candidates[randInt(candidates.length)]]; - // Skip depth 3+ if we found a checkmate (or if we are checkmated in 1...) if (V.SEARCH_DEPTH >= 3 && Math.abs(moves1[0].eval) < V.THRESHOLD_MATE) { - // From here, depth >= 3: may take a while, so we control time - const timeStart = Date.now(); for (let i = 0; i < moves1.length; i++) { - if (Date.now() - timeStart >= 5000) - //more than 5 seconds - return currentBest; //depth 2 at least this.play(moves1[i]); // 0.1 * oldEval : heuristic to avoid some bad moves (not all...) moves1[i].eval = @@ -1213,10 +1209,9 @@ export const ChessRules = class ChessRules { moves1.sort((a, b) => { return (color == "w" ? 1 : -1) * (b.eval - a.eval); }); - } else return currentBest; -// console.log(moves1.map(m => { return [this.getNotation(m), m.eval]; })); + } - candidates = [0]; + let candidates = [0]; for (let j = 1; j < moves1.length && moves1[j].eval == moves1[0].eval; j++) candidates.push(j); return moves1[candidates[randInt(candidates.length)]];