X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=public%2Fjavascripts%2Fbase_rules.js;h=f07aaff263422efde7c1b243b3872859304183a6;hb=2526c041baf44968b0aa7b98af56730e88f6a595;hp=163049536c8a44e041008972a2ee9b96fa4fd999;hpb=818ede16c09c2f5650d7a6b7b5ea42d6dd1a0c30;p=vchess.git diff --git a/public/javascripts/base_rules.js b/public/javascripts/base_rules.js index 16304953..f07aaff2 100644 --- a/public/javascripts/base_rules.js +++ b/public/javascripts/base_rules.js @@ -52,7 +52,7 @@ class ChessRules this.moves = moves; // Use fen string to initialize variables, flags and board this.board = VariantRules.GetBoard(fen); - this.flags = VariantRules.GetFlags(fen); + this.setFlags(fen); this.initVariables(fen); } @@ -65,37 +65,37 @@ class ChessRules const position = fenParts[0].split("/"); for (let i=0; i 0 ? this.getEpSquare(this.lastMove) : undefined; @@ -106,7 +106,7 @@ class ChessRules static GetBoard(fen) { let rows = fen.split(" ")[0].split("/"); - let [sizeX,sizeY] = VariantRules.size; + const [sizeX,sizeY] = VariantRules.size; let board = doubleArray(sizeX, sizeY, ""); for (let i=0; i=0 && i=0 && j=0 && i<8 && j>=0 && j<8 && this.canTake(color, this.getColor(i,j))) - moves.push(this.getBasicMove(x, y, i, j)); + if (i>=0 && i<8 && j>=0 && j<8 && this.canTake([x,y], [i,j])) + moves.push(this.getBasicMove([x,y], [i,j])); } return moves; } // What are the pawn moves from square x,y considering color "color" ? - getPotentialPawnMoves(x, y, color) + getPotentialPawnMoves([x,y]) { - var moves = []; - var V = VariantRules; - let [sizeX,sizeY] = VariantRules.size; - let shift = (color == "w" ? -1 : 1); - let startRank = (color == "w" ? sizeY-2 : 1); - let lastRank = (color == "w" ? 0 : sizeY-1); + const color = this.turn; + let moves = []; + const V = VariantRules; + const [sizeX,sizeY] = VariantRules.size; + const shift = (color == "w" ? -1 : 1); + const firstRank = (color == 'w' ? sizeY-1 : 0); + const startRank = (color == "w" ? sizeY-2 : 1); + const lastRank = (color == "w" ? 0 : sizeY-1); if (x+shift >= 0 && x+shift < sizeX && x+shift != lastRank) { // Normal moves if (this.board[x+shift][y] == V.EMPTY) { - moves.push(this.getBasicMove(x, y, x+shift, y)); - if (x==startRank && this.board[x+2*shift][y] == V.EMPTY) + moves.push(this.getBasicMove([x,y], [x+shift,y])); + // Next condition because variants with pawns on 1st rank generally allow them to jump + if ([startRank,firstRank].includes(x) && this.board[x+2*shift][y] == V.EMPTY) { // Two squares jump - moves.push(this.getBasicMove(x, y, x+2*shift, y)); + moves.push(this.getBasicMove([x,y], [x+2*shift,y])); } } // Captures - if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1)) - && this.board[x+shift][y-1] != V.EMPTY) - { - moves.push(this.getBasicMove(x, y, x+shift, y-1)); - } - if (y0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY) + moves.push(this.getBasicMove([x,y], [x+shift,y-1])); + if (y { // Normal move if (this.board[x+shift][y] == V.EMPTY) - moves.push(this.getBasicMove(x, y, x+shift, y, p)); + moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p})); // Captures - if (y>0 && this.canTake(this.getColor(x,y), this.getColor(x+shift,y-1)) - && this.board[x+shift][y-1] != V.EMPTY) - { - moves.push(this.getBasicMove(x, y, x+shift, y-1, p)); - } - if (y0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY) + moves.push(this.getBasicMove([x,y], [x+shift,y-1], {c:color,p:p})); + if (y { - return !this.underCheck(m, color); - }); + let color = this.turn; + return moves.filter(m => { return !this.underCheck(m, color); }); } // Search for all valid moves considering current turn (for engine and game end) - getAllValidMoves(color) + getAllValidMoves() { + const color = this.turn; const oppCol = this.getOppCol(color); var potentialMoves = []; let [sizeX,sizeY] = VariantRules.size; @@ -512,8 +505,9 @@ class ChessRules } // Stop at the first move found - atLeastOneMove(color) + atLeastOneMove() { + const color = this.turn; const oppCol = this.getOppCol(color); let [sizeX,sizeY] = VariantRules.size; for (var i=0; i=0 && x+pawnShift<8) + for (let c of colors) { - for (let i of [-1,1]) + let pawnShift = (c=="w" ? 1 : -1); + if (x+pawnShift>=0 && x+pawnShift<8) { - if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN - && this.getColor(x+pawnShift,y+i)==c) + for (let i of [-1,1]) { - return true; + if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN + && this.getColor(x+pawnShift,y+i)==c) + { + return true; + } } } } @@ -567,42 +564,42 @@ class ChessRules } // Is square x,y attacked by rooks of color c ? - isAttackedByRook(sq, color) + isAttackedByRook(sq, colors) { - return this.isAttackedBySlideNJump(sq, color, + return this.isAttackedBySlideNJump(sq, colors, VariantRules.ROOK, VariantRules.steps[VariantRules.ROOK]); } // Is square x,y attacked by knights of color c ? - isAttackedByKnight(sq, color) + isAttackedByKnight(sq, colors) { - return this.isAttackedBySlideNJump(sq, color, + return this.isAttackedBySlideNJump(sq, colors, VariantRules.KNIGHT, VariantRules.steps[VariantRules.KNIGHT], "oneStep"); } // Is square x,y attacked by bishops of color c ? - isAttackedByBishop(sq, color) + isAttackedByBishop(sq, colors) { - return this.isAttackedBySlideNJump(sq, color, + return this.isAttackedBySlideNJump(sq, colors, VariantRules.BISHOP, VariantRules.steps[VariantRules.BISHOP]); } // Is square x,y attacked by queens of color c ? - isAttackedByQueen(sq, color) + isAttackedByQueen(sq, colors) { - return this.isAttackedBySlideNJump(sq, color, + return this.isAttackedBySlideNJump(sq, colors, VariantRules.QUEEN, VariantRules.steps[VariantRules.QUEEN]); } // Is square x,y attacked by king of color c ? - isAttackedByKing(sq, color) + isAttackedByKing(sq, colors) { - return this.isAttackedBySlideNJump(sq, color, + return this.isAttackedBySlideNJump(sq, colors, VariantRules.KING, VariantRules.steps[VariantRules.QUEEN], "oneStep"); } // Generic method for non-pawn pieces ("sliding or jumping"): is x,y attacked by piece != color ? - isAttackedBySlideNJump([x,y], c,piece,steps,oneStep) + isAttackedBySlideNJump([x,y], colors, piece, steps, oneStep) { for (let step of steps) { @@ -614,7 +611,7 @@ class ChessRules ry += step[1]; } if (rx>=0 && rx<8 && ry>=0 && ry<8 && this.board[rx][ry] != VariantRules.EMPTY - && this.getPiece(rx,ry) == piece && this.getColor(rx,ry) == c) + && this.getPiece(rx,ry) == piece && colors.includes(this.getColor(rx,ry))) { return true; } @@ -623,20 +620,22 @@ class ChessRules } // Is color c under check after move ? - underCheck(move, c) + underCheck(move) { + const color = this.turn; this.play(move); - let res = this.isAttacked(this.kingPos[c], this.getOppCol(c)); + let res = this.isAttacked(this.kingPos[color], this.getOppCol(color)); this.undo(move); return res; } // On which squares is color c under check (after move) ? - getCheckSquares(move, c) + getCheckSquares(move) { this.play(move); - let res = this.isAttacked(this.kingPos[c], this.getOppCol(c)) - ? [ JSON.parse(JSON.stringify(this.kingPos[c])) ] //need to duplicate! + const color = this.turn; //opponent + let res = this.isAttacked(this.kingPos[color], this.getOppCol(color)) + ? [ JSON.parse(JSON.stringify(this.kingPos[color])) ] //need to duplicate! : [ ]; this.undo(move); return res; @@ -671,7 +670,7 @@ class ChessRules { this.kingPos[c][0] = move.appear[0].x; this.kingPos[c][1] = move.appear[0].y; - this.flags[c] = [false,false]; + this.castleFlags[c] = [false,false]; return; } const oppCol = this.getOppCol(c); @@ -679,14 +678,14 @@ class ChessRules if (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; - this.flags[c][flagIdx] = false; + 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[c].includes(move.end.y)) { - const flagIdx = move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1; - this.flags[oppCol][flagIdx] = false; + const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1); + this.castleFlags[oppCol][flagIdx] = false; } } @@ -700,11 +699,11 @@ class ChessRules play(move, ingame) { + console.log("AVANT " + this.getNotation(move) + " " + this.board[1][5]); if (!!ingame) move.notation = this.getNotation(move); - // Save flags (for undo) - move.flags = JSON.stringify(this.flags); //TODO: less costly? + move.flags = JSON.stringify(this.flags); //save flags (for undo) this.updateVariables(move); this.moves.push(move); this.epSquares.push( this.getEpSquare(move) ); @@ -717,13 +716,14 @@ class ChessRules this.epSquares.pop(); this.moves.pop(); this.unupdateVariables(move); - this.flags = JSON.parse(move.flags); + this.parseFlags(JSON.parse(move.flags)); + console.log("APRES " + this.getNotation(move) + " " + this.board[1][5]); } ////////////// // END OF GAME - checkGameOver(color) + checkRepetition() { // Check for 3 repetitions if (this.moves.length >= 8) @@ -735,23 +735,28 @@ class ChessRules _.isEqual(this.moves[L-3], this.moves[L-7]) && _.isEqual(this.moves[L-4], this.moves[L-8])) { - return "1/2 (repetition)"; + return true; } } + return false; + } - if (this.atLeastOneMove(color)) - { - // game not over + checkGameOver() + { + if (this.checkRepetition()) + return "1/2"; + + if (this.atLeastOneMove()) // game not over return "*"; - } // Game over - return this.checkGameEnd(color); + return this.checkGameEnd(); } - // Useful stand-alone for engine - checkGameEnd(color) + // No moves are possible: compute score + checkGameEnd() { + const color = this.turn; // No valid move: stalemate or checkmate? if (!this.isAttacked(this.kingPos[color], this.getOppCol(color))) return "1/2"; @@ -775,12 +780,12 @@ class ChessRules } // Assumption: at least one legal move - getComputerMove(color) + getComputerMove() { - const oppCol = this.getOppCol(color); + const color = this.turn; // Rank moves using a min-max at depth 2 - let moves1 = this.getAllValidMoves(color); + let moves1 = this.getAllValidMoves(); for (let i=0; i { return (color=="w" ? 1 : -1) * (b.eval - a.eval); }); @@ -825,11 +830,12 @@ class ChessRules return moves1[_.sample(candidates, 1)]; } - alphabeta(color, oppCol, depth, alpha, beta) + alphabeta(depth, alpha, beta) { - if (!this.atLeastOneMove(color)) + const color = this.turn; + if (!this.atLeastOneMove()) { - switch (this.checkGameEnd(color)) + switch (this.checkGameEnd()) { case "1/2": return 0; default: return color=="w" ? -1000 : 1000; @@ -837,14 +843,14 @@ class ChessRules } if (depth == 0) return this.evalPosition(); - const moves = this.getAllValidMoves(color); + const moves = this.getAllValidMoves(); let v = color=="w" ? -1000 : 1000; if (color == "w") { for (let i=0; i= beta) @@ -856,7 +862,7 @@ class ChessRules for (let i=0; i= beta) @@ -989,7 +995,7 @@ class ChessRules for (let i of ['w','b']) { for (let j=0; j<2; j++) - fen += this.flags[i][j] ? '1' : '0'; + fen += this.castleFlags[i][j] ? '1' : '0'; } return fen; } @@ -1041,7 +1047,7 @@ class ChessRules let pgn = ""; pgn += '[Site "vchess.club"]
'; const d = new Date(); - const opponent = this.mode=="human" ? "Anonymous" : "Computer"; + const opponent = mode=="human" ? "Anonymous" : "Computer"; pgn += '[Date "' + d.getFullYear() + '-' + (d.getMonth()+1) + '-' + d.getDate() + '"]
'; pgn += '[White "' + (mycolor=='w'?'Myself':opponent) + '"]
'; pgn += '[Black "' + (mycolor=='b'?'Myself':opponent) + '"]
';