X-Git-Url: https://git.auder.net/img/rock_paper_scissors_lizard_spock.gif?a=blobdiff_plain;f=public%2Fjavascripts%2Fvariants%2FUltima.js;h=18afe40268efffe4c38a9c018d4664cc0bbb4ab8;hb=7688bf7781d10bdf7db2ff558a35852bebcaae53;hp=812d440ba3a5b5d811ea7024b4ff21c19c0064a5;hpb=32cfcea44bf00b0c6c4d172cca715823076ff490;p=vchess.git diff --git a/public/javascripts/variants/Ultima.js b/public/javascripts/variants/Ultima.js index 812d440b..18afe402 100644 --- a/public/javascripts/variants/Ultima.js +++ b/public/javascripts/variants/Ultima.js @@ -1,8 +1,355 @@ class UltimaRules extends ChessRules { - // TODO: think about move UI for "removing an immobilized piece from the board" - // (extend game.js and feedback Rules.js with "there was a click, is it a move?") + static getPpath(b) + { + if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1) + return "Ultima/" + b; + return b; //usual piece + } - // TODO: Keep usual pieces names here (but comment with Ultima pieces names) - // Just change moving + capturing modes. + initVariables(fen) + { + this.kingPos = {'w':[-1,-1], 'b':[-1,-1]}; + const fenParts = fen.split(" "); + const position = fenParts[0].split("/"); + for (let i=0; i=0 && i=0 && j=0 && i=0 && j=0 && i=0 + && j { + if (m + }); + } + + // Coordinator + getPotentialRookMoves(sq) + { + const color = this.getColor(sq); + const oppCol = this.getOppCol(color); + const kp = this.kingPos[color]; + let moves = super.getPotentialQueenMoves(sq); + 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 = [Math.max(m.end.x,kp[0]), Math.min(m.end.y,kp[1])]; + const corner2 = [Math.min(m.end.x,kp[0]), Math.max(m.end.y,kp[1])]; + for (let [i,j] of [corner1,corner2]) + { + if (this.board[i][j] != VariantRules.EMPTY && this.getColor(i,j) == oppCol) + { + m.vanish.push( new PiPo({ + x:i, + y:j, + p:this.getPiece(i,j), + c:oppCol + }) ); + } + } + }); + return moves; + } + + // Long-leaper + getPotentialKnightMoves([x,y]) + { + let moves = super.getPotentialQueenMoves(sq); + // Look in every direction for captures + const V = VariantRules; + const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); + const [sizeX,sizeY] = V.size; + const color = this.getColor(x,y); + for (let step of steps) + { + let [i,j] = [x+step[0], y+step[1]]; + while (i>=0 && i=0 && j=sizeX || j<0 || j>=sizeY || this.getColor(i,j)==color) + continue; + // Found an enemy piece: potential capture (if empty space behind) + // So, while we find enemy pieces + space in this direction, add captures! + i += step[0]; + j += step[1]; + while ( ) //TODO: finish........ + } + return moves; + } + + getPotentialBishopMoves(sq) + { + return super.getPotentialQueenMoves(sq); + // TODO: add captures of coordinators,pinchers,withdrawers... by re-using code + } + + getPotentialQueenMoves([x,y]) + { + let moves = super.getPotentialQueenMoves(sq); + const V = VariantRules; + const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); + let capturingDirections = []; + const color = this.getColor(x,y); + const oppCol = this.getOppCol(color); + adjacentSteps.forEach(step => { + const [i,j] = [x+step[0],y+step[1]]; + if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol) + 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() function does not work on complex array elements + // TODO: this test should be done only once per direction + if (capturingDirection.some(dir => _.isEqual(dir, step))) + { + 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 + })); + } + }); + } + + getPotentialImmobilizerMoves(sq) + { + return super.getPotentialQueenMoves(sq); + } + + getPotentialKingMoves(sq) + { + const V = VariantRules; + 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 surrounded by two enemy pieces, + // and one of them at least should be a pawn + return false; + } + + isAttackedByRook(sq, colors) + { + // Enemy king must be on same file and a rook on same row (or reverse) + } + + isAttackedByKnight(sq, colors) + { + // Square (x,y) must be on same line as a knight, + // and there must be empty square(s) behind. + } + + isAttackedByBishop(sq, colors) + { + // switch on piece nature on square sq: a chameleon attack as this piece + // ==> call the appropriate isAttackedBy... (exception of immobilizers) + // Other exception: a chameleon cannot attack a chameleon (seemingly...) + } + + isAttackedByQueen(sq, colors) + { + // Square (x,y) must be adjacent to a queen, and the queen must have + // some free space in the opposite direction from (x,y) + } + + updateVariables(move) + { + // Just update king position + const piece = this.getPiece(move.start.x,move.start.y); + const c = this.getColor(move.start.x,move.start.y); + if (piece == VariantRules.KING && move.appear.length > 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() + + " 0000"; //TODO: flags?! + } + + getFlagsFen() + { + return "0000"; //TODO: or "-" ? + } }