X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=public%2Fjavascripts%2Fvariants%2FUltima.js;fp=public%2Fjavascripts%2Fvariants%2FUltima.js;h=0000000000000000000000000000000000000000;hb=26c1e3bd4d3e9fb7c86e25c0f423bea57b977111;hp=0b00c262a07602f49d65bbdb54c6bc4e817c728d;hpb=f6dbe8e31a3260487664f1e0b50710b3f3efaf5f;p=vchess.git diff --git a/public/javascripts/variants/Ultima.js b/public/javascripts/variants/Ultima.js deleted file mode 100644 index 0b00c262..00000000 --- a/public/javascripts/variants/Ultima.js +++ /dev/null @@ -1,630 +0,0 @@ -class UltimaRules extends ChessRules -{ - static get HasFlags() { return false; } - - static get HasEnpassant() { return false; } - - static getPpath(b) - { - if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1) - return "Ultima/" + b; - return b; //usual piece - } - - static get PIECES() - { - return ChessRules.PIECES.concat([V.IMMOBILIZER]); - } - - // No castling, but checks, so keep track of kings - setOtherVariables(fen) - { - this.kingPos = {'w':[-1,-1], 'b':[-1,-1]}; - const fenParts = fen.split(" "); - const position = fenParts[0].split("/"); - for (let i=0; i { - if (!!byChameleon && m.start.x!=m.end.x && m.start.y!=m.end.y) - return; //chameleon not moving as pawn - // Try capturing in every direction - for (let step of steps) - { - const sq2 = [m.end.x+2*step[0],m.end.y+2*step[1]]; - if (V.OnBoard(sq2[0],sq2[1]) && this.board[sq2[0]][sq2[1]] != V.EMPTY - && this.getColor(sq2[0],sq2[1]) == color) - { - // Potential capture - const sq1 = [m.end.x+step[0],m.end.y+step[1]]; - if (this.board[sq1[0]][sq1[1]] != V.EMPTY - && this.getColor(sq1[0],sq1[1]) == oppCol) - { - const piece1 = this.getPiece(sq1[0],sq1[1]); - if (!byChameleon || piece1 == V.PAWN) - { - m.vanish.push(new PiPo({ - x:sq1[0], - y:sq1[1], - c:oppCol, - p:piece1 - })); - } - } - } - } - }); - } - - // "Pincher" - getPotentialPawnMoves([x,y]) - { - let moves = super.getPotentialRookMoves([x,y]); - this.addPawnCaptures(moves); - return moves; - } - - addRookCaptures(moves, byChameleon) - { - const color = this.turn; - const oppCol = this.getOppCol(color); - const kp = this.kingPos[color]; - moves.forEach(m => { - // Check piece-king rectangle (if any) corners for enemy pieces - if (m.end.x == kp[0] || m.end.y == kp[1]) - return; //"flat rectangle" - const corner1 = [m.end.x, kp[1]]; - const corner2 = [kp[0], m.end.y]; - for (let [i,j] of [corner1,corner2]) - { - if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol) - { - const piece = this.getPiece(i,j); - if (!byChameleon || piece == V.ROOK) - { - m.vanish.push( new PiPo({ - x:i, - y:j, - p:piece, - c:oppCol - }) ); - } - } - } - }); - } - - // Coordinator - getPotentialRookMoves(sq) - { - let moves = super.getPotentialQueenMoves(sq); - this.addRookCaptures(moves); - return moves; - } - - // Long-leaper - getKnightCaptures(startSquare, byChameleon) - { - // Look in every direction for captures - const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); - const color = this.turn; - const oppCol = this.getOppCol(color); - let moves = []; - const [x,y] = [startSquare[0],startSquare[1]]; - const piece = this.getPiece(x,y); //might be a chameleon! - outerLoop: - for (let step of steps) - { - let [i,j] = [x+step[0], y+step[1]]; - while (V.OnBoard(i,j) && this.board[i][j]==V.EMPTY) - { - i += step[0]; - j += step[1]; - } - if (!V.OnBoard(i,j) || this.getColor(i,j)==color - || (!!byChameleon && this.getPiece(i,j)!=V.KNIGHT)) - { - continue; - } - // last(thing), cur(thing) : stop if "cur" is our color, or beyond board limits, - // or if "last" isn't empty and cur neither. Otherwise, if cur is empty then - // add move until cur square; if cur is occupied then stop if !!byChameleon and - // the square not occupied by a leaper. - let last = [i,j]; - let cur = [i+step[0],j+step[1]]; - let vanished = [ new PiPo({x:x,y:y,c:color,p:piece}) ]; - while (V.OnBoard(cur[0],cur[1])) - { - if (this.board[last[0]][last[1]] != V.EMPTY) - { - const oppPiece = this.getPiece(last[0],last[1]); - if (!!byChameleon && oppPiece != V.KNIGHT) - continue outerLoop; - // Something to eat: - vanished.push( new PiPo({x:last[0],y:last[1],c:oppCol,p:oppPiece}) ); - } - if (this.board[cur[0]][cur[1]] != V.EMPTY) - { - if (this.getColor(cur[0],cur[1]) == color - || this.board[last[0]][last[1]] != V.EMPTY) //TODO: redundant test - { - continue outerLoop; - } - } - else - { - moves.push(new Move({ - appear: [ new PiPo({x:cur[0],y:cur[1],c:color,p:piece}) ], - vanish: JSON.parse(JSON.stringify(vanished)), //TODO: required? - start: {x:x,y:y}, - end: {x:cur[0],y:cur[1]} - })); - } - last = [last[0]+step[0],last[1]+step[1]]; - cur = [cur[0]+step[0],cur[1]+step[1]]; - } - } - return moves; - } - - // Long-leaper - getPotentialKnightMoves(sq) - { - return super.getPotentialQueenMoves(sq).concat(this.getKnightCaptures(sq)); - } - - getPotentialBishopMoves([x,y]) - { - let moves = super.getPotentialQueenMoves([x,y]) - .concat(this.getKnightCaptures([x,y],"asChameleon")); - // No "king capture" because king cannot remain under check - this.addPawnCaptures(moves, "asChameleon"); - this.addRookCaptures(moves, "asChameleon"); - this.addQueenCaptures(moves, "asChameleon"); - // Post-processing: merge similar moves, concatenating vanish arrays - let mergedMoves = {}; - moves.forEach(m => { - const key = m.end.x + V.size.x * m.end.y; - if (!mergedMoves[key]) - mergedMoves[key] = m; - else - { - for (let i=1; i { moves.push(mergedMoves[k]); }); - return moves; - } - - // Withdrawer - addQueenCaptures(moves, byChameleon) - { - if (moves.length == 0) - return; - const [x,y] = [moves[0].start.x,moves[0].start.y]; - const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); - let capturingDirections = []; - const color = this.turn; - const oppCol = this.getOppCol(color); - adjacentSteps.forEach(step => { - const [i,j] = [x+step[0],y+step[1]]; - if (V.OnBoard(i,j) && this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol - && (!byChameleon || this.getPiece(i,j) == V.QUEEN)) - { - capturingDirections.push(step); - } - }); - moves.forEach(m => { - const step = [ - m.end.x!=x ? (m.end.x-x)/Math.abs(m.end.x-x) : 0, - m.end.y!=y ? (m.end.y-y)/Math.abs(m.end.y-y) : 0 - ]; - // NOTE: includes() and even _.isEqual() functions fail... - // TODO: this test should be done only once per direction - if (capturingDirections.some(dir => - { return (dir[0]==-step[0] && dir[1]==-step[1]); })) - { - const [i,j] = [x-step[0],y-step[1]]; - m.vanish.push(new PiPo({ - x:i, - y:j, - p:this.getPiece(i,j), - c:oppCol - })); - } - }); - } - - getPotentialQueenMoves(sq) - { - let moves = super.getPotentialQueenMoves(sq); - this.addQueenCaptures(moves); - return moves; - } - - getPotentialImmobilizerMoves(sq) - { - // Immobilizer doesn't capture - return super.getPotentialQueenMoves(sq); - } - - getPotentialKingMoves(sq) - { - return this.getSlideNJumpMoves(sq, - V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep"); - } - - // isAttacked() is OK because the immobilizer doesn't take - - isAttackedByPawn([x,y], colors) - { - // Square (x,y) must be surroundable by two enemy pieces, - // and one of them at least should be a pawn (moving). - const dirs = [ [1,0],[0,1] ]; - const steps = V.steps[V.ROOK]; - for (let dir of dirs) - { - const [i1,j1] = [x-dir[0],y-dir[1]]; //"before" - const [i2,j2] = [x+dir[0],y+dir[1]]; //"after" - if (V.OnBoard(i1,j1) && V.OnBoard(i2,j2)) - { - if ((this.board[i1][j1]!=V.EMPTY && colors.includes(this.getColor(i1,j1)) - && this.board[i2][j2]==V.EMPTY) - || - (this.board[i2][j2]!=V.EMPTY && colors.includes(this.getColor(i2,j2)) - && this.board[i1][j1]==V.EMPTY)) - { - // Search a movable enemy pawn landing on the empty square - for (let step of steps) - { - let [ii,jj] = (this.board[i1][j1]==V.EMPTY ? [i1,j1] : [i2,j2]); - let [i3,j3] = [ii+step[0],jj+step[1]]; - while (V.OnBoard(i3,j3) && this.board[i3][j3]==V.EMPTY) - { - i3 += step[0]; - j3 += step[1]; - } - if (V.OnBoard(i3,j3) && colors.includes(this.getColor(i3,j3)) - && this.getPiece(i3,j3) == V.PAWN && !this.isImmobilized([i3,j3])) - { - return true; - } - } - } - } - } - return false; - } - - isAttackedByRook([x,y], colors) - { - // King must be on same column or row, - // and a rook should be able to reach a capturing square - // colors contains only one element, giving the oppCol and thus king position - const sameRow = (x == this.kingPos[colors[0]][0]); - const sameColumn = (y == this.kingPos[colors[0]][1]); - if (sameRow || sameColumn) - { - // Look for the enemy rook (maximum 1) - for (let i=0; i 0) - { - this.kingPos[c][0] = move.appear[0].x; - this.kingPos[c][1] = move.appear[0].y; - } - } - - static get VALUES() - { - // TODO: totally experimental! - return { - 'p': 1, - 'r': 2, - 'n': 5, - 'b': 3, - 'q': 3, - 'm': 5, - 'k': 1000 - }; - } - - static get SEARCH_DEPTH() { return 2; } //TODO? - - static GenRandInitFen() - { - let pieces = { "w": new Array(8), "b": new Array(8) }; - // Shuffle pieces on first and last rank - for (let c of ["w","b"]) - { - let positions = _.range(8); - // Get random squares for every piece, totally freely - - let randIndex = _.random(7); - const bishop1Pos = positions[randIndex]; - positions.splice(randIndex, 1); - - randIndex = _.random(6); - const bishop2Pos = positions[randIndex]; - positions.splice(randIndex, 1); - - randIndex = _.random(5); - const knight1Pos = positions[randIndex]; - positions.splice(randIndex, 1); - - randIndex = _.random(4); - const knight2Pos = positions[randIndex]; - positions.splice(randIndex, 1); - - randIndex = _.random(3); - const queenPos = positions[randIndex]; - positions.splice(randIndex, 1); - - randIndex = _.random(2); - const kingPos = positions[randIndex]; - positions.splice(randIndex, 1); - - randIndex = _.random(1); - const rookPos = positions[randIndex]; - positions.splice(randIndex, 1); - const immobilizerPos = positions[0]; - - pieces[c][bishop1Pos] = 'b'; - pieces[c][bishop2Pos] = 'b'; - pieces[c][knight1Pos] = 'n'; - pieces[c][knight2Pos] = 'n'; - pieces[c][queenPos] = 'q'; - pieces[c][kingPos] = 'k'; - pieces[c][rookPos] = 'r'; - pieces[c][immobilizerPos] = 'm'; - } - return pieces["b"].join("") + - "/pppppppp/8/8/8/8/PPPPPPPP/" + - pieces["w"].join("").toUpperCase() + - " w"; - } - - getNotation(move) - { - const initialSquare = V.CoordsToSquare(move.start); - const finalSquare = V.CoordsToSquare(move.end); - let notation = undefined; - if (move.appear[0].p == V.PAWN) - { - // Pawn: generally ambiguous short notation, so we use full description - notation = "P" + initialSquare + finalSquare; - } - else if (move.appear[0].p == V.KING) - notation = "K" + (move.vanish.length>1 ? "x" : "") + finalSquare; - else - notation = move.appear[0].p.toUpperCase() + finalSquare; - if (move.vanish.length > 1 && move.appear[0].p != V.KING) - notation += "X"; //capture mark (not describing what is captured...) - return notation; - } -} - -const VariantRules = UltimaRules;