X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=public%2Fjavascripts%2Fvariants%2FUltima.js;h=ab12c716f4f51944c3075040b45c948316a32a8b;hb=2316f8b8248589effc756d5c09c73a1acb431574;hp=18afe40268efffe4c38a9c018d4664cc0bbb4ab8;hpb=7688bf7781d10bdf7db2ff558a35852bebcaae53;p=vchess.git diff --git a/public/javascripts/variants/Ultima.js b/public/javascripts/variants/Ultima.js index 18afe402..ab12c716 100644 --- a/public/javascripts/variants/Ultima.js +++ b/public/javascripts/variants/Ultima.js @@ -50,16 +50,16 @@ class UltimaRules extends ChessRules // - a "bishop" is a chameleon, capturing as its prey // - a "queen" is a withdrawer, capturing by moving away from pieces - getPotentialMovesFrom([x,y]) + // Is piece on square (x,y) immobilized? + isImmobilized([x,y]) { - // Pre-check: is thing on this square immobilized? - // In this case add potential suicide as a move "taking the immobilizer" const piece = this.getPiece(x,y); const color = this.getColor(x,y); const oppCol = this.getOppCol(color); const V = VariantRules; const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); const [sizeX,sizeY] = V.size; + outerLoop: for (let step of adjacentSteps) { const [i,j] = [x+step[0],y+step[1]]; @@ -67,18 +67,36 @@ class UltimaRules extends ChessRules && this.getColor(i,j) == oppCol) { const oppPiece = this.getPiece(i,j); - if (oppPiece == V.IMMOBILIZER - || (oppPiece == V.BISHOP && piece == V.IMMOBILIZER)) + if (oppPiece == V.IMMOBILIZER) { - return [ new Move({ - appear: [], - vanish: [{x:x,y:y,p:piece,c:color}], - start: {x:x,y:y}, - end: {x:i,y:j} - }) ]; + // Moving is impossible only if this immobilizer is not neutralized + for (let step2 of adjacentSteps) + { + const [i2,j2] = [i+step2[0],j+step2[1]]; + if (i2 == x && j2 == y) + continue; //skip initial piece! + if (i2>=0 && i2=0 && j2 { + 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 (sq2[0]>=0 && sq2[0]=0 && sq2[1] { - if (m - }); + this.addPawnCaptures(moves); + return moves; } - // Coordinator - getPotentialRookMoves(sq) + addRookCaptures(moves, byChameleon) { - const color = this.getColor(sq); + const color = this.turn; 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])]; + 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] != VariantRules.EMPTY && this.getColor(i,j) == oppCol) { - m.vanish.push( new PiPo({ - x:i, - y:j, - p:this.getPiece(i,j), - c:oppCol - }) ); + const piece = this.getPiece(i,j); + if (!byChameleon || piece == VariantRules.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 - getPotentialKnightMoves([x,y]) + getKnightCaptures(startSquare, byChameleon) { - 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); + 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]]; @@ -174,44 +242,115 @@ class UltimaRules extends ChessRules i += step[0]; j += step[1]; } - if (i<0 && i>=sizeX || j<0 || j>=sizeY || this.getColor(i,j)==color) + if (i<0 || i>=sizeX || j<0 || j>=sizeY || this.getColor(i,j)==color + || (!!byChameleon && this.getPiece(i,j)!=V.KNIGHT)) + { 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........ + } + // 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 (cur[0]>=0 && cur[0]=0 && cur[1] { + const key = m.end.x + sizeX * 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 V = VariantRules; const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); let capturingDirections = []; - const color = this.getColor(x,y); + const color = this.turn; const oppCol = this.getOppCol(color); + const [sizeX,sizeY] = V.size; adjacentSteps.forEach(step => { const [i,j] = [x+step[0],y+step[1]]; - if (this.board[i][j] != V.EMPTY && this.getColor(i,j) == oppCol) + if (i>=0 && i=0 && j { 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 + // NOTE: includes() and even _.isEqual() functions fail... // TODO: this test should be done only once per direction - if (capturingDirection.some(dir => _.isEqual(dir, step))) + 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({ @@ -224,8 +363,16 @@ class UltimaRules extends ChessRules }); } + getPotentialQueenMoves(sq) + { + let moves = super.getPotentialQueenMoves(sq); + this.addQueenCaptures(moves); + return moves; + } + getPotentialImmobilizerMoves(sq) { + // Immobilizer doesn't capture return super.getPotentialQueenMoves(sq); } @@ -240,38 +387,177 @@ class UltimaRules extends ChessRules isAttackedByPawn([x,y], colors) { - // Square (x,y) must be surrounded by two enemy pieces, - // and one of them at least should be a pawn + // 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 = VariantRules.steps[VariantRules.ROOK]; + const [sizeX,sizeY] = VariantRules.size; + 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 (i1>=0 && i1=0 && i2=0 && j1=0 && j2=0 && i3=0 && j3=0 && i3=0 && j3=0 && i0=0 && j0=0 && i=0 && j=0 && i=0 && j=0 && i=0 && j call the appropriate isAttackedBy... (exception of immobilizers) - // Other exception: a chameleon cannot attack a chameleon (seemingly...) + // We cheat a little here: since this function is used exclusively for king, + // it's enough to check the immediate surrounding of the square. + const V = VariantRules; + const adjacentSteps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); + const [sizeX,sizeY] = V.size; + for (let step of adjacentSteps) + { + const [i,j] = [x+step[0],y+step[1]]; + if (i>=0 && i=0 && j=0 && sq2[0]=0 && sq2[1] 0) @@ -352,4 +638,25 @@ class UltimaRules extends ChessRules { return "0000"; //TODO: or "-" ? } + + getNotation(move) + { + const initialSquare = + String.fromCharCode(97 + move.start.y) + (VariantRules.size[0]-move.start.x); + const finalSquare = + String.fromCharCode(97 + move.end.y) + (VariantRules.size[0]-move.end.x); + let notation = undefined; + if (move.appear[0].p == VariantRules.PAWN) + { + // Pawn: generally ambiguous short notation, so we use full description + notation = "P" + initialSquare + finalSquare; + } + else if (move.appear[0].p == VariantRules.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 != VariantRules.KING) + notation += "X"; //capture mark (not describing what is captured...) + return notation; + } }