X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FChakart.js;h=a2106e48267f5f869cb70b73d131830b3f6eb52f;hb=82820616dd6f08587a6f53bdb5a1377f73335f10;hp=43bef4b25298bb7b8afae5c2cad949376adb0739;hpb=5d75c82c70bcd4bcc43b65571231f0ba1b532b79;p=vchess.git diff --git a/client/src/variants/Chakart.js b/client/src/variants/Chakart.js index 43bef4b2..a2106e48 100644 --- a/client/src/variants/Chakart.js +++ b/client/src/variants/Chakart.js @@ -1,4 +1,4 @@ -import { ChessRules } from "@/base_rules"; +import { ChessRules, Move, PiPo } from "@/base_rules"; import { SuicideRules } from "@/variants/Suicide"; export class ChakartRules extends ChessRules { @@ -10,12 +10,6 @@ export class ChakartRules extends ChessRules { return false; } - static get HasEnpassant() { - // TODO: maybe enable them later, but then the capturing pawn take the - // mushroom and continue diagonally?! - return false; - } - static get CorrConfirm() { // Because of bonus effects return false; @@ -26,26 +20,20 @@ export class ChakartRules extends ChessRules { } hoverHighlight(x, y) { - if ( - this.firstMove.appear.length == 0 || - this.firstMove.vanish.length == 0 || - this.board[x][y] != V.EMPTY - ) { - return false; - } - const deltaX = Math.abs(this.firstMove.end.x - x); - const deltaY = Math.abs(this.firstMove.end.y - y); + if (this.subTurn == 1) return false; + const L = this.firstMove.length; + const fm = this.firstMove[L-1]; + if (fm.end.effect != 0) return false; + const deltaX = Math.abs(fm.end.x - x); + const deltaY = Math.abs(fm.end.y - y); return ( - this.subTurn == 2 && - // Condition: rook or bishop move, may capture, but no bonus move - [V.ROOK, V.BISHOP].includes(this.firstMove.vanish[0].p) && - ( - this.firstMove.vanish.length == 1 || - ['w', 'b'].includes(this.firstMove.vanish[1].c) - ) && + (deltaX == 0 && deltaY == 0) || ( - this.firstMove.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1 || - this.firstMove.vanish[0].p == V.BISHOP && deltaX + deltaY == 1 + this.board[x][y] == V.EMPTY && + ( + (fm.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1) || + (fm.vanish[0].p == V.BISHOP && deltaX + deltaY == 1) + ) ) ); } @@ -115,7 +103,7 @@ export class ChakartRules extends ChessRules { const fenParts = fen.split(" "); return Object.assign( ChessRules.ParseFen(fen), - { captured: fenParts[4] } + { captured: fenParts[5] } ); } @@ -149,13 +137,16 @@ export class ChakartRules extends ChessRules { } setFlags(fenflags) { + // King can send shell? Queen can be invisible? this.powerFlags = { - w: [...Array(2)], //king can send shell? Queen can be invisible? - b: [...Array(2)] + w: [{ 'k': false, 'q': false }], + b: [{ 'k': false, 'q': false }] }; for (let c of ["w", "b"]) { - for (let i = 0; i < 2; i++) - this.pawnFlags[c][i] = fenFlags.charAt((c == "w" ? 0 : 2) + i) == "1"; + for (let p of ['k', 'q']) { + this.powerFlags[c][p] = + fenFlags.charAt((c == "w" ? 0 : 2) + (p == 'k' ? 0 : 1)) == "1"; + } } } @@ -205,6 +196,7 @@ export class ChakartRules extends ChessRules { [V.PAWN]: parseInt(fenParsed.captured[9]), } }; + this.firstMove = []; this.subTurn = 1; } @@ -212,18 +204,93 @@ export class ChakartRules extends ChessRules { let fen = ""; // Add power flags for (let c of ["w", "b"]) - for (let i = 0; i < 2; i++) fen += (this.powerFlags[c][i] ? "1" : "0"); + for (let p of ['k', 'q']) fen += (this.powerFlags[c][p] ? "1" : "0"); return fen; } - getPotentialMovesFrom(sq) { - if (this.subTurn == 1) return super.getPotentialMovesFrom(sq); + static get RESERVE_PIECES() { + return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN]; + } + + getReserveMoves([x, y]) { + const color = this.turn; + const p = V.RESERVE_PIECES[y]; + if (this.reserve[color][p] == 0) return []; + let moves = []; + const start = (color == 'w' && p == V.PAWN ? 1 : 0); + const end = (color == 'b' && p == V.PAWN ? 7 : 8); + for (let i = start; i < end; i++) { + for (let j = 0; j < V.size.y; j++) { + if (this.board[i][j] == V.EMPTY) { + let mv = new Move({ + appear: [ + new PiPo({ + x: i, + y: j, + c: color, + p: p + }) + ], + vanish: [], + start: { x: x, y: y }, //a bit artificial... + end: { x: i, y: j } + }); + moves.push(mv); + } + } + } + return moves; + } + + getPotentialMovesFrom([x, y]) { + if (this.subTurn == 1) return super.getPotentialMovesFrom([x, y]); if (this.subTurn == 2) { - // TODO: coup compatible avec firstMove + let moves = []; + const L = this.firstMove.length; + const fm = this.firstMove[L-1]; + switch (fm.end.effect) { + // case 0: a click is required (banana or bomb) + case 1: + // Exchange position with any piece + for (let i=0; i<8; i++) { + for (let j=0; j<8; j++) { + const colIJ = this.getColor(i, j); + if ( + i != x && + j != y && + this.board[i][j] != V.EMPTY && + colIJ != 'a' + ) { + const movedUnit = new PiPo({ + x: x, + y: y, + c: colIJ, + p: this.getPiece(i, j) + }); + let mMove = this.getBasicMove([x, y], [i, j]); + mMove.appear.push(movedUnit); + moves.push(mMove); + } + } + } + break; + case 2: + // Resurrect a captured piece + if (x >= V.size.x) moves = this.getReserveMoves([x, y]); + break; + case 3: + // Play again with the same piece + if (fm.end.x == x && fm.end.y == y) + moves = super.getPotentialMovesFrom([x, y]); + break; + } + return moves; } } - getBasicMove([x1, y1], [x2, y2]) { + getBasicMove([x1, y1], [x2, y2], tr) { + // TODO: if this.subTurn == 2 :: no mushroom effect + // (first, transformation. then:) // Apply mushroom, bomb or banana effect (hidden to the player). // Determine egg effect, too, and apply its first part if possible. // add egg + add mushroom for pawns. @@ -233,14 +300,74 @@ export class ChakartRules extends ChessRules { // Infer move type based on its effects (used to decide subTurn 1 --> 2) // --> impossible étant donné juste first part (egg --> effect?) // => stocker l'effet (i, ii ou iii) dans le coup directement, - // Pas terrible, mais y'aura pas 36 variantes comme ça. Disons end.effect == null, 0, 1, 2 ou 3 + // Pas terrible, mais y'aura pas 36 variantes comme ça. Disons end.effect == 0, 1, 2 ou 3 // 0 => tour ou fou, pose potentielle. // If queen can be invisible, add move same start + end but final type changes + // set move.end.effect (if subTurn --> 2) + } + + getEnpassantCaptures([x, y], shiftX) { + const Lep = this.epSquares.length; + const epSquare = this.epSquares[Lep - 1]; //always at least one element + let enpassantMove = null; + if ( + !!epSquare && + epSquare.x == x + shiftX && + Math.abs(epSquare.y - y) == 1 + ) { + // Not using this.getBasicMove() because the mushroom has no effect + enpassantMove = super.getBasicMove([x, y], [epSquare.x, epSquare.y]); + enpassantMove.vanish.push({ + x: x, + y: epSquare.y, + p: V.PAWN, + c: this.getColor(x, epSquare.y) + }); + } + return !!enpassantMove ? [enpassantMove] : []; + } + + getPotentialQueenMoves(sq) { + const normalMoves = super.getPotentialQueenMoves(sq); + // If flag allows it, add 'invisible movements' + let invisibleMoves = []; + if (this.powerFlags[this.turn][V.QUEEN]) { + normalMoves.forEach(m => { + if (m.vanish.length == 1) { + let im = JSON.parse(JSON.stringify(m)); + m.appear[0].p = V.INVISIBLE_QUEEN; + invisibleMoves.push(im); + } + }); + } + return normalMoves.concat(invisibleMoves); } getPotentialKingMoves([x, y]) { let moves = super.getPotentialKingMoves([x, y]); - // TODO: if flags allows it, add 'remote shell captures' + const color = this.turn; + // If flag allows it, add 'remote shell captures' + if (this.powerFlags[this.turn][V.KING]) { + V.steps[V.ROOK].concat(V.steps[V.BISHOP]).forEach(step => { + let [i, j] = [x + 2 * step[0], y + 2 * step[1]]; + while ( + V.OnBoard(i, j) && + ( + this.board[i][j] == V.EMPTY || + ( + this.getColor(i, j) == 'a' && + [V.EGG, V.MUSHROOM].includes(this.getPiece(i, j)) + ) + ) + ) { + i += step[0]; + j += step[1]; + } + if (V.OnBoard(i, j) && this.getColor(i, j) != color) + // May just destroy a bomb or banana: + moves.push(this.getBasicMove([x, y], [i, j])); + }); + } return moves; } @@ -272,41 +399,74 @@ export class ChakartRules extends ChessRules { getAllPotentialMoves() { if (this.subTurn == 1) return super.getAllPotentialMoves(); - // TODO: subTurn == 2, switch on firstMove.end.effect --> lack firstMove, setOtherVariables, play/undo, see Dynamo + let moves = []; + const L = this.firstMove.length; + const fm = this.firstMove[L-1]; + //switch (fm.end.effect) { + // case 0: //... } doClick(square) { if (isNaN(square[0])) return null; - // TODO: If subTurn == 2: - // if square is empty && firstMove is compatible, - // complete the move (banana or bomb or piece exchange). - // if square not empty, just complete with empty move - const Lf = this.firstMove.length; - if (this.subTurn == 2) { - if ( - this.board[square[0]][square[1]] == V.EMPTY && - (La == 0 || !this.oppositeMoves(this.amoves[La-1], this.firstMove[Lf-1])) - ) { - return { - start: { x: -1, y: -1 }, - end: { x: -1, y: -1 }, - appear: [], - vanish: [] - }; - } + if (this.subTurn == 1) return null; + const L = this.firstMove.length; + const fm = this.firstMove[L-1]; + if (fm.end.effect != 0) return null; + const [x, y] = [square[0], square[1]]; + const deltaX = Math.abs(fm.end.x - x); + const deltaY = Math.abs(fm.end.y - y); + if (deltaX == 0 && deltaY == 0) { + // Empty move: + return { + start: { x: -1, y: -1 }, + end: { x: -1, y: -1 }, + appear: [], + vanish: [] + }; + } + if ( + this.board[x][y] == V.EMPTY && + ( + (fm.vanish[0].p == V.ROOK && deltaX == 1 && deltaY == 1) || + (fm.vanish[0].p == V.BISHOP && deltaX + deltaY == 1) + ) + ) { + return new Move({ + start: { x: -1, y: -1 }, + end: { x: x, y: y }, + appear: [ + new PiPo({ + x: x, + y: y, + c: 'a', + p: (fm.vanish[0].p == V.ROOK ? V.BANANA : V.BOMB) + }) + ], + vanish: [] + }); } return null; } play(move) { - // TODO -// --> pour bonus toadette, passer "capture" temporairement en "reserve" pour permettre de jouer le coup. - // il faut alors mettre à jour 'captured' - // TODO: subTurn passe à 2 si arrivée sur bonus cavalier + effect == 1, 2 ou 3 ou si coup de tour ou fou (non cumulables) + move.flags = JSON.stringify(this.aggregateFlags()); + this.epSquares.push(this.getEpSquare(move)); + V.PlayOnBoard(this.board, move); + if (move.end.effect !== undefined) { + this.firstMove.push(move); + this.subTurn = 2; + if (move.end.effect == 2) this.reserve = this.captured; + } + else { + this.turn = V.GetOppCol(this.turn); + this.subTurn = 1; + this.reserve = null; + } } postPlay(move) { - // TODO: if effect = resurect a piece, then this.reserve = this.captured; + if (move.vanish[0].p == V.KING) { } + //si roi et delta >= 2 ou dame et appear invisible queen : turn flag off if (move.vanish.length == 2 && move.vanish[1].c != 'a') // Capture: update this.captured this.captured[move.vanish[1].c][move.vanish[1].p]++; @@ -322,6 +482,8 @@ export class ChakartRules extends ChessRules { undo(move) { // TODO: should be easy once end.effect is set in getBasicMove() + if (move.end.effect !== undefined) + this.firstMove.pop(); } postUndo(move) { @@ -360,14 +522,23 @@ export class ChakartRules extends ChessRules { } getComputerMove() { + // Random mover: const moves = this.getAllValidMoves(); - // TODO: random mover - return moves[0]; + let move1 = moves[randInt(movs.length)]; + this.play(move1); + let move2 = undefined; + if (this.subTurn == 2) { + const moves2 = this.getAllValidMoves(); + move2 = moves2[randInt(moves2.length)]; + } + this.undo(move1); + if (!move2) return move1; + return [move1, move2]; } getNotation(move) { - // TODO - // invisibility used? --> move notation Q?? - return "?"; + // TODO: invisibility used => move notation Q?? + // Also, bonus should be clearly indicated + bomb/bananas locations + return super.getNotation(move); } };