From 3a2a7b5fd3c6bfd0752838094c27e1fb6172d109 Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Sat, 14 Mar 2020 13:50:42 +0100 Subject: [PATCH] Change castle flags. Eightpieces still not OK, but almost --- client/src/base_rules.js | 147 +++++----- client/src/components/BaseGame.vue | 36 +-- client/src/components/Board.vue | 22 +- client/src/variants/Alice.js | 13 +- client/src/variants/Allmate1.js | 32 +-- client/src/variants/Allmate2.js | 34 ++- client/src/variants/Antiking.js | 15 +- client/src/variants/Arena.js | 6 +- client/src/variants/Atomic.js | 26 +- client/src/variants/Benedict.js | 6 +- client/src/variants/Capture.js | 2 +- client/src/variants/Checkered.js | 34 +-- client/src/variants/Circular.js | 16 +- client/src/variants/Crazyhouse.js | 8 +- client/src/variants/Dark.js | 8 +- client/src/variants/Eightpieces.js | 422 ++++++++++++++++------------- client/src/variants/Extinction.js | 8 +- client/src/variants/Grand.js | 10 +- client/src/variants/Grasshopper.js | 2 +- client/src/variants/Hidden.js | 10 +- client/src/variants/Hiddenqueen.js | 91 +++---- client/src/variants/Magnetic.js | 27 +- client/src/variants/Marseille.js | 34 ++- client/src/variants/Recycle.js | 26 +- client/src/variants/Shatranj.js | 3 +- client/src/variants/Suction.js | 54 ++-- client/src/variants/Suicide.js | 6 +- client/src/variants/Threechecks.js | 12 +- client/src/variants/Wildebeest.js | 7 +- client/src/variants/Wormhole.js | 4 + client/src/views/Analyse.vue | 5 +- client/src/views/Game.vue | 11 +- 32 files changed, 604 insertions(+), 533 deletions(-) diff --git a/client/src/base_rules.js b/client/src/base_rules.js index 4c32fe69..890601d2 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -37,6 +37,11 @@ export const ChessRules = class ChessRules { return true; } + // Or castle + static get HasCastle() { + return V.HasFlags; + } + // Some variants don't have en-passant static get HasEnpassant() { return true; @@ -134,7 +139,8 @@ export const ChessRules = class ChessRules { // For FEN checking static IsGoodFlags(flags) { - return !!flags.match(/^[01]{4,4}$/); + // NOTE: a little too permissive to work with more variants + return !!flags.match(/^[a-z]{4,4}$/); } static IsGoodEnpassant(enpassant) { @@ -175,6 +181,11 @@ export const ChessRules = class ChessRules { return b; //usual pieces in pieces/ folder } + // Path to promotion pieces (usually the same) + getPPpath(b) { + return this.getPpath(b); + } + // Aggregates flags into one object aggregateFlags() { return this.castleFlags; @@ -239,13 +250,15 @@ export const ChessRules = class ChessRules { static GenRandInitFen(randomness) { if (randomness == 0) // Deterministic: - return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 1111 -"; + return "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 ahah -"; let pieces = { w: new Array(8), b: new Array(8) }; + let flags = ""; // Shuffle pieces on first (and last rank if randomness == 2) for (let c of ["w", "b"]) { if (c == 'b' && randomness == 1) { pieces['b'] = pieces['w']; + flags += flags; break; } @@ -289,13 +302,14 @@ export const ChessRules = class ChessRules { pieces[c][bishop2Pos] = "b"; pieces[c][knight2Pos] = "n"; pieces[c][rook2Pos] = "r"; + flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos); } // Add turn + flags + enpassant return ( pieces["b"].join("") + "/pppppppp/8/8/8/8/PPPPPPPP/" + pieces["w"].join("").toUpperCase() + - " w 0 1111 -" + " w 0 " + flags + " -" ); } @@ -366,10 +380,9 @@ export const ChessRules = class ChessRules { // Flags part of the FEN string getFlagsFen() { let flags = ""; - // Add castling flags - for (let i of ["w", "b"]) { - for (let j = 0; j < 2; j++) flags += this.castleFlags[i][j] ? "1" : "0"; - } + // Castling flags + for (let c of ["w", "b"]) + flags += this.castleFlags[c].map(V.CoordToColumn).join(""); return flags; } @@ -402,8 +415,10 @@ export const ChessRules = class ChessRules { setFlags(fenflags) { // white a-castle, h-castle, black a-castle, h-castle this.castleFlags = { w: [true, true], b: [true, true] }; - for (let i = 0; i < 4; i++) - this.castleFlags[i < 2 ? "w" : "b"][i % 2] = fenflags.charAt(i) == "1"; + for (let i = 0; i < 4; i++) { + this.castleFlags[i < 2 ? "w" : "b"][i % 2] = + V.ColumnToCoord(fenflags.charAt(i)); + } } ////////////////// @@ -422,10 +437,9 @@ export const ChessRules = class ChessRules { this.setOtherVariables(fen); } - // Scan board for kings and rooks positions - scanKingsRooks(fen) { + // Scan board for kings positions + scanKings(fen) { this.INIT_COL_KING = { w: -1, b: -1 }; - this.INIT_COL_ROOK = { w: [-1, -1], b: [-1, -1] }; this.kingPos = { w: [-1, -1], b: [-1, -1] }; //squares of white and black king const fenRows = V.ParseFen(fen).position.split("/"); const startRow = { 'w': V.size.x - 1, 'b': 0 }; @@ -441,18 +455,6 @@ export const ChessRules = class ChessRules { this.kingPos["w"] = [i, k]; this.INIT_COL_KING["w"] = k; break; - case "r": - if (i == startRow['b']) { - if (this.INIT_COL_ROOK["b"][0] < 0) this.INIT_COL_ROOK["b"][0] = k; - else this.INIT_COL_ROOK["b"][1] = k; - } - break; - case "R": - if (i == startRow['w']) { - if (this.INIT_COL_ROOK["w"][0] < 0) this.INIT_COL_ROOK["w"][0] = k; - else this.INIT_COL_ROOK["w"][1] = k; - } - break; default: { const num = parseInt(fenRows[i].charAt(j)); if (!isNaN(num)) k += num - 1; @@ -475,8 +477,8 @@ export const ChessRules = class ChessRules { : undefined; this.epSquares = [epSq]; } - // Search for king and rooks positions: - this.scanKingsRooks(fen); + // Search for kings positions: + this.scanKings(fen); } ///////////////////// @@ -741,7 +743,7 @@ export const ChessRules = class ChessRules { // What are the king moves from square x,y ? getPotentialKingMoves(sq) { // Initialize with normal moves - let moves = this.getSlideNJumpMoves( + const moves = this.getSlideNJumpMoves( sq, V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep" @@ -768,7 +770,7 @@ export const ChessRules = class ChessRules { castleSide < 2; castleSide++ //large, then small ) { - if (!this.castleFlags[c][castleSide]) continue; + if (this.castleFlags[c][castleSide] >= V.size.y) continue; // If this code is reached, rooks and king are on initial position // Nothing on the path of the king ? (and no checks) @@ -790,10 +792,10 @@ export const ChessRules = class ChessRules { // Nothing on the path to the rook? step = castleSide == 0 ? -1 : 1; - for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) { + const rookPos = this.castleFlags[c][castleSide]; + for (i = y + step; i != rookPos; i += step) { if (this.board[x][i] != V.EMPTY) continue castlingCheck; } - const rookPos = this.INIT_COL_ROOK[c][castleSide]; // Nothing on final squares, except maybe king and castling rook? for (i = 0; i < 2; i++) { @@ -998,84 +1000,72 @@ export const ChessRules = class ChessRules { for (let psq of move.vanish) board[psq.x][psq.y] = psq.c + psq.p; } + prePlay() {} + + play(move) { + // DEBUG: +// if (!this.states) this.states = []; +// const stateFen = this.getBaseFen() + this.getTurnFen();// + this.getFlagsFen(); +// this.states.push(stateFen); + + this.prePlay(move); + if (V.HasFlags) move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo) + if (V.HasEnpassant) this.epSquares.push(this.getEpSquare(move)); + V.PlayOnBoard(this.board, move); + this.turn = V.GetOppCol(this.turn); + this.movesCount++; + this.postPlay(move); + } + // After move is played, update variables + flags - updateVariables(move) { + postPlay(move) { + const c = V.GetOppCol(this.turn); let piece = undefined; - // TODO: update variables before move is played, and just use this.turn? - // (doesn't work in general, think MarseilleChess) - let c = undefined; - if (move.vanish.length >= 1) { + if (move.vanish.length >= 1) // Usual case, something is moved piece = move.vanish[0].p; - c = move.vanish[0].c; - } else { + else // Crazyhouse-like variants piece = move.appear[0].p; - c = move.appear[0].c; - } - if (!['w','b'].includes(c)) { - // Checkered, for example - c = V.GetOppCol(this.turn); - } const firstRank = c == "w" ? V.size.x - 1 : 0; // Update king position + flags if (piece == V.KING && move.appear.length > 0) { this.kingPos[c][0] = move.appear[0].x; this.kingPos[c][1] = move.appear[0].y; - if (V.HasFlags) this.castleFlags[c] = [false, false]; + if (V.HasCastle) this.castleFlags[c] = [V.size.y, V.size.y]; return; } - if (V.HasFlags) { + if (V.HasCastle) { // Update castling flags if rooks are moved const oppCol = V.GetOppCol(c); const oppFirstRank = V.size.x - 1 - firstRank; if ( move.start.x == firstRank && //our rook moves? - this.INIT_COL_ROOK[c].includes(move.start.y) + this.castleFlags[c].includes(move.start.y) ) { - const flagIdx = (move.start.y == this.INIT_COL_ROOK[c][0] ? 0 : 1); - this.castleFlags[c][flagIdx] = false; + const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1); + this.castleFlags[c][flagIdx] = V.size.y; } else if ( move.end.x == oppFirstRank && //we took opponent rook? - this.INIT_COL_ROOK[oppCol].includes(move.end.y) + this.castleFlags[oppCol].includes(move.end.y) ) { - const flagIdx = (move.end.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1); - this.castleFlags[oppCol][flagIdx] = false; + const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1); + this.castleFlags[oppCol][flagIdx] = V.size.y; } } } - // After move is undo-ed *and flags resetted*, un-update other variables - // TODO: more symmetry, by storing flags increment in move (?!) - unupdateVariables(move) { - // (Potentially) Reset king position - const c = this.getColor(move.start.x, move.start.y); - if (this.getPiece(move.start.x, move.start.y) == V.KING) - this.kingPos[c] = [move.start.x, move.start.y]; - } - - play(move) { - // DEBUG: -// if (!this.states) this.states = []; -// const stateFen = this.getBaseFen() + this.getTurnFen();// + this.getFlagsFen(); -// this.states.push(stateFen); - - if (V.HasFlags) move.flags = JSON.stringify(this.aggregateFlags()); //save flags (for undo) - if (V.HasEnpassant) this.epSquares.push(this.getEpSquare(move)); - V.PlayOnBoard(this.board, move); - this.turn = V.GetOppCol(this.turn); - this.movesCount++; - this.updateVariables(move); - } + preUndo() {} undo(move) { + this.preUndo(move); if (V.HasEnpassant) this.epSquares.pop(); if (V.HasFlags) this.disaggregateFlags(JSON.parse(move.flags)); V.UndoOnBoard(this.board, move); this.turn = V.GetOppCol(this.turn); this.movesCount--; - this.unupdateVariables(move); + this.postUndo(move); // DEBUG: // const stateFen = this.getBaseFen() + this.getTurnFen();// + this.getFlagsFen(); @@ -1083,6 +1073,15 @@ export const ChessRules = class ChessRules { // this.states.pop(); } + // After move is undo-ed *and flags resetted*, un-update other variables + // TODO: more symmetry, by storing flags increment in move (?!) + postUndo(move) { + // (Potentially) Reset king position + const c = this.getColor(move.start.x, move.start.y); + if (this.getPiece(move.start.x, move.start.y) == V.KING) + this.kingPos[c] = [move.start.x, move.start.y]; + } + /////////////// // END OF GAME diff --git a/client/src/components/BaseGame.vue b/client/src/components/BaseGame.vue index 8c96e394..c85d47e9 100644 --- a/client/src/components/BaseGame.vue +++ b/client/src/components/BaseGame.vue @@ -191,7 +191,8 @@ export default { this.moves.unshift({ notation: "...", start: { x: -1, y: -1 }, - end: { x: -1, y: -1 } + end: { x: -1, y: -1 }, + fen: game.fenStart }); } this.positionCursorTo(this.moves.length - 1); @@ -207,9 +208,7 @@ export default { } else { this.lastMove = this.moves[index]; } - } - else - this.lastMove = null; + } else this.lastMove = null; }, analyzePosition: function() { let newUrl = @@ -310,7 +309,6 @@ export default { const playSubmove = (smove) => { if (!navigate) smove.notation = this.vr.getNotation(smove); this.vr.play(smove); - this.lastMove = smove; // Is opponent in check? this.incheck = this.vr.getCheckSquares(this.vr.turn); if (!navigate) { @@ -355,11 +353,11 @@ export default { const afterMove = (smove, initurn) => { if (this.vr.turn != initurn) { // Turn has changed: move is complete - if (!smove.fen) { + if (!smove.fen) // NOTE: only FEN of last sub-move is required (thus setting it here) smove.fen = this.vr.getFen(); - this.emitFenIfAnalyze(); - } + this.lastMove = smove; + this.emitFenIfAnalyze(); this.inMultimove = false; if (!noemit) { var score = this.vr.getCurrentScore(); @@ -437,7 +435,11 @@ export default { this.incheck = this.vr.getCheckSquares(this.vr.turn); } else { if (!move) { - if (this.cursor < 0) return; //no more moves + const minCursor = + this.moves.length > 0 && this.moves[0].notation == "..." + ? 1 + : 0; + if (this.cursor < minCursor) return; //no more moves move = this.moves[this.cursor]; } // Caution; if multi-move, undo all submoves from last to first @@ -469,15 +471,13 @@ export default { }, gotoBegin: function() { if (this.inMultimove) this.cancelCurrentMultimove(); - while (this.cursor >= 0) - this.undo(null, null, "light"); - if (this.moves.length > 0 && this.moves[0].notation == "...") { - this.cursor = 0; - this.lastMove = this.moves[0]; - } else { - this.lastMove = null; - } - this.incheck = []; + const minCursor = + this.moves.length > 0 && this.moves[0].notation == "..." + ? 1 + : 0; + while (this.cursor >= minCursor) this.undo(null, null, "light"); + this.lastMove = (minCursor == 1 ? this.moves[0] : null); + this.incheck = this.vr.getCheckSquares(this.vr.turn); this.emitFenIfAnalyze(); }, gotoEnd: function() { diff --git a/client/src/components/Board.vue b/client/src/components/Board.vue index fdeeef31..0d531a6b 100644 --- a/client/src/components/Board.vue +++ b/client/src/components/Board.vue @@ -25,6 +25,7 @@ export default { selectedPiece: null, //moving piece (or clicked piece) start: null, //pixels coordinates + id of starting square (click or drag) click: "", + clickTime: 0, settings: store.state.settings }; }, @@ -112,7 +113,12 @@ export default { attrs: { src: "/images/pieces/" + - this.vr.getPpath(this.vr.board[ci][cj], this.userColor, this.score) + + this.vr.getPpath( + this.vr.board[ci][cj], + // Extra args useful for some variants: + this.userColor, + this.score, + this.orientation) + ".svg" } }) @@ -298,6 +304,9 @@ export default { // A "choice" is a move const applyMove = (e) => { e.stopPropagation(); + // Force a delay between move is shown and clicked + // (otherwise a "double-click" bug might occur) + if (Date.now() - this.clickTime < 200) return; this.play(m); this.choices = []; }; @@ -322,7 +331,10 @@ export default { attrs: { src: "/images/pieces/" + - this.vr.getPpath(m.appear[0].c + m.appear[0].p) + + this.vr.getPPpath( + m.appear[0].c + m.appear[0].p, + // Extra arg useful for some variants: + this.orientation) + ".svg" }, class: { "choice-piece": true }, @@ -441,8 +453,10 @@ export default { let endSquare = getSquareFromId(landing.id); let moves = this.findMatchingMoves(endSquare); this.possibleMoves = []; - if (moves.length > 1) this.choices = moves; - else if (moves.length == 1) this.play(moves[0]); + if (moves.length > 1) { + this.clickTime = Date.now(); + this.choices = moves; + } else if (moves.length == 1) this.play(moves[0]); // else: forbidden move attempt }, findMatchingMoves: function(endSquare) { diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js index 4d6b3c37..d64c0778 100644 --- a/client/src/variants/Alice.js +++ b/client/src/variants/Alice.js @@ -243,22 +243,23 @@ export const VariantRules = class AliceRules extends ChessRules { return res; } - updateVariables(move) { - super.updateVariables(move); //standard king + postPlay(move) { + super.postPlay(move); //standard king const piece = move.vanish[0].p; const c = move.vanish[0].c; // "l" = Alice king if (piece == "l") { this.kingPos[c][0] = move.appear[0].x; this.kingPos[c][1] = move.appear[0].y; - this.castleFlags[c] = [false, false]; + this.castleFlags[c] = [8, 8]; } } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); const c = move.vanish[0].c; - if (move.vanish[0].p == "l") this.kingPos[c] = [move.start.x, move.start.y]; + if (move.vanish[0].p == "l") + this.kingPos[c] = [move.start.x, move.start.y]; } getCurrentScore() { diff --git a/client/src/variants/Allmate1.js b/client/src/variants/Allmate1.js index 43062a39..bc10478e 100644 --- a/client/src/variants/Allmate1.js +++ b/client/src/variants/Allmate1.js @@ -11,7 +11,7 @@ export const VariantRules = class Allmate1Rules extends ChessRules { } static GenRandInitFen(randomness) { - return ChessRules.GenRandInitFen(randomness).replace(/ -$/, ""); + return ChessRules.GenRandInitFen(randomness).slice(0, -2); } getPotentialMovesFrom([x, y]) { @@ -118,7 +118,7 @@ export const VariantRules = class Allmate1Rules extends ChessRules { castleSide < 2; castleSide++ //large, then small ) { - if (!this.castleFlags[c][castleSide]) continue; + if (this.castleFlags[c][castleSide] >= 8) continue; // If this code is reached, rooks and king are on initial position // Nothing on the path of the king ? (and no checks) @@ -137,10 +137,10 @@ export const VariantRules = class Allmate1Rules extends ChessRules { // Nothing on the path to the rook? step = castleSide == 0 ? -1 : 1; - for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) { + const rookPos = this.castleFlags[c][castleSide]; + for (i = y + step; i != rookPos; i += step) { if (this.board[x][i] != V.EMPTY) continue castlingCheck; } - const rookPos = this.INIT_COL_ROOK[c][castleSide]; // Nothing on final squares, except maybe king and castling rook? for (i = 0; i < 2; i++) { @@ -238,34 +238,32 @@ export const VariantRules = class Allmate1Rules extends ChessRules { }); } - updateVariables(move) { - super.updateVariables(move); - const color = V.GetOppCol(this.turn); + postPlay(move) { + super.postPlay(move); if (move.vanish.length >= 2 && move.appear.length == 1) { - move.vanish.forEach(v => { - if (v.c == color) - return; + for (let i = 1; i this.INIT_COL_KING[v.c] - this.castleFlags[v.c][1] = false; + this.castleFlags[v.c][1] = 8; } - }); + } } } - unupdateVariables(move) { - super.unupdateVariables(move); - const color = this.turn; + preUndo(move) { + super.preUndo(move); + const oppCol = this.turn; if (move.vanish.length >= 2 && move.appear.length == 1) { // Did opponent king disappeared? - const psq = move.vanish.find(v => v.p == V.KING && v.c != color) + const psq = move.vanish.find(v => v.p == V.KING && v.c == oppCol) if (psq) this.kingPos[psq.c] = [psq.x, psq.y]; } diff --git a/client/src/variants/Allmate2.js b/client/src/variants/Allmate2.js index 828e459f..daa3542a 100644 --- a/client/src/variants/Allmate2.js +++ b/client/src/variants/Allmate2.js @@ -11,7 +11,7 @@ export const VariantRules = class Allmate2Rules extends ChessRules { } static GenRandInitFen(randomness) { - return ChessRules.GenRandInitFen(randomness).replace(/ -$/, ""); + return ChessRules.GenRandInitFen(randomness).slice(0, -2); } getPotentialMovesFrom([x, y]) { @@ -38,7 +38,7 @@ export const VariantRules = class Allmate2Rules extends ChessRules { } // 2) Among attacked pieces, which cannot escape capture? - // --> without (normal-)capturing: difference with Allmate variant + // --> without (normal-)capturing: difference with Allmate1 variant // Avoid "oppMoves = this.getAllValidMoves();" => infinite recursion outerLoop: for (let i=0; i= 8) continue; // If this code is reached, rooks and king are on initial position // Nothing on the path of the king ? (and no checks) @@ -141,10 +141,10 @@ export const VariantRules = class Allmate2Rules extends ChessRules { // Nothing on the path to the rook? step = castleSide == 0 ? -1 : 1; - for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) { + const rookPos = this.castleFlags[c][castleSide]; + for (i = y + step; i != rookPos; i += step) { if (this.board[x][i] != V.EMPTY) continue castlingCheck; } - const rookPos = this.INIT_COL_ROOK[c][castleSide]; // Nothing on final squares, except maybe king and castling rook? for (i = 0; i < 2; i++) { @@ -242,34 +242,32 @@ export const VariantRules = class Allmate2Rules extends ChessRules { }); } - updateVariables(move) { - super.updateVariables(move); - const color = V.GetOppCol(this.turn); + postPlay(move) { + super.postPlay(move); if (move.vanish.length >= 2 && move.appear.length == 1) { - move.vanish.forEach(v => { - if (v.c == color) - return; + for (let i = 1; i this.INIT_COL_KING[v.c] - this.castleFlags[v.c][1] = false; + this.castleFlags[v.c][1] = 8; } - }); + } } } - unupdateVariables(move) { - super.unupdateVariables(move); - const color = this.turn; + preUndo(move) { + super.preUndo(move); + const oppCol = this.turn; if (move.vanish.length >= 2 && move.appear.length == 1) { // Did opponent king disappeared? - const psq = move.vanish.find(v => v.p == V.KING && v.c != color) + const psq = move.vanish.find(v => v.p == V.KING && v.c == oppCol) if (psq) this.kingPos[psq.c] = [psq.x, psq.y]; } diff --git a/client/src/variants/Antiking.js b/client/src/variants/Antiking.js index 77f0c839..c3fe0137 100644 --- a/client/src/variants/Antiking.js +++ b/client/src/variants/Antiking.js @@ -111,8 +111,8 @@ export const VariantRules = class AntikingRules extends ChessRules { return res; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); const piece = move.vanish[0].p; const c = move.vanish[0].c; // Update antiking position @@ -122,8 +122,8 @@ export const VariantRules = class AntikingRules extends ChessRules { } } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); const c = move.vanish[0].c; if (move.vanish[0].p == V.ANTIKING) this.antikingPos[c] = [move.start.x, move.start.y]; @@ -153,14 +153,16 @@ export const VariantRules = class AntikingRules extends ChessRules { static GenRandInitFen(randomness) { if (randomness == 0) - return "rnbqkbnr/pppppppp/3A4/8/8/3a4/PPPPPPPP/RNBQKBNR w 0 1111 -"; + return "rnbqkbnr/pppppppp/3A4/8/8/3a4/PPPPPPPP/RNBQKBNR w 0 ahah -"; let pieces = { w: new Array(8), b: new Array(8) }; + let flags = ""; let antikingPos = { w: -1, b: -1 }; for (let c of ["w", "b"]) { if (c == 'b' && randomness == 1) { pieces['b'] = pieces['w']; antikingPos['b'] = antikingPos['w']; + flags += flags; break; } @@ -201,6 +203,7 @@ export const VariantRules = class AntikingRules extends ChessRules { pieces[c][bishop2Pos] = "b"; pieces[c][knight2Pos] = "n"; pieces[c][rook2Pos] = "r"; + flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos); } const ranks23_black = "pppppppp/" + @@ -220,7 +223,7 @@ export const VariantRules = class AntikingRules extends ChessRules { ranks23_white + "/" + pieces["w"].join("").toUpperCase() + - " w 0 1111 -" + " w 0 " + flags + " -" ); } diff --git a/client/src/variants/Arena.js b/client/src/variants/Arena.js index 9ccd3cc4..620b980a 100644 --- a/client/src/variants/Arena.js +++ b/client/src/variants/Arena.js @@ -1,14 +1,12 @@ import { ChessRules } from "@/base_rules"; export const VariantRules = class ArenaRules extends ChessRules { - static get hasFlags() { + static get HasFlags() { return false; } static GenRandInitFen(randomness) { - return ChessRules - .GenRandInitFen(randomness) - .replace("w 0 1111 -", "w 0 -"); + return ChessRules.GenRandInitFen(randomness).slice(0, -6) + "-"; } static InArena(x) { diff --git a/client/src/variants/Atomic.js b/client/src/variants/Atomic.js index fdc665d8..51346da9 100644 --- a/client/src/variants/Atomic.js +++ b/client/src/variants/Atomic.js @@ -80,8 +80,8 @@ export const VariantRules = class AtomicRules extends ChessRules { ); } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); if (move.appear.length == 0) { // Capture const firstRank = { w: 7, b: 0 }; @@ -92,29 +92,25 @@ export const VariantRules = class AtomicRules extends ChessRules { Math.abs(this.kingPos[c][1] - move.end.y) <= 1 ) { this.kingPos[c] = [-1, -1]; - this.castleFlags[c] = [false, false]; + this.castleFlags[c] = [8, 8]; } else { // Now check if init rook(s) exploded if (Math.abs(move.end.x - firstRank[c]) <= 1) { - if (Math.abs(move.end.y - this.INIT_COL_ROOK[c][0]) <= 1) - this.castleFlags[c][0] = false; - if (Math.abs(move.end.y - this.INIT_COL_ROOK[c][1]) <= 1) - this.castleFlags[c][1] = false; + if (Math.abs(move.end.y - this.castleFlags[c][0]) <= 1) + this.castleFlags[c][0] = 8; + if (Math.abs(move.end.y - this.castleFlags[c][1]) <= 1) + this.castleFlags[c][1] = 8; } } } } } - unupdateVariables(move) { - super.unupdateVariables(move); - const c = move.vanish[0].c; + postUndo(move) { + super.postUndo(move); + const c = this.turn; const oppCol = V.GetOppCol(c); - if ( - [this.kingPos[c][0], this.kingPos[oppCol][0]].some(e => { - return e < 0; - }) - ) { + if ([this.kingPos[c][0], this.kingPos[oppCol][0]].some(e => e < 0)) { // There is a chance that last move blowed some king away.. for (let psq of move.vanish) { if (psq.p == "k") diff --git a/client/src/variants/Benedict.js b/client/src/variants/Benedict.js index 4a31fceb..eb621462 100644 --- a/client/src/variants/Benedict.js +++ b/client/src/variants/Benedict.js @@ -152,10 +152,10 @@ export const VariantRules = class BenedictRules extends ChessRules { castleSide < 2; castleSide++ //large, then small ) { - if (!this.castleFlags[c][castleSide]) continue; + if (this.castleFlags[c][castleSide] >= 8) continue; // If this code is reached, rooks and king are on initial position - const rookPos = this.INIT_COL_ROOK[c][castleSide]; + const rookPos = this.castleFlags[c][castleSide]; if (this.getColor(x, rookPos) != c) // Rook is here but changed color continue; @@ -176,7 +176,7 @@ export const VariantRules = class BenedictRules extends ChessRules { // Nothing on the path to the rook? step = castleSide == 0 ? -1 : 1; - for (i = y + step; i != this.INIT_COL_ROOK[c][castleSide]; i += step) { + for (i = y + step; i != rookPos; i += step) { if (this.board[x][i] != V.EMPTY) continue castlingCheck; } diff --git a/client/src/variants/Capture.js b/client/src/variants/Capture.js index a43f1671..5b286e0e 100644 --- a/client/src/variants/Capture.js +++ b/client/src/variants/Capture.js @@ -5,7 +5,7 @@ import { randInt } from "@/utils/alea"; export const VariantRules = class LosersRules extends ChessRules { // Trim all non-capturing moves static KeepCaptures(moves) { - return moves.filter(m => m.vanish.length == 2); + return moves.filter(m => m.vanish.length == 2 && m.appear.length == 1); } // Stop at the first capture found (if any) diff --git a/client/src/variants/Checkered.js b/client/src/variants/Checkered.js index 1e42d995..0e089f38 100644 --- a/client/src/variants/Checkered.js +++ b/client/src/variants/Checkered.js @@ -44,7 +44,7 @@ export const VariantRules = class CheckeredRules extends ChessRules { super.setOtherVariables(fen); // Local stack of non-capturing checkered moves: this.cmoves = []; - const cmove = fen.split(" ")[5]; + const cmove = V.ParseFen(fen).cmove; if (cmove == "-") this.cmoves.push(null); else { this.cmoves.push({ @@ -65,7 +65,7 @@ export const VariantRules = class CheckeredRules extends ChessRules { static IsGoodFlags(flags) { // 4 for castle + 16 for pawns - return !!flags.match(/^[01]{20,20}$/); + return !!flags.match(/^[a-z]{4,4}[01]{16,16}$/); } setFlags(fenflags) { @@ -74,7 +74,7 @@ export const VariantRules = class CheckeredRules extends ChessRules { w: [...Array(8).fill(true)], //pawns can move 2 squares? b: [...Array(8).fill(true)] }; - const flags = fenflags.substr(4); //skip first 4 digits, for castle + const flags = fenflags.substr(4); //skip first 4 letters, for castle for (let c of ["w", "b"]) { for (let i = 0; i < 8; i++) this.pawnFlags[c][i] = flags.charAt((c == "w" ? 0 : 8) + i) == "1"; @@ -331,12 +331,17 @@ export const VariantRules = class CheckeredRules extends ChessRules { return res; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); // Does this move turn off a 2-squares pawn flag? - const secondRank = [1, 6]; - if (secondRank.includes(move.start.x) && move.vanish[0].p == V.PAWN) + if ([1, 6].includes(move.start.x) && move.vanish[0].p == V.PAWN) this.pawnFlags[move.start.x == 6 ? "w" : "b"][move.start.y] = false; + this.cmoves.push(this.getCmove(move)); + } + + postUndo(move) { + super.postUndo(move); + this.cmoves.pop(); } getCurrentScore() { @@ -374,9 +379,9 @@ export const VariantRules = class CheckeredRules extends ChessRules { } static GenRandInitFen(randomness) { + // Add 16 pawns flags + empty cmove: return ChessRules.GenRandInitFen(randomness) - // Add 16 pawns flags + empty cmove: - .replace(" w 0 1111", " w 0 11111111111111111111 -"); + .slice(0, -2) + "1111111111111111 - -"; } static ParseFen(fen) { @@ -403,17 +408,6 @@ export const VariantRules = class CheckeredRules extends ChessRules { return fen; } - // TODO (design): this cmove update here or in (un)updateVariables ? - play(move) { - this.cmoves.push(this.getCmove(move)); - super.play(move); - } - - undo(move) { - this.cmoves.pop(); - super.undo(move); - } - static get SEARCH_DEPTH() { return 2; } diff --git a/client/src/variants/Circular.js b/client/src/variants/Circular.js index a89712d6..d6b431f6 100644 --- a/client/src/variants/Circular.js +++ b/client/src/variants/Circular.js @@ -3,6 +3,10 @@ import { ArrayFun } from "@/utils/array"; import { randInt, shuffle } from "@/utils/alea"; export const VariantRules = class CircularRules extends ChessRules { + static get HasCastle() { + return false; + } + static get HasEnpassant() { return false; } @@ -222,15 +226,11 @@ export const VariantRules = class CircularRules extends ChessRules { return flags; } - updateVariables(move) { + postPlay(move) { + super.postPlay(move); const c = move.vanish[0].c; - const secondRank = {"w":6, "b":2}; - // Update king position + flags - if (move.vanish[0].p == V.KING && move.appear.length > 0) { - this.kingPos[c][0] = move.appear[0].x; - this.kingPos[c][1] = move.appear[0].y; - } - else if (move.vanish[0].p == V.PAWN && secondRank[c] == move.start.x) + const secondRank = { "w": 6, "b": 2 }; + if (move.vanish[0].p == V.PAWN && secondRank[c] == move.start.x) // This move turns off a 2-squares pawn flag this.pawnFlags[c][move.start.y] = false; } diff --git a/client/src/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js index 4f9ff632..fb8c7441 100644 --- a/client/src/variants/Crazyhouse.js +++ b/client/src/variants/Crazyhouse.js @@ -200,8 +200,8 @@ export const VariantRules = class CrazyhouseRules extends ChessRules { return true; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); if (move.vanish.length == 2 && move.appear.length == 2) return; //skip castle const color = move.appear[0].c; if (move.vanish.length == 0) { @@ -218,8 +218,8 @@ export const VariantRules = class CrazyhouseRules extends ChessRules { else if (move.vanish.length == 2) this.reserve[color][move.vanish[1].p]++; } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); if (move.vanish.length == 2 && move.appear.length == 2) return; const color = this.turn; if (move.vanish.length == 0) { diff --git a/client/src/variants/Dark.js b/client/src/variants/Dark.js index 96163bfb..a7edb916 100644 --- a/client/src/variants/Dark.js +++ b/client/src/variants/Dark.js @@ -103,8 +103,8 @@ export const VariantRules = class DarkRules extends ChessRules { return []; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) // We took opponent king (because if castle vanish[1] is a rook) this.kingPos[this.turn] = [-1, -1]; @@ -113,8 +113,8 @@ export const VariantRules = class DarkRules extends ChessRules { this.updateEnlightened(); } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); const c = move.vanish[0].c; const oppCol = V.GetOppCol(c); if (this.kingPos[oppCol][0] < 0) diff --git a/client/src/variants/Eightpieces.js b/client/src/variants/Eightpieces.js index c83cf5ed..88c3739a 100644 --- a/client/src/variants/Eightpieces.js +++ b/client/src/variants/Eightpieces.js @@ -13,10 +13,6 @@ export const VariantRules = class EightpiecesRules extends ChessRules { return "l"; } - static get PIECES() { - return ChessRules.PIECES.concat([V.JAILER, V.SENTRY, V.LANCER]); - } - // Lancer directions *from white perspective* static get LANCER_DIRS() { return { @@ -31,6 +27,12 @@ export const VariantRules = class EightpiecesRules extends ChessRules { }; } + static get PIECES() { + return ChessRules.PIECES + .concat([V.JAILER, V.SENTRY]) + .concat(Object.keys(V.LANCER_DIRS)); + } + getPiece(i, j) { const piece = this.board[i][j].charAt(1); // Special lancer case: 8 possible orientations @@ -38,17 +40,66 @@ export const VariantRules = class EightpiecesRules extends ChessRules { return piece; } - getPpath(b) { - if ([V.JAILER, V.SENTRY].concat(Object.keys(V.LANCER_DIRS)).includes(b[1])) - return "Eightpieces/" + b; + getPpath(b, color, score, orientation) { + if ([V.JAILER, V.SENTRY].includes(b[1])) return "Eightpieces/" + b; + if (Object.keys(V.LANCER_DIRS).includes(b[1])) { + if (orientation == 'w') return "Eightpieces/" + b; + // Find opposite direction for adequate display: + let oppDir = ''; + switch (b[1]) { + case 'c': + oppDir = 'g'; + break; + case 'g': + oppDir = 'c'; + break; + case 'd': + oppDir = 'h'; + break; + case 'h': + oppDir = 'd'; + break; + case 'e': + oppDir = 'm'; + break; + case 'm': + oppDir = 'e'; + break; + case 'f': + oppDir = 'o'; + break; + case 'o': + oppDir = 'f'; + break; + } + return "Eightpieces/" + b[0] + oppDir; + } return b; } + getPPpath(b, orientation) { + return this.getPpath(b, null, null, orientation); + } + static ParseFen(fen) { const fenParts = fen.split(" "); - return Object.assign(ChessRules.ParseFen(fen), { - sentrypush: fenParts[5] - }); + return Object.assign( + ChessRules.ParseFen(fen), + { sentrypush: fenParts[5] } + ); + } + + static IsGoodFen(fen) { + if (!ChessRules.IsGoodFen(fen)) return false; + const fenParsed = V.ParseFen(fen); + // 5) Check sentry push (if any) + if ( + fenParsed.sentrypush != "-" && + !fenParsed.sentrypush.match(/^([a-h][1-8],?)+$/) + ) { + return false; + } + return true; } getFen() { @@ -89,9 +140,10 @@ export const VariantRules = class EightpiecesRules extends ChessRules { static GenRandInitFen(randomness) { if (randomness == 0) // Deterministic: - return "jsfqkbnr/pppppppp/8/8/8/8/PPPPPPPP/JSDQKBNR w 0 1111 - -"; + return "jsfqkbnr/pppppppp/8/8/8/8/PPPPPPPP/JSDQKBNR w 0 ahah - -"; let pieces = { w: new Array(8), b: new Array(8) }; + let flags = ""; // Shuffle pieces on first (and last rank if randomness == 2) for (let c of ["w", "b"]) { if (c == 'b' && randomness == 1) { @@ -102,6 +154,7 @@ export const VariantRules = class EightpiecesRules extends ChessRules { pieces['w'].slice(0, lancerIdx) .concat(['g']) .concat(pieces['w'].slice(lancerIdx + 1)); + flags += flags; break; } @@ -140,6 +193,7 @@ export const VariantRules = class EightpiecesRules extends ChessRules { let rookPos = positions[0]; let jailerPos = positions[2]; const kingPos = positions[1]; + flags += V.CoordToColumn(rookPos) + V.CoordToColumn(jailerPos); if (Math.random() < 0.5) [rookPos, jailerPos] = [jailerPos, rookPos]; pieces[c][rookPos] = "r"; @@ -156,56 +210,10 @@ export const VariantRules = class EightpiecesRules extends ChessRules { pieces["b"].join("") + "/pppppppp/8/8/8/8/PPPPPPPP/" + pieces["w"].join("").toUpperCase() + - " w 0 1111 - -" + " w 0 " + flags + " - -" ); } - // Scan kings, rooks and jailers - scanKingsRooks(fen) { - this.INIT_COL_KING = { w: -1, b: -1 }; - this.INIT_COL_ROOK = { w: -1, b: -1 }; - this.INIT_COL_JAILER = { w: -1, b: -1 }; - this.kingPos = { w: [-1, -1], b: [-1, -1] }; - const fenRows = V.ParseFen(fen).position.split("/"); - const startRow = { 'w': V.size.x - 1, 'b': 0 }; - for (let i = 0; i < fenRows.length; i++) { - let k = 0; - for (let j = 0; j < fenRows[i].length; j++) { - switch (fenRows[i].charAt(j)) { - case "k": - this.kingPos["b"] = [i, k]; - this.INIT_COL_KING["b"] = k; - break; - case "K": - this.kingPos["w"] = [i, k]; - this.INIT_COL_KING["w"] = k; - break; - case "r": - if (i == startRow['b'] && this.INIT_COL_ROOK["b"] < 0) - this.INIT_COL_ROOK["b"] = k; - break; - case "R": - if (i == startRow['w'] && this.INIT_COL_ROOK["w"] < 0) - this.INIT_COL_ROOK["w"] = k; - break; - case "j": - if (i == startRow['b'] && this.INIT_COL_JAILER["b"] < 0) - this.INIT_COL_JAILER["b"] = k; - break; - case "J": - if (i == startRow['w'] && this.INIT_COL_JAILER["w"] < 0) - this.INIT_COL_JAILER["w"] = k; - break; - default: { - const num = parseInt(fenRows[i].charAt(j)); - if (!isNaN(num)) k += num - 1; - } - } - k++; - } - } - } - // Is piece on square (x,y) immobilized? isImmobilized([x, y]) { const color = this.getColor(x, y); @@ -267,9 +275,26 @@ export const VariantRules = class EightpiecesRules extends ChessRules { ); } - getPotentialMovesFrom([x,y]) { + getPotentialMovesFrom([x, y]) { // At subTurn == 2, jailers aren't effective (Jeff K) - if (this.subTurn == 1 && !!this.isImmobilized([x, y])) return []; + if (this.subTurn == 1) { + const jsq = this.isImmobilized([x, y]); + if (!!jsq) { + let moves = []; + // Special pass move if king: + if (this.getPiece(x, y) == V.KING) { + moves.push( + new Move({ + appear: [], + vanish: [], + start: { x: x, y: y }, + end: { x: jsq[0], y: jsq[1] } + }) + ); + } + return moves; + } + } if (this.subTurn == 2) { // Temporarily change pushed piece color. // (Not using getPiece() because of lancers) @@ -310,10 +335,11 @@ export const VariantRules = class EightpiecesRules extends ChessRules { // Don't forget to re-add the sentry on the board: // Also fix color of pushed piece afterward: moves.forEach(m => { - m.appear.push({x: x, y: y, p: V.SENTRY, c: color}); - m.appear[0].c = oppCol; + m.appear.unshift({x: x, y: y, p: V.SENTRY, c: color}); + m.appear[1].c = oppCol; m.vanish[0].c = oppCol; }); + this.board[x][y] = saveXYstate; } return moves; } @@ -372,7 +398,7 @@ export const VariantRules = class EightpiecesRules extends ChessRules { // En passant: const Lep = this.epSquares.length; - const epSquare = this.epSquares[Lep - 1]; //always at least one element + const epSquare = this.epSquares[Lep - 1]; if ( !!epSquare && epSquare.x == x + shiftX && @@ -391,8 +417,7 @@ export const VariantRules = class EightpiecesRules extends ChessRules { return moves; } - // Obtain all lancer moves in "step" direction, - // without final re-orientation. + // Obtain all lancer moves in "step" direction getPotentialLancerMoves_aux([x, y], step, tr) { let moves = []; // Add all moves to vacant squares until opponent is met: @@ -423,16 +448,35 @@ export const VariantRules = class EightpiecesRules extends ChessRules { this.sentryPush[L-1][pl-1].y == y ) { // I was pushed: allow all directions (for this move only), but - // do not change direction after moving. + // do not change direction after moving, *except* if I keep the + // same orientation in which I was pushed. const color = this.getColor(x, y); + const curDir = V.LANCER_DIRS[this.board[x][x].charAt(1)]; Object.values(V.LANCER_DIRS).forEach(step => { const dirCode = Object.keys(V.LANCER_DIRS).find(k => { - return (V.LANCER_DIRS[k][0] == step[0] && V.LANCER_DIRS[k][1] == step[1]); + return ( + V.LANCER_DIRS[k][0] == step[0] && + V.LANCER_DIRS[k][1] == step[1] + ); }); - Array.prototype.push.apply( - moves, - this.getPotentialLancerMoves_aux([x, y], step, { p: dirCode, c: color }) - ); + const dirMoves = + this.getPotentialLancerMoves_aux( + [x, y], + step, + { p: dirCode, c: color } + ); + if (curDir[0] == step[0] && curDir[1] == step[1]) { + // Keeping same orientation: can choose after + let chooseMoves = []; + dirMoves.forEach(m => { + Object.keys(V.LANCER_DIRS).forEach(k => { + let mk = JSON.parse(JSON.stringify(m)); + mk.appear[0].p = k; + moves.push(mk); + }); + }); + Array.prototype.push.apply(moves, chooseMoves); + } else Array.prototype.push.apply(moves, dirMoves); }); return moves; } @@ -451,7 +495,24 @@ export const VariantRules = class EightpiecesRules extends ChessRules { }); }); return moves; - } else return monodirMoves; + } else { + // I'm pushed: add potential nudges + let potentialNudges = []; + for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) { + if ( + V.OnBoard(x + step[0], y + step[1]) && + this.board[x + step[0]][y + step[1]] == V.EMPTY + ) { + potentialNudges.push( + this.getBasicMove( + [x, y], + [x + step[0], y + step[1]] + ) + ); + } + } + return monodirMoves.concat(potentialNudges); + } } getPotentialSentryMoves([x, y]) { @@ -475,8 +536,22 @@ export const VariantRules = class EightpiecesRules extends ChessRules { if (m.appear.length == 0) { let res = false; this.play(m); - let moves2 = this.filterValid( - this.getPotentialMovesFrom([m.end.x, m.end.y])); + let potentialMoves = this.getPotentialMovesFrom([m.end.x, m.end.y]); + // Add nudges (if any a priori possible) + for (let step of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) { + if ( + V.OnBoard(m.end.x + step[0], m.end.y + step[1]) && + this.board[m.end.x + step[0]][m.end.y + step[1]] == V.EMPTY + ) { + potentialMoves.push( + this.getBasicMove( + [m.end.x, m.end.y], + [m.end.x + step[0], m.end.y + step[1]] + ) + ); + } + } + let moves2 = this.filterValid(potentialMoves); for (let m2 of moves2) { this.play(m2); res = !this.underCheck(color); @@ -498,23 +573,6 @@ export const VariantRules = class EightpiecesRules extends ChessRules { }); } - getPotentialKingMoves([x, y]) { - let moves = super.getPotentialKingMoves([x, y]); - // Augment with pass move is the king is immobilized: - const jsq = this.isImmobilized([x, y]); - if (!!jsq) { - moves.push( - new Move({ - appear: [], - vanish: [], - start: { x: x, y: y }, - end: { x: jsq[0], y: jsq[1] } - }) - ); - } - return moves; - } - // Adapted: castle with jailer possible getCastleMoves([x, y]) { const c = this.getColor(x, y); @@ -535,9 +593,8 @@ export const VariantRules = class EightpiecesRules extends ChessRules { castleSide < 2; castleSide++ ) { - if (!this.castleFlags[c][castleSide]) continue; + if (this.castleFlags[c][castleSide] >= 8) continue; // Rook (or jailer) and king are on initial position - const finDist = finalSquares[castleSide][0] - y; let step = finDist / Math.max(1, Math.abs(finDist)); i = y; @@ -546,18 +603,14 @@ export const VariantRules = class EightpiecesRules extends ChessRules { this.isAttacked([x, i], [oppCol]) || (this.board[x][i] != V.EMPTY && (this.getColor(x, i) != c || - ![V.KING, V.ROOK].includes(this.getPiece(x, i)))) + ![V.KING, V.ROOK, V.JAILER].includes(this.getPiece(x, i)))) ) { continue castlingCheck; } i += step; } while (i != finalSquares[castleSide][0]); - step = castleSide == 0 ? -1 : 1; - const rookOrJailerPos = - castleSide == 0 - ? Math.min(this.INIT_COL_ROOK[c], this.INIT_COL_JAILER[c]) - : Math.max(this.INIT_COL_ROOK[c], this.INIT_COL_JAILER[c]); + const rookOrJailerPos = this.castleFlags[c][castleSide]; for (i = y + step; i != rookOrJailerPos; i += step) if (this.board[x][i] != V.EMPTY) continue castlingCheck; @@ -595,6 +648,12 @@ export const VariantRules = class EightpiecesRules extends ChessRules { return moves; } + atLeastOneMove() { + // If in second-half of a move, we already know that a move is possible + if (this.subTurn == 2) return true; + return super.atLeastOneMove(); + } + filterValid(moves) { // Disable check tests for sentry pushes, // because in this case the move isn't finished @@ -604,6 +663,9 @@ export const VariantRules = class EightpiecesRules extends ChessRules { if (m.appear.length > 0) movesWithoutSentryPushes.push(m); else movesWithSentryPushes.push(m); }); + + // TODO: if after move a sentry can take king in 2 times?! + const filteredMoves = super.filterValid(movesWithoutSentryPushes); // If at least one full move made, everything is allowed: if (this.movesCount >= 2) @@ -626,97 +688,47 @@ export const VariantRules = class EightpiecesRules extends ChessRules { return this.filterValid(this.getPotentialMovesFrom(sentrySq)); } - updateVariables(move) { - const c = this.turn; - const piece = move.vanish[0].p; - const firstRank = (c == "w" ? V.size.x - 1 : 0); - - // Update king position + flags - if (piece == V.KING) { - this.kingPos[c][0] = move.appear[0].x; - this.kingPos[c][1] = move.appear[0].y; - this.castleFlags[c] = [false, false]; - return; - } - - // Update castling flags if rook or jailer moved (or is captured) - const oppCol = V.GetOppCol(c); - const oppFirstRank = V.size.x - 1 - firstRank; - let flagIdx = 0; - if ( - // Our rook moves? - move.start.x == firstRank && - this.INIT_COL_ROOK[c] == move.start.y - ) { - if (this.INIT_COL_ROOK[c] > this.INIT_COL_JAILER[c]) flagIdx++; - this.castleFlags[c][flagIdx] = false; - } else if ( - // Our jailer moves? - move.start.x == firstRank && - this.INIT_COL_JAILER[c] == move.start.y - ) { - if (this.INIT_COL_JAILER[c] > this.INIT_COL_ROOK[c]) flagIdx++; - this.castleFlags[c][flagIdx] = false; - } else if ( - // We took opponent's rook? - move.end.x == oppFirstRank && - this.INIT_COL_ROOK[oppCol] == move.end.y - ) { - if (this.INIT_COL_ROOK[oppCol] > this.INIT_COL_JAILER[oppCol]) flagIdx++; - this.castleFlags[oppCol][flagIdx] = false; - } else if ( - // We took opponent's jailer? - move.end.x == oppFirstRank && - this.INIT_COL_JAILER[oppCol] == move.end.y - ) { - if (this.INIT_COL_JAILER[oppCol] > this.INIT_COL_ROOK[oppCol]) flagIdx++; - this.castleFlags[oppCol][flagIdx] = false; - } - + prePlay(move) { if (move.appear.length == 0 && move.vanish.length == 1) { // The sentry is about to push a piece: subTurn goes from 1 to 2 this.sentryPos = { x: move.end.x, y: move.end.y }; - } else if (this.subTurn == 2) { + } else if (this.subTurn == 2 && move.vanish[0].p != V.PAWN) { // A piece is pushed: forbid array of squares between start and end // of move, included (except if it's a pawn) let squares = []; - if (move.vanish[0].p != V.PAWN) { - if ([V.KNIGHT,V.KING].includes(move.vanish[0].p)) - // short-range pieces: just forbid initial square - squares.push(move.start); - else { - const deltaX = move.end.x - move.start.x; - const deltaY = move.end.y - move.start.y; - const step = [ - deltaX / Math.abs(deltaX) || 0, - deltaY / Math.abs(deltaY) || 0 - ]; - for ( - let sq = {x: move.start.x, y: move.start.y}; - sq.x != move.end.x && sq.y != move.end.y; - sq.x += step[0], sq.y += step[1] - ) { - squares.push(sq); - } + if ([V.KNIGHT,V.KING].includes(move.vanish[0].p)) + // short-range pieces: just forbid initial square + squares.push({ x: move.start.x, y: move.start.y }); + else { + const deltaX = move.end.x - move.start.x; + const deltaY = move.end.y - move.start.y; + const step = [ + deltaX / Math.abs(deltaX) || 0, + deltaY / Math.abs(deltaY) || 0 + ]; + for ( + let sq = {x: move.start.x, y: move.start.y}; + sq.x != move.end.x && sq.y != move.end.y; + sq.x += step[0], sq.y += step[1] + ) { + squares.push({ x: sq.x, y: sq.y }); } - // Add end square as well, to know if I was pushed (useful for lancers) - squares.push(move.end); } + // Add end square as well, to know if I was pushed (useful for lancers) + squares.push({ x: move.end.x, y: move.end.y }); this.sentryPush.push(squares); } else this.sentryPush.push(null); } - // TODO: cleaner (global) update/unupdate variables logic, rename... - unupdateVariables(move) { - super.unupdateVariables(move); - this.sentryPush.pop(); - } - play(move) { +// if (!this.states) this.states = []; +// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); +// this.states.push(stateFen); + + this.prePlay(move); move.flags = JSON.stringify(this.aggregateFlags()); this.epSquares.push(this.getEpSquare(move)); V.PlayOnBoard(this.board, move); - this.updateVariables(move); // Is it a sentry push? (useful for undo) move.sentryPush = (this.subTurn == 2); if (this.subTurn == 1) this.movesCount++; @@ -726,19 +738,37 @@ export const VariantRules = class EightpiecesRules extends ChessRules { this.turn = V.GetOppCol(this.turn); this.subTurn = 1; } + this.postPlay(move); + } + + postPlay(move) { + if (move.vanish.length == 0) + // Special pass move of the king: nothing to update! + return; + super.postPlay(move); } undo(move) { this.epSquares.pop(); this.disaggregateFlags(JSON.parse(move.flags)); V.UndoOnBoard(this.board, move); - const L = this.sentryPush.length; // Decrement movesCount except if the move is a sentry push if (!move.sentryPush) this.movesCount--; - // Turn changes only if not undoing second part of a sentry push - if (!move.sentryPush || this.subTurn == 1) + if (this.subTurn == 2) this.subTurn = 1; + else { this.turn = V.GetOppCol(this.turn); - this.unupdateVariables(move); + if (move.sentryPush) this.subTurn = 2; + } + this.postUndo(move); + +// const stateFen = this.getBaseFen() + this.getTurnFen() + this.getFlagsFen(); +// if (stateFen != this.states[this.states.length-1]) debugger; +// this.states.pop(); + } + + postUndo(move) { + super.postUndo(move); + this.sentryPush.pop(); } isAttacked(sq, colors) { @@ -763,7 +793,10 @@ export const VariantRules = class EightpiecesRules extends ChessRules { colors.includes(this.getColor(coord.x, coord.y)) ) ) { - lancerPos.push(coord); + if (this.getPiece(coord.x, coord.y) == V.LANCER) + lancerPos.push({x: coord.x, y: coord.y}); + coord.x += step[0]; + coord.y += step[1]; } for (let xy of lancerPos) { const dir = V.LANCER_DIRS[this.board[xy.x][xy.y].charAt(1)]; @@ -780,16 +813,19 @@ export const VariantRules = class EightpiecesRules extends ChessRules { const deltaX = x2 - x1; const deltaY = y2 - y1; const step = [ deltaX / Math.abs(deltaX), deltaY / Math.abs(deltaY) ]; - if (allowedStep.every(st => st[0] != step[0] || st[1] != step[1])) + if (allowedSteps.every(st => st[0] != step[0] || st[1] != step[1])) return false; - let sq = [ x1 = step[0], y1 + step[1] ]; + let sq = [ x1 + step[0], y1 + step[1] ]; while (sq[0] != x2 && sq[1] != y2) { if ( + // NOTE: no need to check OnBoard in this special case (!lancer && this.board[sq[0]][sq[1]] != V.EMPTY) || (!!lancer && this.getColor(sq[0], sq[1]) != color) ) { return false; } + sq[0] += step[0]; + sq[1] += step[1]; } return true; }; @@ -850,7 +886,7 @@ export const VariantRules = class EightpiecesRules extends ChessRules { V.OnBoard(sq[0], sq[1]) && this.getColor(sq[0], sq[1]) == color ) { - candidates.push(sq); + candidates.push([ sq[0], sq[1] ]); } } } @@ -879,14 +915,25 @@ export const VariantRules = class EightpiecesRules extends ChessRules { // TODO: this situation should not happen return null; - const setEval = (move) => { + const setEval = (move, next) => { const score = this.getCurrentScore(); + const curEval = move.eval; if (score != "*") { move.eval = score == "1/2" ? 0 : (score == "1-0" ? 1 : -1) * maxeval; - } else move[i].eval = this.evalPosition(); + } else move.eval = this.evalPosition(); + if ( + // "next" is defined after sentry pushes + !!next && ( + !curEval || + color == 'w' && move.eval > curEval || + color == 'b' && move.eval < curEval + ) + ) { + move.second = next; + } }; // Just search_depth == 1 (because of sentries. TODO: can do better...) @@ -898,7 +945,7 @@ export const VariantRules = class EightpiecesRules extends ChessRules { const moves2 = this.getAllValidMoves(); moves2.forEach(m2 => { this.play(m2); - setEval(m1); + setEval(m1, m2); this.undo(m2); }); } @@ -911,9 +958,12 @@ export const VariantRules = class EightpiecesRules extends ChessRules { let candidates = [0]; for (let j = 1; j < moves1.length && moves1[j].eval == moves1[0].eval; j++) candidates.push(j); - return moves1[candidates[randInt(candidates.length)]]; + const choice = moves1[candidates[randInt(candidates.length)]]; + return (!choice.second ? choice : [choice, choice.second]); } + // TODO: if subTurn == 2, take some precautions, in particular pawn pushed on 1st rank. + // --> should indicate Sxb2,bxc1 getNotation(move) { // Special case "king takes jailer" is a pass move if (move.appear.length == 0 && move.vanish.length == 0) return "pass"; diff --git a/client/src/variants/Extinction.js b/client/src/variants/Extinction.js index 45ace336..3fa53276 100644 --- a/client/src/variants/Extinction.js +++ b/client/src/variants/Extinction.js @@ -93,8 +93,8 @@ export const VariantRules = class ExtinctionRules extends ChessRules { return []; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); // Treat the promotion case: (not the capture part) if (move.appear[0].p != move.vanish[0].p) { this.material[move.appear[0].c][move.appear[0].p]++; @@ -105,8 +105,8 @@ export const VariantRules = class ExtinctionRules extends ChessRules { this.material[move.vanish[1].c][move.vanish[1].p]--; } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); if (move.appear[0].p != move.vanish[0].p) { this.material[move.appear[0].c][move.appear[0].p]--; this.material[move.appear[0].c][V.PAWN]++; diff --git a/client/src/variants/Grand.js b/client/src/variants/Grand.js index b8a0838f..e6333cce 100644 --- a/client/src/variants/Grand.js +++ b/client/src/variants/Grand.js @@ -289,8 +289,8 @@ export const VariantRules = class GrandRules extends ChessRules { ); } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); if (move.vanish.length == 2 && move.appear.length == 1) { // Capture: update this.captured this.captured[move.vanish[1].c][move.vanish[1].p]++; @@ -301,8 +301,8 @@ export const VariantRules = class GrandRules extends ChessRules { } } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); if (move.vanish.length == 2 && move.appear.length == 1) this.captured[move.vanish[1].c][move.vanish[1].p]--; if (move.vanish[0].p != move.appear[0].p) @@ -324,7 +324,7 @@ export const VariantRules = class GrandRules extends ChessRules { if (randomness == 0) { // No castling in the official initial setup return "r8r/1nbqkmcbn1/pppppppppp/10/10/10/10/PPPPPPPPPP/1NBQKMCBN1/R8R " + - "w 0 0000 - 00000000000000"; + "w 0 zzzz - 00000000000000"; } let pieces = { w: new Array(10), b: new Array(10) }; diff --git a/client/src/variants/Grasshopper.js b/client/src/variants/Grasshopper.js index 416f6325..82caa9f4 100644 --- a/client/src/variants/Grasshopper.js +++ b/client/src/variants/Grasshopper.js @@ -139,7 +139,7 @@ export const VariantRules = class GrasshopperRules extends ChessRules { static GenRandInitFen(randomness) { return ChessRules.GenRandInitFen(randomness) - .replace("w 0 1111 -", "w 0 1111") + .slice(0, -2) .replace( "/pppppppp/8/8/8/8/PPPPPPPP/", "/gggggggg/pppppppp/8/8/PPPPPPPP/GGGGGGGG/" diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js index 046fe99e..79c3dfbd 100644 --- a/client/src/variants/Hidden.js +++ b/client/src/variants/Hidden.js @@ -74,7 +74,7 @@ export const VariantRules = class HiddenRules extends ChessRules { } // Scan board for kings positions (no castling) - scanKingsRooks(fen) { + scanKings(fen) { this.kingPos = { w: [-1, -1], b: [-1, -1] }; const fenRows = V.ParseFen(fen).position.split("/"); for (let i = 0; i < fenRows.length; i++) { @@ -225,8 +225,8 @@ export const VariantRules = class HiddenRules extends ChessRules { return []; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); if ( move.vanish.length >= 2 && [V.KING,V.HIDDEN_CODE[V.KING]].includes(move.vanish[1].p) @@ -236,8 +236,8 @@ export const VariantRules = class HiddenRules extends ChessRules { } } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); const c = move.vanish[0].c; const oppCol = V.GetOppCol(c); if (this.kingPos[oppCol][0] < 0) diff --git a/client/src/variants/Hiddenqueen.js b/client/src/variants/Hiddenqueen.js index 7048d41c..15d9b1e9 100644 --- a/client/src/variants/Hiddenqueen.js +++ b/client/src/variants/Hiddenqueen.js @@ -42,36 +42,30 @@ export const VariantRules = class HiddenqueenRules extends ChessRules { const color = move.vanish[0].c; const pawnShift = color == "w" ? -1 : 1; const startRank = color == "w" ? V.size.x - 2 : 1; - const lastRank = color == "w" ? 0 : V.size.x - 1; return ( - // The queen is discovered if she reaches the 8th rank, - // even if this would be a technically valid pawn move. - move.end.x != lastRank && ( + move.end.x - move.start.x == pawnShift && ( - move.end.x - move.start.x == pawnShift && ( - ( - // Normal move - move.end.y == move.start.y && - this.board[move.end.x][move.end.y] == V.EMPTY - ) - || - ( - // Capture - Math.abs(move.end.y - move.start.y) == 1 && - this.board[move.end.x][move.end.y] != V.EMPTY - ) + // Normal move + move.end.y == move.start.y && + this.board[move.end.x][move.end.y] == V.EMPTY + ) + || + ( + // Capture + Math.abs(move.end.y - move.start.y) == 1 && + this.board[move.end.x][move.end.y] != V.EMPTY ) ) - || - ( - // Two-spaces initial jump - move.start.x == startRank && - move.end.y == move.start.y && - move.end.x - move.start.x == 2 * pawnShift && - this.board[move.end.x][move.end.y] == V.EMPTY - ) + ) + || + ( + // Two-spaces initial jump + move.start.x == startRank && + move.end.y == move.start.y && + move.end.x - move.start.x == 2 * pawnShift && + this.board[move.end.x][move.end.y] == V.EMPTY ) ); } @@ -109,7 +103,7 @@ export const VariantRules = class HiddenqueenRules extends ChessRules { ? piece == V.PAWN ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] : [V.QUEEN] //hidden queen revealed - : piece; + : [piece]; //V.PAWN if (this.board[x + shiftX][y] == V.EMPTY) { // One square forward for (let p of finalPieces) { @@ -147,24 +141,22 @@ export const VariantRules = class HiddenqueenRules extends ChessRules { } } - if (V.HasEnpassant) { - // En passant - const Lep = this.epSquares.length; - const epSquare = this.epSquares[Lep - 1]; //always at least one element - if ( - !!epSquare && - epSquare.x == x + shiftX && - Math.abs(epSquare.y - y) == 1 - ) { - let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]); - enpassantMove.vanish.push({ - x: x, - y: epSquare.y, - p: "p", - c: this.getColor(x, epSquare.y) - }); - moves.push(enpassantMove); - } + // En passant + const Lep = this.epSquares.length; + const epSquare = this.epSquares[Lep - 1]; //always at least one element + if ( + !!epSquare && + epSquare.x == x + shiftX && + Math.abs(epSquare.y - y) == 1 + ) { + let enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]); + enpassantMove.vanish.push({ + x: x, + y: epSquare.y, + p: "p", + c: this.getColor(x, epSquare.y) + }); + moves.push(enpassantMove); } return moves; @@ -189,19 +181,18 @@ export const VariantRules = class HiddenqueenRules extends ChessRules { return fen; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); if (move.vanish.length == 2 && move.vanish[1].p == V.KING) // We took opponent king this.kingPos[this.turn] = [-1, -1]; } - unupdateVariables(move) { - super.unupdateVariables(move); - const c = move.vanish[0].c; - const oppCol = V.GetOppCol(c); + preUndo(move) { + super.preUndo(move); + const oppCol = this.turn; if (this.kingPos[oppCol][0] < 0) - // Last move took opponent's king: + // Move takes opponent's king: this.kingPos[oppCol] = [move.vanish[1].x, move.vanish[1].y]; } diff --git a/client/src/variants/Magnetic.js b/client/src/variants/Magnetic.js index 4549e846..a59ce411 100644 --- a/client/src/variants/Magnetic.js +++ b/client/src/variants/Magnetic.js @@ -145,34 +145,37 @@ export const VariantRules = class MagneticRules extends ChessRules { return []; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); const c = move.vanish[0].c; if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) { // We took opponent king ! const oppCol = V.GetOppCol(c); this.kingPos[oppCol] = [-1, -1]; - this.castleFlags[oppCol] = [false, false]; + this.castleFlags[oppCol] = [8, 8]; } // Did we magnetically move our (init) rooks or opponents' ones ? const firstRank = c == "w" ? 7 : 0; const oppFirstRank = 7 - firstRank; const oppCol = V.GetOppCol(c); move.vanish.forEach(psq => { - if (psq.x == firstRank && this.INIT_COL_ROOK[c].includes(psq.y)) - this.castleFlags[c][psq.y == this.INIT_COL_ROOK[c][0] ? 0 : 1] = false; + if ( + psq.x == firstRank && + this.castleFlags[c].includes(psq.y) + ) { + this.castleFlags[c][psq.y == this.castleFlags[c][0] ? 0 : 1] = 8; + } else if ( psq.x == oppFirstRank && - this.INIT_COL_ROOK[oppCol].includes(psq.y) - ) - this.castleFlags[oppCol][ - psq.y == this.INIT_COL_ROOK[oppCol][0] ? 0 : 1 - ] = false; + this.castleFlags[oppCol].includes(psq.y) + ) { + this.castleFlags[oppCol][psq.y == this.castleFlags[oppCol][0] ? 0 : 1] = 8; + } }); } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); const c = move.vanish[0].c; const oppCol = V.GetOppCol(c); if (this.kingPos[oppCol][0] < 0) { diff --git a/client/src/variants/Marseille.js b/client/src/variants/Marseille.js index 121bf015..c3bec32f 100644 --- a/client/src/variants/Marseille.js +++ b/client/src/variants/Marseille.js @@ -34,7 +34,7 @@ export const VariantRules = class MarseilleRules extends ChessRules { if (sq != "-") return V.SquareToCoords(sq); return undefined; })]; - this.scanKingsRooks(fen); + this.scanKings(fen); // Extract subTurn from turn indicator: "w" (first move), or // "w1" or "w2" white subturn 1 or 2, and same for black const fullTurn = V.ParseFen(fen).turn; @@ -158,7 +158,35 @@ export const VariantRules = class MarseilleRules extends ChessRules { } this.subTurn = 3 - this.subTurn; } - this.updateVariables(move); + this.postPlay(move); + } + + postPlay(move) { + const c = move.turn.charAt(0); + const piece = move.vanish[0].p; + const firstRank = c == "w" ? V.size.x - 1 : 0; + + if (piece == V.KING && move.appear.length > 0) { + this.kingPos[c][0] = move.appear[0].x; + this.kingPos[c][1] = move.appear[0].y; + if (V.HasCastle) this.castleFlags[c] = [V.size.y, V.size.y]; + return; + } + const oppCol = V.GetOppCol(c); + const oppFirstRank = V.size.x - 1 - firstRank; + if ( + move.start.x == firstRank && //our rook moves? + 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; + } else if ( + move.end.x == oppFirstRank && //we took opponent rook? + this.castleFlags[oppCol].includes(move.end.y) + ) { + const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1); + this.castleFlags[oppCol][flagIdx] = V.size.y; + } } undo(move) { @@ -176,7 +204,7 @@ export const VariantRules = class MarseilleRules extends ChessRules { } this.turn = move.turn[0]; this.subTurn = parseInt(move.turn[1]); - this.unupdateVariables(move); + super.postUndo(move); } // NOTE: GenRandInitFen() is OK, diff --git a/client/src/variants/Recycle.js b/client/src/variants/Recycle.js index e31badbe..d91e8248 100644 --- a/client/src/variants/Recycle.js +++ b/client/src/variants/Recycle.js @@ -223,31 +223,23 @@ export const VariantRules = class RecycleRules extends ChessRules { return this.getPiece(x2, y2) != V.KING; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); if (move.vanish.length == 2 && move.appear.length == 2) return; //skip castle - const color = V.GetOppCol(this.turn); - if (move.vanish.length == 0) { - this.reserve[color][move.appear[0].p]--; - return; - } - else if (move.vanish.length == 2 && move.vanish[1].c == color) { + const color = move.appear[0].c; + if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]--; + else if (move.vanish.length == 2 && move.vanish[1].c == color) // Self-capture this.reserve[color][move.vanish[1].p]++; - } } - unupdateVariables(move) { - super.unupdateVariables(move); + postUndo(move) { + super.postUndo(move); if (move.vanish.length == 2 && move.appear.length == 2) return; const color = this.turn; - if (move.vanish.length == 0) { - this.reserve[color][move.appear[0].p]++; - return; - } - else if (move.vanish.length == 2 && move.vanish[1].c == color) { + if (move.vanish.length == 0) this.reserve[color][move.appear[0].p]++; + else if (move.vanish.length == 2 && move.vanish[1].c == color) this.reserve[color][move.vanish[1].p]--; - } } static get SEARCH_DEPTH() { diff --git a/client/src/variants/Shatranj.js b/client/src/variants/Shatranj.js index b45d915a..c364b816 100644 --- a/client/src/variants/Shatranj.js +++ b/client/src/variants/Shatranj.js @@ -19,7 +19,8 @@ export const VariantRules = class ShatranjRules extends ChessRules { } static GenRandInitFen(randomness) { - return ChessRules.GenRandInitFen(randomness).replace("w 1111 -", "w"); + // Remove castle flags and en-passant indication + return ChessRules.GenRandInitFen(randomness).slice(0, -7); } getPotentialPawnMoves([x, y]) { diff --git a/client/src/variants/Suction.js b/client/src/variants/Suction.js index 0fc88ce4..9ac5b442 100644 --- a/client/src/variants/Suction.js +++ b/client/src/variants/Suction.js @@ -1,11 +1,18 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; export const VariantRules = class SuctionRules extends ChessRules { + static get HasFlags() { + return false; + } + setOtherVariables(fen) { + +console.log(fen); + super.setOtherVariables(fen); - // Local stack of captures + // Local stack of "captures" this.cmoves = []; - const cmove = fen.split(" ")[5]; + const cmove = V.ParseFen(fen).cmove; if (cmove == "-") this.cmoves.push(null); else { this.cmoves.push({ @@ -15,6 +22,12 @@ export const VariantRules = class SuctionRules extends ChessRules { } } + static ParseFen(fen) { + return Object.assign({}, ChessRules.ParseFen(fen), { + cmove: fen.split(" ")[4] + }); + } + static IsGoodFen(fen) { if (!ChessRules.IsGoodFen(fen)) return false; const fenParts = fen.split(" "); @@ -171,27 +184,9 @@ export const VariantRules = class SuctionRules extends ChessRules { }); } - updateVariables(move) { - super.updateVariables(move); - if (move.vanish.length == 2) { - // Was opponent king swapped? - if (move.vanish[1].p == V.KING) - this.kingPos[this.turn] = [move.appear[1].x, move.appear[1].y]; - } - } - - unupdateVariables(move) { - super.unupdateVariables(move); - if (move.appear.length == 2) { - // Was opponent king swapped? - if (move.appear[1].p == V.KING) - this.kingPos[move.vanish[1].c] = [move.vanish[1].x,move.vanish[1].y]; - } - } - static GenRandInitFen(randomness) { // Add empty cmove: - return ChessRules.GenRandInitFen(randomness) + " -"; + return ChessRules.GenRandInitFen(randomness).slice(0, -6) + "- -"; } getFen() { @@ -203,14 +198,23 @@ export const VariantRules = class SuctionRules extends ChessRules { return super.getFen() + " " + cmoveFen; } - play(move) { + postPlay(move) { + super.postPlay(move); + if (move.vanish.length == 2) { + // Was opponent king swapped? + if (move.vanish[1].p == V.KING) + this.kingPos[this.turn] = [move.appear[1].x, move.appear[1].y]; + } this.cmoves.push(this.getCmove(move)); - super.play(move); } - undo(move) { + postUndo(move) { + super.postUndo(move); + if (move.appear.length == 2) { + if (move.appear[1].p == V.KING) + this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y]; + } this.cmoves.pop(); - super.undo(move); } atLeastOneMove() { diff --git a/client/src/variants/Suicide.js b/client/src/variants/Suicide.js index f030b080..24e8ef29 100644 --- a/client/src/variants/Suicide.js +++ b/client/src/variants/Suicide.js @@ -114,8 +114,10 @@ export const VariantRules = class SuicideRules extends ChessRules { } // No variables update because no royal king + no castling - updateVariables() {} - unupdateVariables() {} + prePlay() {} + postPlay() {} + preUndo() {} + postUndo() {} getCurrentScore() { if (this.atLeastOneMove()) return "*"; diff --git a/client/src/variants/Threechecks.js b/client/src/variants/Threechecks.js index f3b77254..3aa6db98 100644 --- a/client/src/variants/Threechecks.js +++ b/client/src/variants/Threechecks.js @@ -32,8 +32,8 @@ export const VariantRules = class ThreechecksRules extends ChessRules { return b; } - updateVariables(move) { - super.updateVariables(move); + postPlay(move) { + super.postPlay(move); // Does this move give check? const oppCol = this.turn; if (this.underCheck(oppCol)) @@ -48,16 +48,14 @@ export const VariantRules = class ThreechecksRules extends ChessRules { } static GenRandInitFen(randomness) { - return ChessRules.GenRandInitFen(randomness) - // Add check flags (at 0) - .replace(" w 0 1111", " w 0 111100"); + // Add check flags (at 0) + return ChessRules.GenRandInitFen(randomness).slice(0, -2) + "00"; } getFlagsFen() { let fen = super.getFlagsFen(); // Add check flags - for (let c of ["w", "b"]) - fen += this.checkFlags[c]; + for (let c of ["w", "b"]) fen += this.checkFlags[c]; return fen; } diff --git a/client/src/variants/Wildebeest.js b/client/src/variants/Wildebeest.js index 565f6275..bf5c178b 100644 --- a/client/src/variants/Wildebeest.js +++ b/client/src/variants/Wildebeest.js @@ -247,12 +247,14 @@ export const VariantRules = class WildebeestRules extends ChessRules { static GenRandInitFen(randomness) { if (!randomness) randomness = 2; if (randomness == 0) - return "rnccwkqbbnr/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/RNBBQKWCCNR w 0 1111 -"; + return "rnccwkqbbnr/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/RNBBQKWCCNR w 0 akak -"; let pieces = { w: new Array(10), b: new Array(10) }; + let flags = ""; for (let c of ["w", "b"]) { if (c == 'b' && randomness == 1) { pieces['b'] = pieces['w']; + flags += flags; break; } @@ -308,12 +310,13 @@ export const VariantRules = class WildebeestRules extends ChessRules { pieces[c][bishop2Pos] = "b"; pieces[c][knight2Pos] = "n"; pieces[c][rook2Pos] = "r"; + flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos); } return ( pieces["b"].join("") + "/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/" + pieces["w"].join("").toUpperCase() + - " w 0 1111 -" + " w 0 " + flags + " -" ); } }; diff --git a/client/src/variants/Wormhole.js b/client/src/variants/Wormhole.js index 04699b25..462cb3bf 100644 --- a/client/src/variants/Wormhole.js +++ b/client/src/variants/Wormhole.js @@ -278,6 +278,10 @@ export const VariantRules = class WormholeRules extends ChessRules { return this.turn == "w" ? "0-1" : "1-0"; } + static get SEARCH_DEPTH() { + return 2; + } + evalPosition() { let evaluation = 0; for (let i = 0; i < V.size.x; i++) { diff --git a/client/src/views/Analyse.vue b/client/src/views/Analyse.vue index e829ae4f..f662d614 100644 --- a/client/src/views/Analyse.vue +++ b/client/src/views/Analyse.vue @@ -96,11 +96,10 @@ export default { }, adjustFenSize: function() { let fenInput = document.getElementById("fen"); - fenInput.style.width = this.curFen.length + "ch"; + fenInput.style.width = (this.curFen.length+1) + "ch"; }, tryGotoFen: function() { - if (V.IsGoodFen(this.curFen)) - { + if (V.IsGoodFen(this.curFen)) { this.gameRef.fen = this.curFen; this.loadGame(); } diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index 18a28366..cb1b720d 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -938,14 +938,9 @@ export default { if (game.score == "*") { // Set clocks + initime game.initime = [0, 0]; - if (L >= 1) { - const gameLastupdate = game.moves[L-1].played; - game.initime[L % 2] = gameLastupdate; - if (L >= 2) { - game.clocks[L % 2] = - tc.mainTime - (Date.now() - gameLastupdate) / 1000; - } - } + if (L >= 1) game.initime[L % 2] = game.moves[L-1].played; + // NOTE: game.clocks shouldn't be computed right now: + // job will be done in re_setClocks() called soon below. } // Sort chat messages from newest to oldest game.chats.sort((c1, c2) => { -- 2.44.0