From 241bf8f2a9a2c48d793aeb0b1d20207f6371de70 Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Mon, 24 Feb 2020 01:24:14 +0100 Subject: [PATCH] Fix SuctionChess, draft HiddenRules (unfinished) --- client/src/base_rules.js | 14 +- client/src/components/Board.vue | 8 +- client/src/translations/en.js | 3 +- client/src/translations/es.js | 3 +- client/src/translations/fr.js | 3 +- client/src/translations/rules/Hidden/en.pug | 26 +++ client/src/translations/rules/Hidden/es.pug | 23 +++ client/src/translations/rules/Hidden/fr.pug | 23 +++ client/src/translations/rules/Suction/en.pug | 8 +- client/src/translations/rules/Suction/es.pug | 8 +- client/src/translations/rules/Suction/fr.pug | 8 +- client/src/utils/printDiagram.js | 3 +- client/src/variants/Alice.js | 8 +- client/src/variants/Antiking.js | 8 +- client/src/variants/Baroque.js | 10 +- client/src/variants/Checkered.js | 14 +- client/src/variants/Crazyhouse.js | 2 +- client/src/variants/Dark.js | 38 +--- client/src/variants/Grand.js | 8 +- client/src/variants/Hidden.js | 185 +++++++++++++++++++ client/src/variants/Recycle.js | 2 +- client/src/variants/Suction.js | 80 +++++++- client/src/variants/Wildebeest.js | 8 +- server/db/populate.sql | 1 + 24 files changed, 410 insertions(+), 84 deletions(-) create mode 100644 client/src/translations/rules/Hidden/en.pug create mode 100644 client/src/translations/rules/Hidden/es.pug create mode 100644 client/src/translations/rules/Hidden/fr.pug create mode 100644 client/src/variants/Hidden.js diff --git a/client/src/base_rules.js b/client/src/base_rules.js index 7e4057bb..7949d7f6 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -53,11 +53,6 @@ export const ChessRules = class ChessRules { return "all"; } - // Path to pieces - static getPpath(b) { - return b; //usual pieces in pieces/ folder - } - // Turn "wb" into "B" (for FEN) static board2fen(b) { return b[0] == "w" ? b[1].toUpperCase() : b[1]; @@ -160,6 +155,11 @@ export const ChessRules = class ChessRules { return V.CoordToColumn(coords.y) + (V.size.x - coords.x); } + // Path to pieces + getPpath(b) { + return b; //usual pieces in pieces/ folder + } + // Aggregates flags into one object aggregateFlags() { return this.castleFlags; @@ -379,7 +379,9 @@ export const ChessRules = class ChessRules { // INITIALIZATION constructor(fen) { - this.re_init(fen); + // In printDiagram() fen isn't supply because only getPpath() is used + if (fen) + this.re_init(fen); } // Fen string fully describes the game state diff --git a/client/src/components/Board.vue b/client/src/components/Board.vue index 62680021..d4423464 100644 --- a/client/src/components/Board.vue +++ b/client/src/components/Board.vue @@ -88,7 +88,7 @@ export default { attrs: { src: "/images/pieces/" + - V.getPpath(this.vr.board[ci][cj]) + + this.vr.getPpath(this.vr.board[ci][cj], this.userColor, this.score) + ".svg" } }) @@ -154,7 +154,7 @@ export default { attrs: { src: "/images/pieces/" + - this.vr.getReservePpath(playingColor, i) + + this.vr.getReservePpath(i, playingColor) + ".svg" } }), @@ -181,7 +181,7 @@ export default { attrs: { src: "/images/pieces/" + - this.vr.getReservePpath(oppCol, i) + + this.vr.getReservePpath(i, oppCol) + ".svg" } }), @@ -255,7 +255,7 @@ export default { attrs: { src: "/images/pieces/" + - V.getPpath(m.appear[0].c + m.appear[0].p) + + this.vr.getPpath(m.appear[0].c + m.appear[0].p) + ".svg" }, class: { "choice-piece": true }, diff --git a/client/src/translations/en.js b/client/src/translations/en.js index d1759ebc..920e19cb 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -148,5 +148,6 @@ export const translations = { "Reuse pieces": "Reuse pieces", "Reverse captures": "Reverse captures", "Shared pieces": "Shared pieces", - "Standard rules": "Standard rules" + "Standard rules": "Standard rules", + "Unidentified pieces": "Unidentified pieces" }; diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 3698845f..d3ead1a0 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -148,5 +148,6 @@ export const translations = { "Reuse pieces": "Reutilizar piezas", "Reverse captures": "Capturas invertidas", "Shared pieces": "Piezas compartidas", - "Standard rules": "Reglas estandar" + "Standard rules": "Reglas estandar", + "Unidentified pieces": "Piezas no identificadas" }; diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 647c35e7..5abf762c 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -148,5 +148,6 @@ export const translations = { "Reuse pieces": "Réutiliser les pièces", "Reverse captures": "Captures inversées", "Shared pieces": "Pièces partagées", - "Standard rules": "Règles usuelles" + "Standard rules": "Règles usuelles", + "Unidentified pieces": "Pièces non identifiées" }; diff --git a/client/src/translations/rules/Hidden/en.pug b/client/src/translations/rules/Hidden/en.pug new file mode 100644 index 00000000..f98808f9 --- /dev/null +++ b/client/src/translations/rules/Hidden/en.pug @@ -0,0 +1,26 @@ +p.boxed + | If a piece captures one of the same kind, both disappear. + +// TODO: + // Due to random 2 ranks: no castling (would reveal too much) + // To not reveal much, also no en passant + +p. + The defensive power of pawns is thus increased, because they don't fear + captures (by other pawns). + +p. + Endings are also affected quite a lot, and sometimes new threats occur: + on the diagram, 3.Bxg7 wins a pawn because 3...Bxg7 would make both + bishops disappear. + +figure.diagram-container + .diagram + | fen:r1bqkbnr/pp1ppppp/2n5/2p5/8/1P6/PBPPPPPP/RN1QKBNR: + figcaption After 1.b3 c5 2.Bb2 Nc6 + +h3 Source + +p + a(href="https://www.chessvariants.com/rules/strate-go-chess") Strate-Go chess + |  on chessvariants.com. diff --git a/client/src/translations/rules/Hidden/es.pug b/client/src/translations/rules/Hidden/es.pug new file mode 100644 index 00000000..b6adc808 --- /dev/null +++ b/client/src/translations/rules/Hidden/es.pug @@ -0,0 +1,23 @@ +p.boxed + | Si una pieza captura otra del mismo tipo, las dos desaparecen. + +p. + El poder defensivo de los peones aumenta así, ya que no temen + más capturas (por otros peones). + +p. + Las finales también se ven muy afectadas y, a veces, nuevas amenazas + ocurren: en el diagrama, 3.Bxg7 gana un peón porque 3...Bxg7 causaría + la desaparición de los dos alfiles. + +figure.diagram-container + .diagram + | fen:r1bqkbnr/pp1ppppp/2n5/2p5/8/1P6/PBPPPPPP/RN1QKBNR: + figcaption Después de 1.b3 c5 2.Bb2 Nc6 + +h3 Fuente + +p + | La + a(href="https://www.chessvariants.com/rules/antimatter-chess") variante Antimateria + |  en chessvariants.com. diff --git a/client/src/translations/rules/Hidden/fr.pug b/client/src/translations/rules/Hidden/fr.pug new file mode 100644 index 00000000..7b1cdfae --- /dev/null +++ b/client/src/translations/rules/Hidden/fr.pug @@ -0,0 +1,23 @@ +p.boxed + | Si une pièce en capture une autre du même type, les deux disparaissent. + +p. + Le pouvoir défensif des pions est ainsi augmenté, puisqu'ils ne craignent + plus les captures (par d'autres pions). + +p. + Les finales sont aussi beaucoup affectées, et parfois de nouvelles menaces + surviennent : sur le diagramme, 3.Bxg7 gagne un pion car 3...Bxg7 provoquerait + la disparition des deux fous. + +figure.diagram-container + .diagram + | fen:r1bqkbnr/pp1ppppp/2n5/2p5/8/1P6/PBPPPPPP/RN1QKBNR: + figcaption After 1.b3 c5 2.Bb2 Nc6 + +h3 Source + +p + | La + a(href="https://www.chessvariants.com/rules/antimatter-chess") variante Antimatière + |  sur chessvariants.com. diff --git a/client/src/translations/rules/Suction/en.pug b/client/src/translations/rules/Suction/en.pug index 60ccf6ae..36ca0943 100644 --- a/client/src/translations/rules/Suction/en.pug +++ b/client/src/translations/rules/Suction/en.pug @@ -5,10 +5,14 @@ p. When a piece is taken it is not removed from the board, but moved instead to the square that was initially occupied by the capturing piece. +p. + It is forbidden to "un-do" a capture right after it was played: + in the diagram position, 3...Bxg7 is not allowed. + figure.diagram-container .diagram - | fen:rnbqkb1r/ppp1pppp/5B2/3p4/8/1P6/PnPPPPPP/RN1QKBNR: - figcaption After 1.b3 d5 2.Bb2 Nf6 3.Bxf6 + | fen:rnbqk1nr/ppppppBp/6p1/8/8/1P6/PbPPPPPP/RN1QKBNR: + figcaption After 1.b3 g6 2.Bb2 Bg7 3.Bxg7 ul li. diff --git a/client/src/translations/rules/Suction/es.pug b/client/src/translations/rules/Suction/es.pug index 685b7552..3bc9e275 100644 --- a/client/src/translations/rules/Suction/es.pug +++ b/client/src/translations/rules/Suction/es.pug @@ -6,10 +6,14 @@ p. Cuando se captura una pieza, no se retira del tablero, sino solo se movió a la casilla originalmente ocupada por la pieza que capturó. +p. + Está prohibido "deshacer" una captura inmediatamente después de que se + haya jugado: en la posición del diagrama, 3...Bxg7 no está permitido. + figure.diagram-container .diagram - | fen:rnbqkb1r/ppp1pppp/5B2/3p4/8/1P6/PnPPPPPP/RN1QKBNR: - figcaption Después de 1.b3 d5 2.Bb2 Nf6 3.Bxf6 + | fen:rnbqk1nr/ppppppBp/6p1/8/8/1P6/PbPPPPPP/RN1QKBNR: + figcaption Después de 1.b3 g6 2.Bb2 Bg7 3.Bxg7 ul li. diff --git a/client/src/translations/rules/Suction/fr.pug b/client/src/translations/rules/Suction/fr.pug index 2097062b..931517ef 100644 --- a/client/src/translations/rules/Suction/fr.pug +++ b/client/src/translations/rules/Suction/fr.pug @@ -6,10 +6,14 @@ p. Quand une pièce est capturée elle n'est pas retirée de l'échiquier, mais seulement déplacée sur la case initialement occupée par la pièce capturante. +p. + Il est interdit de "défaire" une capture juste après qu'elle a été jouée : + dans la position du diagramme, 3...Bxg7 n'est pas autorisé. + figure.diagram-container .diagram - | fen:rnbqkb1r/ppp1pppp/5B2/3p4/8/1P6/PnPPPPPP/RN1QKBNR: - figcaption Après 1.b3 d5 2.Bb2 Nf6 3.Bxf6 + | fen:rnbqk1nr/ppppppBp/6p1/8/8/1P6/PbPPPPPP/RN1QKBNR: + figcaption Après 1.b3 g6 2.Bb2 Bg7 3.Bxg7 ul li. diff --git a/client/src/utils/printDiagram.js b/client/src/utils/printDiagram.js index aebdc247..d4562bf2 100644 --- a/client/src/utils/printDiagram.js +++ b/client/src/utils/printDiagram.js @@ -70,6 +70,7 @@ export function getDiagram(args) { const orientation = args.orientation || "w"; const markArray = getMarkArray(args.marks); const shadowArray = getShadowArray(args.shadow); + const vr = new V(); //just for pieces images paths let boardDiv = ""; const [startX, startY, inc] = orientation == "w" ? [0, 0, 1] : [V.size.x - 1, V.size.y - 1, -1]; @@ -87,7 +88,7 @@ export function getDiagram(args) { boardDiv += ""; } diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js index e7daf7f8..b16d728e 100644 --- a/client/src/variants/Alice.js +++ b/client/src/variants/Alice.js @@ -25,14 +25,14 @@ export const VariantRules = class AliceRules extends ChessRules { }; } - static getPpath(b) { - return (Object.keys(this.ALICE_PIECES).includes(b[1]) ? "Alice/" : "") + b; - } - static get PIECES() { return ChessRules.PIECES.concat(Object.keys(V.ALICE_PIECES)); } + getPpath(b) { + return (Object.keys(this.ALICE_PIECES).includes(b[1]) ? "Alice/" : "") + b; + } + setOtherVariables(fen) { super.setOtherVariables(fen); const rows = V.ParseFen(fen).position.split("/"); diff --git a/client/src/variants/Antiking.js b/client/src/variants/Antiking.js index 3b1b36e3..f07f6268 100644 --- a/client/src/variants/Antiking.js +++ b/client/src/variants/Antiking.js @@ -3,10 +3,6 @@ import { ArrayFun } from "@/utils/array"; import { randInt } from "@/utils/alea"; export const VariantRules = class AntikingRules extends ChessRules { - static getPpath(b) { - return b[1] == "a" ? "Antiking/" + b : b; - } - static get ANTIKING() { return "a"; } @@ -15,6 +11,10 @@ export const VariantRules = class AntikingRules extends ChessRules { return ChessRules.PIECES.concat([V.ANTIKING]); } + getPpath(b) { + return b[1] == "a" ? "Antiking/" + b : b; + } + setOtherVariables(fen) { super.setOtherVariables(fen); this.antikingPos = { w: [-1, -1], b: [-1, -1] }; diff --git a/client/src/variants/Baroque.js b/client/src/variants/Baroque.js index 6557b380..8f1f9ba0 100644 --- a/client/src/variants/Baroque.js +++ b/client/src/variants/Baroque.js @@ -11,17 +11,17 @@ export const VariantRules = class BaroqueRules extends ChessRules { return false; } - static getPpath(b) { + static get PIECES() { + return ChessRules.PIECES.concat([V.IMMOBILIZER]); + } + + getPpath(b) { if (b[1] == "m") //'m' for Immobilizer (I is too similar to 1) return "Baroque/" + b; return b; //usual piece } - static get PIECES() { - return ChessRules.PIECES.concat([V.IMMOBILIZER]); - } - // No castling, but checks, so keep track of kings setOtherVariables(fen) { this.kingPos = { w: [-1, -1], b: [-1, -1] }; diff --git a/client/src/variants/Checkered.js b/client/src/variants/Checkered.js index 0e4f0a0d..d477a6d0 100644 --- a/client/src/variants/Checkered.js +++ b/client/src/variants/Checkered.js @@ -1,10 +1,6 @@ import { ChessRules } from "@/base_rules"; export const VariantRules = class CheckeredRules extends ChessRules { - static getPpath(b) { - return b[0] == "c" ? "Checkered/" + b : b; - } - static board2fen(b) { const checkered_codes = { p: "s", @@ -40,6 +36,10 @@ export const VariantRules = class CheckeredRules extends ChessRules { return ChessRules.PIECES.concat(["s", "t", "u", "c", "o"]); } + getPpath(b) { + return b[0] == "c" ? "Checkered/" + b : b; + } + setOtherVariables(fen) { super.setOtherVariables(fen); // Local stack of non-capturing checkered moves: @@ -130,7 +130,7 @@ export const VariantRules = class CheckeredRules extends ChessRules { } } if (m.vanish.length == 1) moves.push(m); - //no capture + // No capture else { // A capture occured (m.vanish.length == 2) m.appear[0].c = "c"; @@ -156,7 +156,7 @@ export const VariantRules = class CheckeredRules extends ChessRules { // Does m2 un-do m1 ? (to disallow undoing checkered moves) oppositeMoves(m1, m2) { return ( - !!m1 && + m1 && m2.appear[0].c == "c" && m2.appear.length == 1 && m2.vanish.length == 1 && @@ -170,8 +170,8 @@ export const VariantRules = class CheckeredRules extends ChessRules { filterValid(moves) { if (moves.length == 0) return []; const color = this.turn; + const L = this.cmoves.length; //at least 1: init from FEN return moves.filter(m => { - const L = this.cmoves.length; //at least 1: init from FEN if (this.oppositeMoves(this.cmoves[L - 1], m)) return false; this.play(m); const res = !this.underCheck(color); diff --git a/client/src/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js index d7d82b8c..24018d14 100644 --- a/client/src/variants/Crazyhouse.js +++ b/client/src/variants/Crazyhouse.js @@ -104,7 +104,7 @@ export const VariantRules = class CrazyhouseRules extends ChessRules { } // Used by the interface: - getReservePpath(color, index) { + getReservePpath(index, color) { return color + V.RESERVE_PIECES[index]; } diff --git a/client/src/variants/Dark.js b/client/src/variants/Dark.js index d6515efd..3944ca16 100644 --- a/client/src/variants/Dark.js +++ b/client/src/variants/Dark.js @@ -46,9 +46,7 @@ export const VariantRules = class DarkRules extends ChessRules { V.OnBoard(i + pawnShift[color], j + shiftY) && this.board[i + pawnShift[color]][j + shiftY] == V.EMPTY ) { - this.enlightened[color][i + pawnShift[color]][ - j + shiftY - ] = true; + this.enlightened[color][i + pawnShift[color]][j + shiftY] = true; } } } @@ -83,25 +81,15 @@ export const VariantRules = class DarkRules extends ChessRules { return potentialMoves; //because there are no checks } - atLeastOneMove() { - if (this.kingPos[this.turn][0] < 0) return false; - return true; //TODO: is it right? - } - - underCheck() { - return false; //there is no check - } - getCheckSquares() { return []; } updateVariables(move) { super.updateVariables(move); - if (move.vanish.length >= 2 && move.vanish[1].p == V.KING) { - // We took opponent king ! (because if castle vanish[1] is a rook) + 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]; - } // Update lights for both colors: this.updateEnlightened(); @@ -111,15 +99,9 @@ export const VariantRules = class DarkRules extends ChessRules { super.unupdateVariables(move); const c = move.vanish[0].c; const oppCol = V.GetOppCol(c); - if (this.kingPos[oppCol][0] < 0) { - // Last move took opponent's king - for (let psq of move.vanish) { - if (psq.p == "k") { - this.kingPos[oppCol] = [psq.x, psq.y]; - break; - } - } - } + if (this.kingPos[oppCol][0] < 0) + // Last move took opponent's king: + this.kingPos[oppCol] = [move.vanish[1].x, move.vanish[1].y]; // Update lights for both colors: this.updateEnlightened(); @@ -129,12 +111,10 @@ export const VariantRules = class DarkRules extends ChessRules { const color = this.turn; const kp = this.kingPos[color]; if (kp[0] < 0) - //king disappeared + // King disappeared return color == "w" ? "0-1" : "1-0"; - if (this.atLeastOneMove()) - // game not over - return "*"; - return "1/2"; //no moves but kings still there (seems impossible) + // Assume that stalemate is impossible (I think so. Would need proof...) + return "*"; } static get THRESHOLD_MATE() { diff --git a/client/src/variants/Grand.js b/client/src/variants/Grand.js index c4e2eb1d..d804f614 100644 --- a/client/src/variants/Grand.js +++ b/client/src/variants/Grand.js @@ -5,10 +5,6 @@ import { randInt } from "@/utils/alea"; // NOTE: initial setup differs from the original; see // https://www.chessvariants.com/large.dir/freeling.html export const VariantRules = class GrandRules extends ChessRules { - static getPpath(b) { - return ([V.MARSHALL, V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b; - } - static IsGoodFen(fen) { if (!ChessRules.IsGoodFen(fen)) return false; const fenParsed = V.ParseFen(fen); @@ -35,6 +31,10 @@ export const VariantRules = class GrandRules extends ChessRules { return Object.assign(ChessRules.ParseFen(fen), { captured: fenParts[5] }); } + getPpath(b) { + return ([V.MARSHALL, V.CARDINAL].includes(b[1]) ? "Grand/" : "") + b; + } + getFen() { return super.getFen() + " " + this.getCapturedFen(); } diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js new file mode 100644 index 00000000..1836352a --- /dev/null +++ b/client/src/variants/Hidden.js @@ -0,0 +1,185 @@ +import { ChessRules } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { randInt } from "@/utils/alea"; + +export const VariantRules = class HiddenRules extends ChessRules { + static get HasFlags() { + return false; + } + + static get HasEnpassant() { + return false; + } + + // Analyse in Hidden mode makes no sense + static get CanAnalyze() { + return false; + } + + // Moves are revealed only when game ends + static get ShowMoves() { + return "none"; + } + + static get HIDDEN_DECODE() { + return { + s: "p", + t: "q", + u: "r", + c: "b", + o: "n", + l: "k" + }; + } + static get HIDDEN_CODE() { + return { + p: "s", + q: "t", + r: "u", + b: "c", + n: "o", + k: "l" + }; + } + + static get PIECES() { + return ChessRules.PIECES.concat(Object.values(V.HIDDEN_CODE)); + } + + // Scan board for kings positions (no castling) + scanKingsRooks(fen) { + this.kingPos = { w: [-1, -1], b: [-1, -1] }; + const fenRows = V.ParseFen(fen).position.split("/"); + for (let i = 0; i < fenRows.length; i++) { + let k = 0; //column index on board + for (let j = 0; j < fenRows[i].length; j++) { + switch (fenRows[i].charAt(j)) { + case "k": + case "l": + this.kingPos["b"] = [i, k]; + break; + case "K": + case "L": + this.kingPos["w"] = [i, k]; + break; + default: { + const num = parseInt(fenRows[i].charAt(j)); + if (!isNaN(num)) k += num - 1; + } + } + k++; + } + } + } + + getPpath(b, color, score) { + if (Object.keys(V.HIDDEN_DECODE).includes(b[1])) { + // Supposed to be hidden. + if (score == "*" && (!color || color != b[0])) + return "Hidden/" + b[0] + "p"; + // Else: condition OK to show the piece + return b[0] + V.HIDDEN_DECODE[b[1]]; + } + // The piece is already not supposed to be hidden: + return b; + } + + //getPotentialMovesFrom: TODO: write + + // TODO: bishops on different colors, a1 --> h1, h2 --> a2 ? + static GenRandInitFen() { + let pieces = { w: new Array(8), b: new Array(8) }; + // Shuffle pieces + pawns on two first ranks + for (let c of ["w", "b"]) { + let positions = ArrayFun.range(16); + + // Get random squares for bishops + let randIndex = 2 * randInt(8); + const bishop1Pos = positions[randIndex]; + // The second bishop must be on a square of different color + let randIndex_tmp = 2 * randInt(8) + 1; + const bishop2Pos = positions[randIndex_tmp]; + // Remove chosen squares + positions.splice(Math.max(randIndex, randIndex_tmp), 1); + positions.splice(Math.min(randIndex, randIndex_tmp), 1); + + // Get random squares for knights + randIndex = randInt(14); + const knight1Pos = positions[randIndex]; + positions.splice(randIndex, 1); + randIndex = randInt(13); + const knight2Pos = positions[randIndex]; + positions.splice(randIndex, 1); + + // Get random square for queen + randIndex = randInt(12); + const queenPos = positions[randIndex]; + positions.splice(randIndex, 1); + + // Get random squares for pawns + // TODO... + + // Rooks and king positions are now fixed, + // because of the ordering rook-king-rook + const rook1Pos = positions[0]; + const kingPos = positions[1]; + const rook2Pos = positions[2]; + + // Finally put the shuffled pieces in the board array + pieces[c][rook1Pos] = "r"; + pieces[c][knight1Pos] = "n"; + pieces[c][bishop1Pos] = "b"; + pieces[c][queenPos] = "q"; + pieces[c][kingPos] = "k"; + pieces[c][bishop2Pos] = "b"; + pieces[c][knight2Pos] = "n"; + pieces[c][rook2Pos] = "r"; + } + return ( + pieces["b"].join("") + + "/pppppppp/8/8/8/8/PPPPPPPP/" + + pieces["w"].join("").toUpperCase() + + " w 0" + ); + } + + getCheckSquares() { + return []; + } + + updateVariables(move) { + super.updateVariables(move); + if ( + move.vanish.length >= 2 && + [V.KING,V.HIDDEN_CODE[V.KING]].includes(move.vanish[1].p) + ) { + // 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); + if (this.kingPos[oppCol][0] < 0) + // Last move took opponent's king: + this.kingPos[oppCol] = [move.vanish[1].x, move.vanish[1].y]; + } + + getCurrentScore() { + const color = this.turn; + const kp = this.kingPos[color]; + if (kp[0] < 0) + // King disappeared + return color == "w" ? "0-1" : "1-0"; + // Assume that stalemate is impossible: + return "*"; + } + + getComputerMove() { + // Just return a random move. TODO: something smarter... + const moves = this.getAllValidMoves(); + return moves[randInt(moves.length)]; + } +}; diff --git a/client/src/variants/Recycle.js b/client/src/variants/Recycle.js index d99228cd..50ccd74e 100644 --- a/client/src/variants/Recycle.js +++ b/client/src/variants/Recycle.js @@ -74,7 +74,7 @@ export const VariantRules = class RecycleRules extends ChessRules { } // Used by the interface: - getReservePpath(color, index) { + getReservePpath(index, color) { return color + V.RESERVE_PIECES[index]; } diff --git a/client/src/variants/Suction.js b/client/src/variants/Suction.js index bfb9fa00..38ee5c2e 100644 --- a/client/src/variants/Suction.js +++ b/client/src/variants/Suction.js @@ -1,6 +1,35 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; -export const VariantRules = class AntimatterRules extends ChessRules { +export const VariantRules = class SuctionRules extends ChessRules { + setOtherVariables(fen) { + super.setOtherVariables(fen); + // Local stack of captures + this.cmoves = []; + const cmove = fen.split(" ")[5]; + if (cmove == "-") this.cmoves.push(null); + else { + this.cmoves.push({ + start: ChessRules.SquareToCoords(cmove.substr(0, 2)), + end: ChessRules.SquareToCoords(cmove.substr(2)) + }); + } + } + + static IsGoodFen(fen) { + if (!ChessRules.IsGoodFen(fen)) return false; + const fenParts = fen.split(" "); + if (fenParts.length != 6) return false; + if (fenParts[5] != "-" && !fenParts[5].match(/^([a-h][1-8]){2}$/)) + return false; + return true; + } + + getCmove(move) { + if (move.vanish.length == 2) + return { start: move.start, end: move.end }; + return null; + } + getBasicMove([sx, sy], [ex, ey]) { const startColor = this.getColor(sx, sy); const startPiece = this.getPiece(sx, sy); @@ -121,6 +150,27 @@ export const VariantRules = class AntimatterRules extends ChessRules { return []; } + // Does m2 un-do m1 ? (to disallow undoing captures) + oppositeMoves(m1, m2) { + return ( + m1 && + m2.vanish.length == 2 && + m1.start.x == m2.start.x && + m1.end.x == m2.end.x && + m1.start.y == m2.start.y && + m1.end.y == m2.end.y + ); + } + + filterValid(moves) { + if (moves.length == 0) return []; + const color = this.turn; + return moves.filter(m => { + const L = this.cmoves.length; //at least 1: init from FEN + return !this.oppositeMoves(this.cmoves[L - 1], m); + }); + } + updateVariables(move) { super.updateVariables(move); if (move.vanish.length == 2) { @@ -139,12 +189,32 @@ export const VariantRules = class AntimatterRules extends ChessRules { } } - atLeastOneMove() { - return true; + static GenRandInitFen() { + // Add empty cmove: + return ChessRules.GenRandInitFen() + " -"; } - filterValid(moves) { - return moves; + getFen() { + const L = this.cmoves.length; + const cmoveFen = !this.cmoves[L - 1] + ? "-" + : ChessRules.CoordsToSquare(this.cmoves[L - 1].start) + + ChessRules.CoordsToSquare(this.cmoves[L - 1].end); + return super.getFen() + " " + cmoveFen; + } + + play(move) { + this.cmoves.push(this.getCmove(move)); + super.play(move); + } + + undo(move) { + this.cmoves.pop(); + super.undo(move); + } + + atLeastOneMove() { + return true; } getCheckSquares() { diff --git a/client/src/variants/Wildebeest.js b/client/src/variants/Wildebeest.js index 933abbde..27b131d5 100644 --- a/client/src/variants/Wildebeest.js +++ b/client/src/variants/Wildebeest.js @@ -3,10 +3,6 @@ import { ArrayFun } from "@/utils/array"; import { sample, randInt } from "@/utils/alea"; export const VariantRules = class WildebeestRules extends ChessRules { - static getPpath(b) { - return ([V.CAMEL, V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b; - } - static get size() { return { x: 10, y: 11 }; } @@ -52,6 +48,10 @@ export const VariantRules = class WildebeestRules extends ChessRules { return true; } + getPpath(b) { + return ([V.CAMEL, V.WILDEBEEST].includes(b[1]) ? "Wildebeest/" : "") + b; + } + // There may be 2 enPassant squares (if pawn jump 3 squares) getEnpassantFen() { const L = this.epSquares.length; diff --git a/server/db/populate.sql b/server/db/populate.sql index 2876ba78..384b7356 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -16,6 +16,7 @@ insert or ignore into Variants (name,description) values ('Enpassant', 'Capture en passant'), ('Extinction', 'Capture all of a kind'), ('Grand', 'Big board'), + ('Hidden', 'Unidentified pieces'), ('Losers', 'Lose all pieces'), ('Magnetic', 'Laws of attraction'), ('Marseille', 'Move twice'), -- 2.44.0