X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FPacosako.js;h=70121c9df374934bf20f38f57da067999a5e8dbe;hb=21704b041240cb440d03cfa64a90ed0be6f28415;hp=cde503977b372bfb3addc8b40dd02fd09bb2fdd1;hpb=8ec71052706a106ea3d04316d7f76a48aff98bec;p=vchess.git diff --git a/client/src/variants/Pacosako.js b/client/src/variants/Pacosako.js index cde50397..70121c9d 100644 --- a/client/src/variants/Pacosako.js +++ b/client/src/variants/Pacosako.js @@ -3,6 +3,23 @@ import { randInt } from "@/utils/alea"; export class PacosakoRules extends ChessRules { + static get Options() { + return { + select: ChessRules.Options.select, + check: [ + { + label: "pacoplay mode", + variable: "pacoplay", + defaut: false + } + ] + }; + } + + static AbbreviateOptions(opts) { + return (opts["pacoplay"] ? "PP" : ""); + } + static get IMAGE_EXTENSION() { return ".png"; } @@ -30,25 +47,25 @@ export class PacosakoRules extends ChessRules { x: ['b', 'k'], y: ['q', 'q'], z: ['q', 'k'], - '_': ['k', 'k'] + '@': ['k', 'k'] }; } static fen2board(f) { - // Underscore is character 95, in file w_ - return f.charCodeAt() <= 95 ? "w" + f.toLowerCase() : "b" + f; + // Arobase is character 64 + return f.charCodeAt() <= 90 ? "w" + f.toLowerCase() : "b" + f; } static IsGoodPosition(position) { if (position.length == 0) return false; const rows = position.split("/"); if (rows.length != V.size.x) return false; - let kingSymb = ['k', 'g', 'm', 'u', 'x', 'z', '_']; + let kingSymb = ['k', 'g', 'm', 'u', 'x', 'z', '@']; let kings = { 'k': 0, 'K': 0 }; for (let row of rows) { let sumElts = 0; for (let i = 0; i < row.length; i++) { - if (!!(row[i].toLowerCase().match(/[a-z_]/))) { + if (!!(row[i].toLowerCase().match(/[a-z@]/))) { sumElts++; if (kingSymb.includes(row[i])) kings['k']++; // Not "else if", if two kings dancing together @@ -104,12 +121,12 @@ export class PacosakoRules extends ChessRules { this.kingPos = { w: [-1, -1], b: [-1, -1] }; const fenRows = V.ParseFen(fen).position.split("/"); const startRow = { 'w': V.size.x - 1, 'b': 0 }; - const kingSymb = ['k', 'g', 'm', 'u', 'x', 'z', '_']; + const kingSymb = ['k', 'g', 'm', 'u', 'x', 'z', '@']; for (let i = 0; i < fenRows.length; i++) { let k = 0; for (let j = 0; j < fenRows[i].length; j++) { const c = fenRows[i].charAt(j); - if (!!(c.toLowerCase().match(/[a-z_]/))) { + if (!!(c.toLowerCase().match(/[a-z@]/))) { if (kingSymb.includes(c)) this.kingPos["b"] = [i, k]; // Not "else if", in case of two kings dancing together @@ -126,19 +143,24 @@ export class PacosakoRules extends ChessRules { } setOtherVariables(fen) { - super.setOtherVariables(fen); // Stack of "last move" only for intermediate chaining this.lastMoveEnd = [null]; // Local stack of non-capturing union moves: this.umoves = []; const umove = V.ParseFen(fen).umove; - if (umove == "-") this.umoves.push(null); - else { - this.umoves.push({ - start: ChessRules.SquareToCoords(umove.substr(0, 2)), - end: ChessRules.SquareToCoords(umove.substr(2)) - }); + this.pacoplay = !umove; //"pacoplay.com mode" ? + if (!this.pacoplay) { + if (umove == "-") this.umoves.push(null); + else { + this.umoves.push({ + start: ChessRules.SquareToCoords(umove.substr(0, 2)), + end: ChessRules.SquareToCoords(umove.substr(2)) + }); + } } + // Local stack of positions to avoid redundant moves: + this.repetitions = []; + super.setOtherVariables(fen); } static IsGoodFen(fen) { @@ -151,12 +173,13 @@ export class PacosakoRules extends ChessRules { } static IsGoodFlags(flags) { - // 4 for castle + 16 for pawns - return !!flags.match(/^[a-z]{4,4}[01]{16,16}$/); + // 4 for castle + 16 for pawns (more permissive, for pacoplay mode) + return !!flags.match(/^[a-z]{4,4}[01]{0,16}$/); } setFlags(fenflags) { super.setFlags(fenflags); //castleFlags + if (this.pacoplay) return; this.pawnFlags = { w: [...Array(8)], //pawns can move 2 squares? b: [...Array(8)] @@ -169,12 +192,16 @@ export class PacosakoRules extends ChessRules { } aggregateFlags() { + if (!this.pacoplay) return super.aggregateFlags(); return [this.castleFlags, this.pawnFlags]; } disaggregateFlags(flags) { - this.castleFlags = flags[0]; - this.pawnFlags = flags[1]; + if (!this.pacoplay) super.disaggregateFlags(flags); + else { + this.castleFlags = flags[0]; + this.pawnFlags = flags[1]; + } } getUmove(move) { @@ -197,17 +224,20 @@ export class PacosakoRules extends ChessRules { ); } - static GenRandInitFen(randomness) { + static GenRandInitFen(options) { // Add 16 pawns flags + empty umove: - return ChessRules.GenRandInitFen(randomness) - .slice(0, -2) + "1111111111111111 - -"; + const pawnFlags = (options.pacoplay ? "" : "1111111111111111"); + return ChessRules.GenRandInitFen(options).slice(0, -2) + + pawnFlags + " -" + (!options.pacoplay ? " -" : ""); } getFlagsFen() { let fen = super.getFlagsFen(); - // Add pawns flags - for (let c of ["w", "b"]) - for (let i = 0; i < 8; i++) fen += (this.pawnFlags[c][i] ? "1" : "0"); + if (!this.pacoplay) { + // Add pawns flags + for (let c of ["w", "b"]) + for (let i = 0; i < 8; i++) fen += (this.pawnFlags[c][i] ? "1" : "0"); + } return fen; } @@ -222,11 +252,13 @@ export class PacosakoRules extends ChessRules { } getFen() { - return super.getFen() + " " + this.getUmoveFen(); + const umoveFen = this.pacoplay ? "" : (" " + this.getUmoveFen()); + return super.getFen() + umoveFen; } getFenForRepeat() { - return super.getFenForRepeat() + "_" + this.getUmoveFen(); + const umoveFen = this.pacoplay ? "" : ("_" + this.getUmoveFen()); + return super.getFenForRepeat() + umoveFen; } getColor(i, j) { @@ -370,7 +402,7 @@ export class PacosakoRules extends ChessRules { } let baseMoves = []; const c = this.turn; - switch (piece || this.getPiece(x, y)) { + switch (piece) { case V.PAWN: { const firstRank = (c == 'w' ? 7 : 0); baseMoves = this.getPotentialPawnMoves([x, y]).filter(m => { @@ -380,7 +412,8 @@ export class PacosakoRules extends ChessRules { ( m.start.x == firstRank || Math.abs(m.end.x - m.start.x) == 1 || - this.pawnFlags[c][m.start.y] + this.pacoplay || + (!this.pacoplay && this.pawnFlags[c][m.start.y]) ) && ( @@ -469,6 +502,20 @@ export class PacosakoRules extends ChessRules { return moves; } + getPotentialKingMoves(sq) { + if (!this.pacoplay) return super.getPotentialKingMoves(sq); + // Initialize with normal moves, without captures + let moves = []; + for (let s of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) { + const [i, j] = [sq[0] + s[0], sq[1] + s[1]]; + if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) + moves.push(this.getBasicMove(sq, [i, j])); + } + if (this.castleFlags[this.turn].some(v => v < V.size.y)) + moves = moves.concat(this.getCastleMoves(sq)); + return moves; + } + getEpSquare(moveOrSquare) { if (typeof moveOrSquare === "string") { const square = moveOrSquare; @@ -705,10 +752,34 @@ export class PacosakoRules extends ChessRules { getCheckSquares() { return []; } + filterValid(moves) { if (moves.length == 0) return []; - const L = this.umoves.length; //at least 1: init from FEN - return moves.filter(m => !this.oppositeMoves(this.umoves[L - 1], m)); + const L = (!this.pacoplay ? this.umoves.length : 0); + return moves.filter(m => { + if (L > 0 && this.oppositeMoves(this.umoves[L - 1], m)) return false; + if (!m.end.released) return true; + // Check for repetitions: + V.PlayOnBoard(this.board, m); + const newState = { + piece: m.end.released, + square: { x: m.end.x, y: m.end.y }, + position: this.getBaseFen() + }; + const repet = + this.repetitions.some(r => { + return ( + r.piece == newState.piece && + ( + r.square.x == newState.square.x && + r.square.y == newState.square.y + ) && + r.position == newState.position + ); + }); + V.UndoOnBoard(this.board, m); + return !repet; + }); } updateCastleFlags(move, piece) { @@ -742,13 +813,14 @@ export class PacosakoRules extends ChessRules { // NOTE: lm.p != V.KING, always. const piece = !!lm - ? lm.p : - this.getPiece(move.vanish[0].x, move.vanish[0].y); + ? lm.p + : this.getPiece(move.vanish[0].x, move.vanish[0].y); if (piece == V.KING) this.kingPos[c] = [move.appear[0].x, move.appear[0].y]; this.updateCastleFlags(move, piece); const pawnFirstRank = (c == 'w' ? 6 : 1); if ( + !this.pacoplay && move.start.x == pawnFirstRank && piece == V.PAWN && Math.abs(move.end.x - move.start.x) == 2 @@ -777,7 +849,17 @@ export class PacosakoRules extends ChessRules { }); } V.PlayOnBoard(this.board, move); - this.umoves.push(this.getUmove(move)); + if (!this.pacoplay) this.umoves.push(this.getUmove(move)); + if (!move.end.released) this.repetitions = []; + else { + this.repetitions.push( + { + piece: move.end.released, + square: { x: move.end.x, y: move.end.y }, + position: this.getBaseFen() + } + ); + } } undo(move) { @@ -789,7 +871,8 @@ export class PacosakoRules extends ChessRules { this.turn = V.GetOppCol(this.turn); this.movesCount--; } - this.umoves.pop(); + if (!this.pacoplay) this.umoves.pop(); + if (!!move.end.released) this.repetitions.pop(); this.postUndo(move); } @@ -833,6 +916,7 @@ export class PacosakoRules extends ChessRules { for (let i = mvArray.length - 1; i >= 0; i--) this.undo(mvArray[i]); if (!mv.end.released) return (mvArray.length > 1 ? mvArray : mvArray[0]); } + return null; //never reached } // NOTE: evalPosition() is wrong, but unused since bot plays at random @@ -870,8 +954,13 @@ export class PacosakoRules extends ChessRules { // Add potential promotion indications: const firstLastRank = (c == 'w' ? [7, 0] : [0, 7]); if (move.end.x == firstLastRank[1] && piece == V.PAWN) { - const up = this.getUnionPieces(move.appear[0].c, move.appear[0].p); - notation += "=" + up[c].toUpperCase(); + notation += "="; + if (ChessRules.PIECES.includes(move.appear[0].p)) + notation += move.appear[0].p.toUpperCase(); + else { + const up = this.getUnionPieces(move.appear[0].c, move.appear[0].p); + notation += up[c].toUpperCase(); + } } else if ( move.end.x == firstLastRank[0] &&