X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FDynamo.js;h=01593a16773c228e4859c2471bbb31ada43dd9d3;hb=93ce6119a67b8510762dd68a07936b6f22f2ad67;hp=9d47fd8fbd72ebc5cfe89fc42e68c489f38d1be3;hpb=5e1bc6519d4c81aeac40aec7390c64c913cbf566;p=vchess.git diff --git a/client/src/variants/Dynamo.js b/client/src/variants/Dynamo.js index 9d47fd8f..01593a16 100644 --- a/client/src/variants/Dynamo.js +++ b/client/src/variants/Dynamo.js @@ -1,7 +1,7 @@ import { ChessRules, Move, PiPo } from "@/base_rules"; export class DynamoRules extends ChessRules { - // TODO: later, allow to push out pawns on a and h files? + // TODO: later, allow to push out pawns on a and h files static get HasEnpassant() { return false; } @@ -17,8 +17,7 @@ export class DynamoRules extends ChessRules { // Local stack of "action moves" this.amoves = []; const amove = V.ParseFen(fen).amove; - if (cmove == "-") this.amoves.push(null); - else { + if (amove != "-") { const amoveParts = amove.split("/"); let amove = { // No need for start & end @@ -26,7 +25,7 @@ export class DynamoRules extends ChessRules { vanish: [] }; [0, 1].map(i => { - amoveParts[0].split(".").forEach(av => { + amoveParts[i].split(".").forEach(av => { // Format is "bpe3" const xy = V.SquareToCoords(av.substr(2)); move[i == 0 ? "appear" : "vanish"].push( @@ -41,12 +40,14 @@ export class DynamoRules extends ChessRules { }); this.amoves.push(move); } + // Stack "first moves" (on subTurn 1) to merge and check opposite moves + this.firstMove = []; } static ParseFen(fen) { return Object.assign( ChessRules.ParseFen(fen), - { cmove: fen.split(" ")[4] } + { amove: fen.split(" ")[4] } ); } @@ -59,28 +60,30 @@ export class DynamoRules extends ChessRules { return true; } - getAmove(move) { - if (move.appear.length == 2 && move.vanish.length == 2) - return { appear: move.appear, vanish: move.vanish }; - return null; + getFen() { + return super.getFen() + " " + this.getAmoveFen(); } - // TODO: this.firstMove + rooks location in setOtherVariables - // only rooks location in FEN (firstMove is forgotten if quit game and come back) - doClick(square) { - // If subTurn == 2 && square is the final square of last move, - // then return an empty move - if ( - this.subTurn == 2 && - square.x == this.firstMove.end.x && - square.y == this.firstMove.end.y - ) { - return { - appear: [], - vanish: [] - }; - } - return null; + getFenForRepeat() { + return super.getFenForRepeat() + "_" + this.getAmoveFen(); + } + + getAmoveFen() { + const L = this.amoves.length; + return ( + ["appear","vanish"].map( + mpart => { + return ( + this.amoves[L-1][mpart].map( + av => { + const square = V.CoordsToSquare({ x: av.x, y: av.y }); + return av.c + av.p + square; + } + ).join(".") + ); + } + ).join("/") + ); } canTake() { @@ -88,7 +91,7 @@ export class DynamoRules extends ChessRules { return false; } - // "pa" : piece (as a square) doing this push/pull action + // "pa": piece (as a square) doing this push/pull action getActionMoves([sx, sy], [ex, ey], pa) { const color = this.getColor(sx, sy); const lastRank = (color == 'w' ? 0 : 7); @@ -101,123 +104,103 @@ export class DynamoRules extends ChessRules { moves.push(move); }); } else moves.push(super.getBasicMove([sx, sy], [ex, ey])); - const actionType = - ( - Math.abs(pa[0] - sx) < Math.abs(pa[0] - ex) || - Math.abs(pa[1] - sy) < Math.abs(pa[1] - ey) - ) - ? "push" - : "pull"; - moves.forEach(m => m.action = [{ by: pa, type: actionType }]); return moves; } - // TODO: if type is given, consider only actions of this type - getPactions(sq, color, type) { + // Actions on piece on square "sq", by color "color" + // NOTE: to push a piece out of the board, make it slide until our piece + // (doing the action, moving or not) + getPactions(sq, color) { const [x, y] = sq; let moves = []; let squares = {}; - if (!by) { - const oppCol = V.GetOppCol(color); - // Look in all directions for a "color" piece - for (let step of V.steps[V.KNIGHT]) { - const xx = x + step[0], - yy = y + step[1]; - if ( - V.OnBoard(xx, yy) && - this.getPiece(xx, yy) == V.KNIGHT && - this.getColor(xx, yy) == color - ) { - const px = x - step[0], - py = y - step[1]; - if (V.OnBoard(px, py)) { - if (this.board[px][py] == V.EMPTY) { - const hash = "s" + px + py; - if (!squares[hash]) { - squares[hash] = true; - Array.prototype.push.apply( - moves, - this.getActionMoves([x, y], [px, py], [xx, yy]) - ); - } - else { //add piece doing action - } - } - } else { - const hash = "s" + xx + yy; + const oppCol = V.GetOppCol(color); + // Look in all directions for a "color" piece + for (let step of V.steps[V.KNIGHT]) { + const xx = x + step[0], + yy = y + step[1]; + if ( + V.OnBoard(xx, yy) && + this.getPiece(xx, yy) == V.KNIGHT && + this.getColor(xx, yy) == color + ) { + const px = x - step[0], + py = y - step[1]; + if (V.OnBoard(px, py)) { + if (this.board[px][py] == V.EMPTY) { + const hash = "s" + px + py; if (!squares[hash]) { squares[hash] = true; - moves.push( - new Move({ - start: { x: x, y: y }, - end: { x: xx, y: yy }, - appear: [], - vanish: [ - new PiPo({ - x: x, - y: y, - p: this.getPiece(x, y), - c: oppCol - }) - ] - }) + Array.prototype.push.apply( + moves, + this.getActionMoves([x, y], [px, py], [xx, yy]) ); } + else { //add piece doing action + } + } + } else { + const hash = "s" + xx + yy; + if (!squares[hash]) { + squares[hash] = true; + moves.push( + new Move({ + start: { x: x, y: y }, + end: { x: xx, y: yy }, + appear: [], + vanish: [ + new PiPo({ + x: x, + y: y, + p: this.getPiece(x, y), + c: oppCol + }) + ] + }) + ); } } } - for (let step in V.steps[V.ROOK]) { - // (+ if color is ours, pawn pushes) king, rook and queen - // --> pawns special case can push from a little distance if on 2nd rank (or 1st rank) - } - for (let step in V.steps[V.BISHOP]) { - // King, bishop, queen, and possibly pawns attacks (if color is enemy) - } + } + for (let step in V.steps[V.ROOK]) { + // (+ if color is ours, pawn pushes) king, rook and queen + // --> pawns special case can push from a little distance if on 2nd rank (or 1st rank) + } + for (let step in V.steps[V.BISHOP]) { + // King, bishop, queen, and possibly pawns attacks (if color is enemy) } return moves; } - // NOTE: to push a piece out of the board, make it slide until our piece - // (doing the action, moving or not) - // TODO: for pushes, play the pushed piece first. + // NOTE: for pushes, play the pushed piece first. // for pulls: play the piece doing the action first + // If castle, then no options available next (just re-click) getPotentialMovesFrom([x, y]) { const color = this.turn; - if (this.getColor(x, y) != color) - // The only moves possible with enemy pieces are pulls and pushes: - return this.getPactions([x, y], color); - // Playing my pieces: either on their own, or pushed by another - // If subTurn == 2 then we should have a first move, - // TODO = use it to allow some type of action - if (this.subTurn == 2) { + if (this.subTurn == 1) { + // Free to play any move or action: return ( - this.moveOnSubturn1.isAnAction - ? super.getPotentialMovesFrom([x, y]) - : this.getPactions([x, y], color, TODO_arg) + super.getPotentialMovesFrom([x, y]) + .concat(this.getPactions([x, y], color)) ); } - // Both options are possible at subTurn1: normal move, or push - return ( - super.getPotentialMovesFrom([x, y]) - .concat(this.getPactions([x, y], color, "push")) - // TODO: discard moves that let the king underCheck, and no second - // move can counter check. Example: pinned queen pushes pinned pawn. - .filter(m => { - this.play(m); - const res = this.filterMoves(this.getPotentialMoves(/* TODO: args? */)).length > 0; - this.undo(m); - return res; - }) - ); - } - - // TODO: track rooks locations, should be a field in FEN, in castleflags? - // --> only useful if castleFlags is still ON - getCastleMoves(sq) { - // TODO: if rook1 isn't at its place (with castleFlags ON), set it off - // same for rook2. - let moves = super.getCastleMoves(sq); - // TODO: restore castleFlags + // If subTurn == 2 then we should have a first move, + // which restrict what we can play now. + // Case 1: an opponent's piece moved: we can only move the piece which + // did the action, in the moving direction. + // Case 2: one of our pieces moved: either by action or by itself. + // Just check if it could be a normal move. If yes, allow both. + const L = this.firstMove.length; + const fm = this.firstMove[L-1]; + if (fm.vanish[0].c != color) { + // Case 1: TODO + } + else { + // Case 2: TODO + // Use fm.start.x, fm.start.y, fm.end.x, fm.end.y, fm.vanish[0].c + // Search for the piece doing the action "pa": the action type + // is deduced from pa relative positon then. + } } // Does m2 un-do m1 ? (to disallow undoing actions) @@ -238,7 +221,6 @@ export class DynamoRules extends ChessRules { return true; }; return ( - !!m1 && m1.appear.length == 2 && m2.appear.length == 2 && m1.vanish.length == 2 && @@ -248,13 +230,51 @@ export class DynamoRules extends ChessRules { ); } + getAmove(move1, move2) { + // Just merge (one is action one is move, one may be empty) + return { + appear: move1.appear.concat(move2.appear), + vanish: move1.vanish.concat(move2.vanish) + } + } + filterValid(moves) { - if (moves.length == 0) return []; const color = this.turn; - return moves.filter(m => { - const L = this.amoves.length; //at least 1: init from FEN - return !this.oppositeMoves(this.amoves[L - 1], m); - }); + if (this.subTurn == 1) { + return moves.filter(m => { + // A move is valid either if it doesn't result in a check, + // or if a second move is possible to counter the check + // (not undoing a potential move + action of the opponent) + this.play(m); + let res = this.underCheck(color); + if (res) { + const moves2 = this.getAllPotentialMoves(); + for (m2 of moves2) { + this.play(m2); + const res2 = this.underCheck(color); + this.undo(m2); + if (!res2) { + res = false; + break; + } + } + } + this.undo(m); + return !res; + }); + } + const Lf = this.firstMove.length; + const La = this.amoves.length; + if (La == 0) return super.filterValid(moves); + return ( + super.filterValid( + moves.filter(m => { + // Move shouldn't undo another: + const amove = this.getAmove(this.firstMove[Lf-1], m); + return !this.oppositeMoves(this.amoves[La-1], amove); + }) + ) + ); } isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) { @@ -313,49 +333,57 @@ export class DynamoRules extends ChessRules { return super.getCurrentScore(); } + doClick(square) { + // If subTurn == 2 && square is the final square of last move, + // then return an empty move + const L = this.firstMove.length; + if ( + this.subTurn == 2 && + square.x == this.firstMove[L-1].end.x && + square.y == this.firstMove[L-1].end.y + ) { + return { + appear: [], + vanish: [] + }; + } + return null; + } + play(move) { move.flags = JSON.stringify(this.aggregateFlags()); V.PlayOnBoard(this.board, move); - if (this.subTurn == 1) { - // TODO: is there a second move possible? - // (if the first move is a normal one, there may be no actions available) - // --> If not, just change turn as ion the else {} section - this.subTurn = 2; - this.movesCount++; - } else { - // subTurn == 2 + if (this.subTurn == 2) { this.turn = V.GetOppCol(this.turn); - this.subTurn = 1; + this.movesCount++; } + else this.firstMove.push(move); + this.subTurn = 3 - this.subTurn; this.postPlay(move); } updateCastleFlags(move, piece) { const c = V.GetOppCol(this.turn); const firstRank = (c == "w" ? V.size.x - 1 : 0); - // Update castling flags if rooks are moved (only) - if (piece == V.KING && move.appear.length > 0) - this.castleFlags[c] = [V.size.y, V.size.y]; - else if ( - move.start.x == firstRank && - this.castleFlags[c].includes(move.start.y) - ) { - const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1); - this.castleFlags[c][flagIdx] = V.size.y; + // Update castling flags + if (piece == V.KING) this.castleFlags[c] = [V.size.y, V.size.y]; + for (let v of move.vanish) { + if (v.x == firstRank && this.castleFlags[c].includes(v.y)) { + const flagIdx = (v.y == this.castleFlags[c][0] ? 0 : 1); + this.castleFlags[c][flagIdx] = V.size.y; + } } } undo(move) { this.disaggregateFlags(JSON.parse(move.flags)); V.UndoOnBoard(this.board, move); - if (this.subTurn == 2) { - this.subTurn = 1; - this.movesCount--; - } else { - // subTurn == 1 (after a move played) + if (this.subTurn == 1) { this.turn = V.GetOppCol(this.turn); - this.subTurn = 2; + this.movesCount--; } + else this.firstMove.pop(); + this.subTurn = 3 - this.subTurn; this.postUndo(move); } };