X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=public%2Fjavascripts%2Fvariants%2FCheckered.js;h=585d20ff99ea63ab4c4d81aac0869eac0f84e50a;hb=7931e479adf93c87771ded1892a0873af72ae46d;hp=6ed73a8bf8131ccbbe8592d87ef3fb23e21653b2;hpb=46302e643947a66a5593a8eb1140d314effcea95;p=vchess.git diff --git a/public/javascripts/variants/Checkered.js b/public/javascripts/variants/Checkered.js index 6ed73a8b..585d20ff 100644 --- a/public/javascripts/variants/Checkered.js +++ b/public/javascripts/variants/Checkered.js @@ -1,6 +1,5 @@ class CheckeredRules extends ChessRules { - // Path to pieces static getPpath(b) { return b[0]=='c' ? "Checkered/"+b : b; @@ -20,224 +19,112 @@ class CheckeredRules extends ChessRules } static fen2board(f) { + // Tolerate upper-case versions of checkered pieces (why not?) const checkered_pieces = { 's': 'p', + 'S': 'p', 't': 'q', + 'T': 'q', 'u': 'r', + 'U': 'r', 'c': 'b', + 'C': 'b', 'o': 'n', + 'O': 'n', }; if (Object.keys(checkered_pieces).includes(f)) return 'c'+checkered_pieces[f]; return ChessRules.fen2board(f); } - static GetFlags(fen) - { - let flags = [ - ChessRules.GetFlags(fen), //castle - { - "w": new Array(8), //pawns can move 2 squares - "b": new Array(8) - } - ]; - const fenFlags = fen.split(" ")[1].substr(4); //skip first 4 digits, for castle - for (let c of ['w','b']) - { - for (let i=0; i<8; i++) - flags[1][c][i] = (fenFlags.charAt((c=='w'?0:8)+i) == '1'); - } - return flags; + static get PIECES() { + return ChessRules.PIECES.concat(['s','t','u','c','o']); } - canTake([x1,y1], [x2,y2]) + static IsGoodFlags(flags) { - const color1 = this.getColor(x1,y1); - const color2 = this.getColor(x2,y2); - // Checkered aren't captured - return color1 != color2 && color2 != 'c' && (color1 != 'c' || color2 != this.turn); + // 4 for castle + 16 for pawns + return !!flags.match(/^[01]{20,20}$/); } - addCaptures([sx,sy], [ex,ey], moves) + setFlags(fen) { - const piece = this.getPiece(sx,sy); - if (piece != VariantRules.KING) + super.setFlags(fen); //castleFlags + this.pawnFlags = + { + "w": new Array(8), //pawns can move 2 squares? + "b": new Array(8) + }; + const flags = fen.split(" ")[1].substr(4); //skip first 4 digits, for castle + for (let c of ['w','b']) { - moves.push(this.getBasicMove([sx,sy], [ex,ey], {c:'c',p:piece})); - const takePiece = this.getPiece(ex,ey); - if (takePiece != piece) - moves.push(this.getBasicMove([sx,sy], [ex,ey], {c:'c',p:takePiece})); + for (let i=0; i<8; i++) + this.pawnFlags[c][i] = (flags.charAt((c=='w'?0:8)+i) == '1'); } - else - moves.push(this.getBasicMove([sx,sy], [ex,ey])); } - // Generic method to find possible moves of non-pawn pieces ("sliding or jumping") - getSlideNJumpMoves([x,y], steps, oneStep) - { - const color = this.getColor(x,y); - let moves = []; - const [sizeX,sizeY] = VariantRules.size; - outerLoop: - for (var loop=0; loop=0 && i=0 && j=0 && i<8 && j>=0 && j<8 && this.canTake([x,y], [i,j])) - this.addCaptures([x,y], [i,j], moves); - } - return moves; + // Aggregates flags into one object + get flags() { + return [this.castleFlags, this.pawnFlags]; } - // What are the pawn moves from square x,y considering color "color" ? - getPotentialPawnMoves([x,y]) + // Reverse operation + parseFlags(flags) { - const color = this.getColor(x,y); - var moves = []; - var V = VariantRules; - let [sizeX,sizeY] = VariantRules.size; - const c = (color == 'c' ? this.turn : color); - const shift = (c == "w" ? -1 : 1); - let startRank = (c == "w" ? sizeY-2 : 1); - let lastRank = (c == "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 && this.flags[1][c][y]) - { - // Two squares jump - moves.push(this.getBasicMove([x,y], [x+2*shift,y])); - } - } - // Captures - if (y>0 && this.canTake([x,y], [x+shift,y-1]) && this.board[x+shift][y-1] != V.EMPTY) - this.addCaptures([x,y], [x+shift,y-1], moves); - if (y { - // Normal move - if (this.board[x+shift][y] == V.EMPTY) - moves.push(this.getBasicMove([x,y], [x+shift,y], {c:color,p:p})); - // Captures - if (y>0 && 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:'c',p:p})); - if (y0 ? this.epSquares[Lep-1] : undefined; - if (!!epSquare && epSquare.x == x+shift && Math.abs(epSquare.y - y) == 1) - { - let epStep = epSquare.y - y; - var enpassantMove = this.getBasicMove([x,y], [x+shift,y+epStep]); - enpassantMove.vanish.push({ - x: x, - y: y+epStep, - p: 'p', - c: this.getColor(x,y+epStep) - }); - enpassantMove.appear[0].c = 'c'; - moves.push(enpassantMove); - } - - return moves; + this.castleFlags = flags[0]; + this.pawnFlags = flags[1]; } - getCastleMoves([x,y]) + canTake([x1,y1], [x2,y2]) { - const c = this.getColor(x,y); - if (x != (c=="w" ? 7 : 0) || y != this.INIT_COL_KING[c]) - return []; //x isn't first rank, or king has moved (shortcut) - - const V = VariantRules; + const color1 = this.getColor(x1,y1); + const color2 = this.getColor(x2,y2); + // Checkered aren't captured + return color1 != color2 && color2 != 'c' && (color1 != 'c' || color2 != this.turn); + } - // Castling ? - const oppCol = this.getOppCol(c); + // Post-processing: apply "checkerization" of standard moves + getPotentialMovesFrom([x,y]) + { + let standardMoves = super.getPotentialMovesFrom([x,y]); + const lastRank = this.turn == "w" ? 0 : 7; + if (this.getPiece(x,y) == V.KING) + return standardMoves; //king has to be treated differently (for castles) let moves = []; - let i = 0; - const finalSquares = [ [2,3], [6,5] ]; //king, then rook - castlingCheck: - for (let castleSide=0; castleSide < 2; castleSide++) //large, then small - { - if (!this.flags[0][c][castleSide]) - continue; - // If this code is reached, rooks and king are on initial position - - // Nothing on the path of the king (and no checks; OK also if y==finalSquare)? - let step = finalSquares[castleSide][0] < y ? -1 : 1; - for (i=y; i!=finalSquares[castleSide][0]; i+=step) + standardMoves.forEach(m => { + if (m.vanish[0].p == V.PAWN) { - if (this.isAttacked([x,i], oppCol) || (this.board[x][i] != V.EMPTY && - // NOTE: next check is enough, because of chessboard constraints - (this.getColor(x,i) != c || ![V.KING,V.ROOK].includes(this.getPiece(x,i))))) + if (Math.abs(m.end.x-m.start.x)==2 && !this.pawnFlags[this.turn][m.start.y]) + return; //skip forbidden 2-squares jumps + if (this.board[m.end.x][m.end.y] == V.EMPTY && m.vanish.length==2 + && this.getColor(m.start.x,m.start.y) == 'c') { - continue castlingCheck; + return; //checkered pawns cannot take en-passant } } - - // Nothing on the path to the rook? - step = castleSide == 0 ? -1 : 1; - for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) - { - if (this.board[x][i] != V.EMPTY) - continue castlingCheck; - } - const rookPos = this.INIT_COL_ROOK[c][castleSide]; - - // Nothing on final squares, except maybe king and castling rook? - for (i=0; i<2; i++) + if (m.vanish.length == 1) + moves.push(m); //no capture + else { - if (this.board[x][finalSquares[castleSide][i]] != V.EMPTY && - this.getPiece(x,finalSquares[castleSide][i]) != V.KING && - finalSquares[castleSide][i] != rookPos) + // A capture occured (m.vanish.length == 2) + m.appear[0].c = "c"; + moves.push(m); + if (m.appear[0].p != m.vanish[1].p //avoid promotions (already treated): + && (m.vanish[0].p != V.PAWN || m.end.x != lastRank)) { - continue castlingCheck; + // Add transformation into captured piece + let m2 = JSON.parse(JSON.stringify(m)); + m2.appear[0].p = m.vanish[1].p; + moves.push(m2); } } - - // If this code is reached, castle is valid - moves.push( new Move({ - appear: [ - new PiPo({x:x,y:finalSquares[castleSide][0],p:V.KING,c:c}), - new PiPo({x:x,y:finalSquares[castleSide][1],p:V.ROOK,c:c})], - vanish: [ - new PiPo({x:x,y:y,p:V.KING,c:c}), - new PiPo({x:x,y:rookPos,p:V.ROOK,c:c})], - end: Math.abs(y - rookPos) <= 2 - ? {x:x, y:rookPos} - : {x:x, y:y + 2 * (castleSide==0 ? -1 : 1)} - }) ); - } - + }); return moves; } canIplay(side, [x,y]) { - return ((side=='w' && this.moves.length%2==0) || (side=='b' && this.moves.length%2==1)) - && [side,'c'].includes(this.getColor(x,y)); + return (side == this.turn && [side,'c'].includes(this.getColor(x,y))); } // Does m2 un-do m1 ? (to disallow undoing checkered moves) @@ -274,7 +161,7 @@ class CheckeredRules extends ChessRules { for (let i of [-1,1]) { - if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==VariantRules.PAWN + if (y+i>=0 && y+i<8 && this.getPiece(x+pawnShift,y+i)==V.PAWN && this.getColor(x+pawnShift,y+i)==c) { return true; @@ -299,7 +186,8 @@ class CheckeredRules extends ChessRules this.play(move); const color = this.turn; this.moves.push(move); //artifically change turn, for checkered pawns (TODO) - const kingAttacked = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']); + const kingAttacked = this.isAttacked( + this.kingPos[color], [this.getOppCol(color),'c']); let res = kingAttacked ? [ JSON.parse(JSON.stringify(this.kingPos[color])) ] //need to duplicate! : [ ]; @@ -310,68 +198,38 @@ class CheckeredRules extends ChessRules updateVariables(move) { - const piece = this.getPiece(move.start.x,move.start.y); - const c = this.getColor(move.start.x,move.start.y); - - if (c != 'c') //checkered not concerned by castle flags - { - const firstRank = (c == "w" ? 7 : 0); - // Update king position + flags - if (piece == VariantRules.KING && move.appear.length > 0) - { - this.kingPos[c][0] = move.appear[0].x; - this.kingPos[c][1] = move.appear[0].y; - this.flags[0][c] = [false,false]; - return; - } - const oppCol = this.getOppCol(c); - const oppFirstRank = 7 - firstRank; - 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[0][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[0][oppCol][flagIdx] = false; - } - } - - // Does it turn off a 2-squares pawn flag? + super.updateVariables(move); + // Does this move turn off a 2-squares pawn flag? const secondRank = [1,6]; - if (secondRank.includes(move.start.x) && move.vanish[0].p == VariantRules.PAWN) - this.flags[1][move.start.x==6 ? "w" : "b"][move.start.y] = false; + if (secondRank.includes(move.start.x) && move.vanish[0].p == V.PAWN) + this.pawnFlags[move.start.x==6 ? "w" : "b"][move.start.y] = false; } checkGameEnd() { const color = this.turn; - if (!this.isAttacked(this.kingPos[color], this.getOppCol(color)) - && !this.isAttacked(this.kingPos[color], 'c')) - { - return "1/2"; - } - // OK, checkmate - return color == "w" ? "0-1" : "1-0"; + // Artifically change turn, for checkered pawns + this.turn = this.getOppCol(this.turn); + const res = this.isAttacked(this.kingPos[color], [this.getOppCol(color),'c']) + ? (color == "w" ? "0-1" : "1-0") + : "1/2"; + this.turn = this.getOppCol(this.turn); + return res; } evalPosition() { - const [sizeX,sizeY] = VariantRules.size; let evaluation = 0; //Just count material for now, considering checkered neutral (...) - for (let i=0; i 0 && piece != move.appear[0].p) //promotion + notation += "=" + move.appear[0].p.toUpperCase(); + } return notation; }