From: Benjamin Auder Date: Sun, 10 Jan 2021 23:56:08 +0000 (+0100) Subject: Add Hypnotic + Mesmer variants X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/css/store/view-package.css%27%29%20%7D%7D?a=commitdiff_plain;h=2fac4d67083700a1f1e85ed8662c176c24cdea6b;p=vchess.git Add Hypnotic + Mesmer variants --- diff --git a/TODO b/TODO index a6d77bf4..4707a60c 100644 --- a/TODO +++ b/TODO @@ -3,8 +3,6 @@ Embedded rules language not updated when language is set (in Analyse, Game and P If new live game starts in background, "new game" notify OK but not first move. NEW VARIANTS: -https://www.chessvariants.com/mvopponent.dir/hypnotic-chess.html -https://www.chessvariants.com/mvopponent.dir/mesmer-chess.html https://www.reddit.com/r/TotemChess/comments/imi3v7/totem_rules/ --> replaced by "Joker" chess --> start on bishop of our color (instead), moves like a Bishop + Dabbabah, can swap with a piece *on same color* (as a move) at anytime. hmm diff --git a/client/public/images/pieces/Mesmer/bm.svg b/client/public/images/pieces/Mesmer/bm.svg new file mode 120000 index 00000000..000f539e --- /dev/null +++ b/client/public/images/pieces/Mesmer/bm.svg @@ -0,0 +1 @@ +../Maxima/bg.svg \ No newline at end of file diff --git a/client/public/images/pieces/Mesmer/wm.svg b/client/public/images/pieces/Mesmer/wm.svg new file mode 120000 index 00000000..436f84d6 --- /dev/null +++ b/client/public/images/pieces/Mesmer/wm.svg @@ -0,0 +1 @@ +../Maxima/wg.svg \ No newline at end of file diff --git a/client/src/base_rules.js b/client/src/base_rules.js index f1049b05..c849626d 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -163,7 +163,6 @@ export const ChessRules = class ChessRules { // Check if FEN describes a board situation correctly static IsGoodFen(fen) { -console.log("ddd"); const fenParsed = V.ParseFen(fen); // 1) Check position if (!V.IsGoodPosition(fenParsed.position)) return false; diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 41683bd1..10ac4f86 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -241,6 +241,8 @@ export const translations = { "Mate the knight": "Mate the knight", "Meet the Mammoth": "Meet the Mammoth", "Middle battle": "Middle battle", + "Mind control (v1)": "Mind control (v1)", + "Mind control (v2)": "Mind control (v2)", "Mongolian Horde (v1)": "Mongolian Horde (v1)", "Mongolian Horde (v2)": "Mongolian Horde (v2)", "Move like a knight (v1)": "Move like a knight (v1)", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 14d67eba..7ae7098b 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -241,6 +241,8 @@ export const translations = { "Mate the knight": "Matar el caballo", "Meet the Mammoth": "Conoce al Mamut", "Middle battle": "Batalla media", + "Mind control (v1)": "Control telepático(v1)", + "Mind control (v2)": "Control telepático(v2)", "Mongolian Horde (v1)": "Horda mongol (v1)", "Mongolian Horde (v2)": "Horda mongol (v2)", "Move like a knight (v1)": "Moverse como un caballo (v1)", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 255595ec..f905ab3a 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -241,6 +241,8 @@ export const translations = { "Mate the knight": "Matez le cavalier", "Meet the Mammoth": "Rencontrez le Mammouth", "Middle battle": "Bataille du milieu", + "Mind control (v1)": "Contrôle télépathique (v1)", + "Mind control (v2)": "Contrôle télépathique (v2)", "Mongolian Horde (v1)": "Horde mongole (v1)", "Mongolian Horde (v2)": "Horde mongole (v2)", "Move like a knight (v1)": "Bouger comme un cavalier (v1)", diff --git a/client/src/translations/rules/Hypnotic/en.pug b/client/src/translations/rules/Hypnotic/en.pug index 21203baa..d3263999 100644 --- a/client/src/translations/rules/Hypnotic/en.pug +++ b/client/src/translations/rules/Hypnotic/en.pug @@ -1 +1,28 @@ -p.boxed TODO +p.boxed + | You can control enemy pieces which are attacked. + +p. + Rather than moving one of his own pieces, a player may elect to move any + enemy piece that is attacked. The move must be a legal move for the enemy + piece, except that the (hypnotized) piece now captures its friends. + +p. + The hypnotized piece is temporarily frozen and thus cannot be moved by its + owner on the next turn. + +figure.diagram-container + .diagram.diag12 + | fen:r1bqk1nr/pppp1ppp/2n5/1Bb1p3/4P3/5N2/PPPP1PPP/RNBQK2R: + .diagram.diag22 + | fen:r1bnk1nr/pppp1ppp/8/1Bb1p3/4P3/5N2/PPPP1PPP/RNBQK2R: + figcaption Before and after Nxd8 (controlled by bishop on b5). + +h3 Source + +p + | Hypnotic Chess on + a(href="https://www.chessvariants.com/mvopponent.dir/hypnotic-chess.html") + | chessvariants.com + | . + +p Inventor: W. D. Troyka (2003) diff --git a/client/src/translations/rules/Hypnotic/es.pug b/client/src/translations/rules/Hypnotic/es.pug index 21203baa..e76cb56d 100644 --- a/client/src/translations/rules/Hypnotic/es.pug +++ b/client/src/translations/rules/Hypnotic/es.pug @@ -1 +1,29 @@ -p.boxed TODO +p.boxed + | Puedes controlar las piezas enemigas qué son atacadas. + +p. + En lugar de mover una de sus piezas, un jugador puede decidir mover + una pieza del oponente que está atacando. La jugada debe ser legal al punto + de vista del adversario, con el detalle que la pieza (hipnotizada) + ahora captura sus amigos. + +p. + La pieza hipnotizada está temporalmente inmovilizada y, por lo tanto, + no puede movido por su dueño en el siguiente turno. + +figure.diagram-container + .diagram.diag12 + | fen:r1bqk1nr/pppp1ppp/2n5/1Bb1p3/4P3/5N2/PPPP1PPP/RNBQK2R: + .diagram.diag22 + | fen:r1bnk1nr/pppp1ppp/8/1Bb1p3/4P3/5N2/PPPP1PPP/RNBQK2R: + figcaption Antes y después de Nxd8 (controlado por el alfil en b5). + +h3 Fuente + +p + | Hypnotic Chess en + a(href="https://www.chessvariants.com/mvopponent.dir/hypnotic-chess.html") + | chessvariants.com + | . + +p Inventor: W. D. Troyka (2003) diff --git a/client/src/translations/rules/Hypnotic/fr.pug b/client/src/translations/rules/Hypnotic/fr.pug index 21203baa..378231b4 100644 --- a/client/src/translations/rules/Hypnotic/fr.pug +++ b/client/src/translations/rules/Hypnotic/fr.pug @@ -1 +1,29 @@ -p.boxed TODO +p.boxed + | Vous pouvez contrôler les pièces ennemies qui sont attaquées. + +p. + Plutôt que de déplacer une de ses pièces, un joueur peut décider de + déplacer une pièce adverse qu'il attaque. Le coup doit être légal du point + de vue de l'adversaire, au détail près que la pièce (hypnotisée) capture + désormais ses amis. + +p. + La pièce hypnotisée est temporairement immobilisée et ne peut donc pas être + déplacée par son propriétaire au tour suivant. + +figure.diagram-container + .diagram.diag12 + | fen:r1bqk1nr/pppp1ppp/2n5/1Bb1p3/4P3/5N2/PPPP1PPP/RNBQK2R: + .diagram.diag22 + | fen:r1bnk1nr/pppp1ppp/8/1Bb1p3/4P3/5N2/PPPP1PPP/RNBQK2R: + figcaption Avant et après Nxd8 (contrôlé par le fou en b5). + +h3 Source + +p + | Hypnotic Chess sur + a(href="https://www.chessvariants.com/mvopponent.dir/hypnotic-chess.html") + | chessvariants.com + | . + +p Inventeur : W. D. Troyka (2003) diff --git a/client/src/translations/rules/Mesmer/en.pug b/client/src/translations/rules/Mesmer/en.pug index 21203baa..1f5b1bc6 100644 --- a/client/src/translations/rules/Mesmer/en.pug +++ b/client/src/translations/rules/Mesmer/en.pug @@ -1 +1,31 @@ -p.boxed TODO +p.boxed + | Pieces attacked by the Mesmerist can be controlled. + +p. + A new piece — the Mesmerist — has the power of mind control, + and may elect to move any enemy piece which he observes. + The Mesmerist moves like an orthodox queen, but cannot capture (except + through mind control). His victim is said to be mesmerized. + +p The Mesmerist is represented by an upside-down queen for now. + +figure.diagram-container + .diagram + | fen:rnbqkbnr/pppppppp/3m4/8/8/3M4/PPPPPPPP/RNBQKBNR: + figcaption Deterministic initial position. + +p. + The mesmerized piece is temporarily frozen and thus cannot be moved by its + owner on the next turn. + +p The game is won by capturing the enemy King or Mesmerist. + +h3 Source + +p + | Mesmer Chess on + a(href="https://www.chessvariants.com/mvopponent.dir/mesmer-chess.html") + | chessvariants.com + | . + +p Inventor: W. D. Troyka (2003) diff --git a/client/src/translations/rules/Mesmer/es.pug b/client/src/translations/rules/Mesmer/es.pug index 21203baa..371ba39a 100644 --- a/client/src/translations/rules/Mesmer/es.pug +++ b/client/src/translations/rules/Mesmer/es.pug @@ -1 +1,31 @@ -p.boxed TODO +p.boxed + | Las piezas atacadas por el Hipnotizador pueden controlarse. + +p. + Una nueva pieza — el Hipnotizador — puede elegir mudarse + una pieza enemiga que observa, porque luego controla su mente. + El Hipnotizador se mueve como una dama ortodoxa, pero no puede capturar + (excepto por telepatía). Se dice que su víctima está hipnotizada. + +p El Hipnotizador está representado por una dama al revés por ahora. + +figure.diagram-container + .diagram + | fen:rnbqkbnr/pppppppp/3m4/8/8/3M4/PPPPPPPP/RNBQKBNR: + figcaption Posición inicial determinista. + +p. + La pieza hipnotizada está temporalmente inmovilizada y, por lo tanto, + no puede ser movido por su dueño en el siguiente turno. + +p El juego se gana capturando al Rey o al Hipnotizador oponente. + +h3 Fuente + +p + | Mesmer Chess en + a(href="https://www.chessvariants.com/mvopponent.dir/mesmer-chess.html") + | chessvariants.com + | . + +p Inventor: W. D. Troyka (2003) diff --git a/client/src/translations/rules/Mesmer/fr.pug b/client/src/translations/rules/Mesmer/fr.pug index 21203baa..9c5255f3 100644 --- a/client/src/translations/rules/Mesmer/fr.pug +++ b/client/src/translations/rules/Mesmer/fr.pug @@ -1 +1,31 @@ -p.boxed TODO +p.boxed + | Les pièces attaquées par l'Hypnotiseur peuvent être contrôlées. + +p. + Une nouvelle pièce — l'Hypnotiseur — peut choisir de déplacer + une pièce ennemie qu'il observe, car il contrôle alors son esprit. + L'Hypnotiseur se déplace comme une dame orthodoxe, mais ne peut pas capturer + (sauf par télépathie). Sa victime est dite hypnotisée. + +p L'Hypnotiseur est représenté par une dame inversé pour l'instant. + +figure.diagram-container + .diagram + | fen:rnbqkbnr/pppppppp/3m4/8/8/3M4/PPPPPPPP/RNBQKBNR: + figcaption Position initiale déterministe. + +p. + La pièce hypnotisée est temporairement immobilisée, et ne peut donc pas + être déplacée par son propriétaire au tour suivant. + +p La partie est gagnée en capturant le Roi ou l'Hypnotiseur adverse. + +h3 Source + +p + | Mesmer Chess sur + a(href="https://www.chessvariants.com/mvopponent.dir/mesmer-chess.html") + | chessvariants.com + | . + +p Inventeur : W. D. Troyka (2003) diff --git a/client/src/translations/variants/en.pug b/client/src/translations/variants/en.pug index 312df305..f77c4b15 100644 --- a/client/src/translations/variants/en.pug +++ b/client/src/translations/variants/en.pug @@ -405,10 +405,12 @@ p. "Freecapture", "Gridolina", "Hamilton", + "Hypnotic", "Isardam", "Kingsmaker", "Magnetic", "Maharajah", + "Mesmer", "Otage", "Pacosako", "Parachute", diff --git a/client/src/translations/variants/es.pug b/client/src/translations/variants/es.pug index 9d360c8e..e3a287fb 100644 --- a/client/src/translations/variants/es.pug +++ b/client/src/translations/variants/es.pug @@ -416,10 +416,12 @@ p. "Freecapture", "Gridolina", "Hamilton", + "Hypnotic", "Isardam", "Kingsmaker", "Magnetic", "Maharajah", + "Mesmer", "Otage", "Pacosako", "Parachute", diff --git a/client/src/translations/variants/fr.pug b/client/src/translations/variants/fr.pug index 411a2056..cd1a6e40 100644 --- a/client/src/translations/variants/fr.pug +++ b/client/src/translations/variants/fr.pug @@ -415,10 +415,12 @@ p. "Freecapture", "Gridolina", "Hamilton", + "Hypnotic", "Isardam", "Kingsmaker", "Magnetic", "Maharajah", + "Mesmer", "Otage", "Pacosako", "Parachute", diff --git a/client/src/variants/Hypnotic.js b/client/src/variants/Hypnotic.js new file mode 100644 index 00000000..130b1279 --- /dev/null +++ b/client/src/variants/Hypnotic.js @@ -0,0 +1,181 @@ +import { ChessRules } from "@/base_rules"; + +export class HypnoticRules extends ChessRules { + + static IsGoodFen(fen) { + if (!ChessRules.IsGoodFen(fen)) return false; + const fenParsed = V.ParseFen(fen); + // 5) Check arrival of last hypnotizing move (if any) + if ( + !fenParsed.hSquare || + (fenParsed.hSquare != "-" && !fenParsed.hSquare.match(/^[a-h][1-8]$/)) + ) { + return false; + } + return true; + } + + static ParseFen(fen) { + const fenParts = fen.split(" "); + return Object.assign( + { hSquare: fenParts[5] }, + ChessRules.ParseFen(fen) + ); + } + + static GenRandInitFen(randomness) { + return ChessRules.GenRandInitFen(randomness) + " -"; + } + + setOtherVariables(fen) { + super.setOtherVariables(fen); + const parsedFen = V.ParseFen(fen); + this.hSquares = [ + parsedFen.hSquare != "-" + ? V.SquareToCoords(parsedFen.hSquare) + : null + ]; + } + + getFen() { + const L = this.hSquares.length; + return ( + super.getFen() + " " + + (!this.hSquares[L-1] ? "-" : V.CoordsToSquare(this.hSquares[L-1])) + ); + } + + canIplay(side) { + // Wrong, but sufficient approximation let's say + return this.turn == side; + } + + canTake([x1, y1], [x2, y2]) { + const c = this.turn; + const c1 = this.getColor(x1, y1); + const c2 = this.getColor(x2, y2); + return (c == c1 && c1 != c2) || (c != c1 && c1 == c2); + } + + getPotentialMovesFrom([x, y]) { + const L = this.hSquares.length; + const lh = this.hSquares[L-1]; + if (!!lh && lh.x == x && lh.y == y) return []; + const c = this.getColor(x, y); + if (c == this.turn) return super.getPotentialMovesFrom([x, y]); + // Playing opponent's pieces: hypnotizing moves. Allowed? + if (!this.isAttacked([x, y], this.turn)) return []; + const moves = + this.getPiece(x, y) == V.KING + // No castling with enemy king (...yes, should eat it but...) + ? super.getSlideNJumpMoves( + [x, y], V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep") + : super.getPotentialMovesFrom([x, y]); + return moves; + } + + getEnpassantCaptures([x, y], shiftX) { + const Lep = this.epSquares.length; + const epSquare = this.epSquares[Lep - 1]; //always at least one element + let enpassantMove = null; + const c = this.getColor(x, y); + if ( + !!epSquare && + epSquare.x == x + shiftX && + Math.abs(epSquare.y - y) == 1 && + // Next conditions to avoid capturing self hypnotized pawns: + this.board[x][epSquare.y] != V.EMPTY && + this.getColor(x, epSquare.y) != c //TODO: probably redundant + ) { + enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]); + enpassantMove.vanish.push({ + x: x, + y: epSquare.y, + p: this.board[x][epSquare.y].charAt(1), + c: this.getColor(x, epSquare.y) + }); + } + return !!enpassantMove ? [enpassantMove] : []; + } + + // TODO: avoid following code duplication, by using getColor() + // instead of this.turn at the beginning of 2 next methods + addPawnMoves([x1, y1], [x2, y2], moves, promotions) { + let finalPieces = [V.PAWN]; + const color = this.getColor(x1, y1); + const lastRank = (color == "w" ? 0 : V.size.x - 1); + if (x2 == lastRank) finalPieces = V.PawnSpecs.promotions; + let tr = null; + for (let piece of finalPieces) { + tr = (piece != V.PAWN ? { c: color, p: piece } : null); + moves.push(this.getBasicMove([x1, y1], [x2, y2], tr)); + } + } + + getPotentialPawnMoves([x, y], promotions) { + const color = this.getColor(x, y); + const [sizeX, sizeY] = [V.size.x, V.size.y]; + const forward = (color == 'w' ? -1 : 1); + + let moves = []; + if (x + forward >= 0 && x + forward < sizeX) { + if (this.board[x + forward][y] == V.EMPTY) { + this.addPawnMoves([x, y], [x + forward, y], moves, promotions); + if ( + ((color == 'w' && x == 6) || (color == 'b' && x == 1)) && + this.board[x + 2 * forward][y] == V.EMPTY + ) { + moves.push(this.getBasicMove([x, y], [x + 2 * forward, y])); + } + } + for (let shiftY of [-1, 1]) { + if ( + y + shiftY >= 0 && y + shiftY < sizeY && + this.board[x + forward][y + shiftY] != V.EMPTY && + this.canTake([x, y], [x + forward, y + shiftY]) + ) { + this.addPawnMoves( + [x, y], [x + forward, y + shiftY], + moves, promotions + ); + } + } + } + Array.prototype.push.apply(moves, + this.getEnpassantCaptures([x, y], forward)); + return moves; + } + + postPlay(move) { + super.postPlay(move); + if (move.vanish[0].c == this.turn) + this.hSquares.push({ x: move.appear[0].x, y: move.appear[0].y }); + else this.hSquares.push(null); + if (move.vanish.length == 2 && move.vanish[1].p == V.KING) + this.kingPos[move.vanish[1].c] = [-1, -1]; + } + postUndo(move) { + super.postUndo(move); + this.hSquares.pop(); + if (move.vanish.length == 2 && move.vanish[1].p == V.KING) + this.kingPos[move.vanish[1].c] = [move.vanish[1].x, move.vanish[1].y]; + } + + getCheckSquares() { + return []; + } + filterValid(moves) { + return moves; + } + + getCurrentScore() { + const c = this.turn; + if (this.kingPos[c][0] < 0) return (c == 'w' ? "0-1" : "1-0"); + return "*"; + } + + static get SEARCH_DEPTH() { + return 2; + } + +}; diff --git a/client/src/variants/Mesmer.js b/client/src/variants/Mesmer.js new file mode 100644 index 00000000..ec40b482 --- /dev/null +++ b/client/src/variants/Mesmer.js @@ -0,0 +1,262 @@ +import { ChessRules } from "@/base_rules"; +import { Antiking2Rules } from "@/variants/Antiking2"; + +export class MesmerRules extends ChessRules { + + static IsGoodFen(fen) { + if (!ChessRules.IsGoodFen(fen)) return false; + const fenParsed = V.ParseFen(fen); + // 5) Check arrival of last hypnotizing move (if any) + if ( + !fenParsed.hSquare || + (fenParsed.hSquare != "-" && !fenParsed.hSquare.match(/^[a-h][1-8]$/)) + ) { + return false; + } + return true; + } + + static get MESMERIST() { + return 'm'; + } + + static get PIECES() { + return ChessRules.PIECES.concat([V.MESMERIST]); + } + + getPpath(b) { + return (b.charAt(1) == 'm' ? "Mesmer/" : "") + b; + } + + static ParseFen(fen) { + const fenParts = fen.split(" "); + return Object.assign( + { hSquare: fenParts[5] }, + ChessRules.ParseFen(fen) + ); + } + + static GenRandInitFen(randomness) { + const antikingFen = Antiking2Rules.GenRandInitFen(randomness); + return antikingFen.replace('a', 'M').replace('A', 'm') + " -"; + } + + setOtherVariables(fen) { + super.setOtherVariables(fen); + const parsedFen = V.ParseFen(fen); + this.hSquares = [ + parsedFen.hSquare != "-" + ? V.SquareToCoords(parsedFen.hSquare) + : null + ]; + } + + scanKings(fen) { + super.scanKings(fen); + // Squares of white and black mesmerist: + this.mesmerPos = { w: [-1, -1], b: [-1, -1] }; + const fenRows = V.ParseFen(fen).position.split("/"); + 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 "m": + this.mesmerPos["b"] = [i, k]; + break; + case "M": + this.mesmerPos["w"] = [i, k]; + break; + default: { + const num = parseInt(fenRows[i].charAt(j), 10); + if (!isNaN(num)) k += num - 1; + } + } + k++; + } + } + } + + getFen() { + const L = this.hSquares.length; + return ( + super.getFen() + " " + + (!this.hSquares[L-1] ? "-" : V.CoordsToSquare(this.hSquares[L-1])) + ); + } + + canIplay(side) { + // Wrong, but sufficient approximation let's say + return this.turn == side; + } + + canTake([x1, y1], [x2, y2]) { + const c = this.turn; + const c1 = this.getColor(x1, y1); + const c2 = this.getColor(x2, y2); + return (c == c1 && c1 != c2) || (c != c1 && c1 == c2); + } + + getPotentialMovesFrom([x, y]) { + const L = this.hSquares.length; + const lh = this.hSquares[L-1]; + if (!!lh && lh.x == x && lh.y == y) return []; + const c = this.getColor(x, y); + const piece = this.getPiece(x, y); + if (c == this.turn) { + if (piece == V.MESMERIST) return this.getPotentialMesmeristMoves([x, y]); + return super.getPotentialMovesFrom([x, y]); + } + // Playing opponent's pieces: hypnotizing moves. Allowed? + if (piece == V.MESMERIST || !this.isAttackedByMesmerist([x, y], this.turn)) + return []; + const moves = + piece == V.KING + // No castling with enemy king (...yes, should eat it but...) + ? super.getSlideNJumpMoves( + [x, y], V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep") + : super.getPotentialMovesFrom([x, y]); + return moves; + } + + // Moves like a queen without capturing + getPotentialMesmeristMoves([x, y]) { + const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]); + let moves = []; + for (let step of steps) { + let i = x + step[0]; + let j = y + step[1]; + while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + moves.push(this.getBasicMove([x, y], [i, j])); + i += step[0]; + j += step[1]; + } + } + return moves; + } + + isAttackedByMesmerist(sq, color) { + return ( + super.isAttackedBySlideNJump( + sq, color, V.MESMERIST, V.steps[V.ROOK].concat(V.steps[V.BISHOP])) + ); + } + + getEnpassantCaptures([x, y], shiftX) { + const Lep = this.epSquares.length; + const epSquare = this.epSquares[Lep - 1]; //always at least one element + let enpassantMove = null; + const c = this.getColor(x, y); + if ( + !!epSquare && + epSquare.x == x + shiftX && + Math.abs(epSquare.y - y) == 1 && + // Next conditions to avoid capturing self hypnotized pawns: + this.board[x][epSquare.y] != V.EMPTY && + this.getColor(x, epSquare.y) != c //TODO: probably redundant + ) { + enpassantMove = this.getBasicMove([x, y], [epSquare.x, epSquare.y]); + enpassantMove.vanish.push({ + x: x, + y: epSquare.y, + p: this.board[x][epSquare.y].charAt(1), + c: this.getColor(x, epSquare.y) + }); + } + return !!enpassantMove ? [enpassantMove] : []; + } + + // TODO: avoid following code duplication, by using getColor() + // instead of this.turn at the beginning of 2 next methods + addPawnMoves([x1, y1], [x2, y2], moves, promotions) { + let finalPieces = [V.PAWN]; + const color = this.getColor(x1, y1); + const lastRank = (color == "w" ? 0 : V.size.x - 1); + if (x2 == lastRank) finalPieces = V.PawnSpecs.promotions; + let tr = null; + for (let piece of finalPieces) { + tr = (piece != V.PAWN ? { c: color, p: piece } : null); + moves.push(this.getBasicMove([x1, y1], [x2, y2], tr)); + } + } + + getPotentialPawnMoves([x, y], promotions) { + const color = this.getColor(x, y); + const [sizeX, sizeY] = [V.size.x, V.size.y]; + const forward = (color == 'w' ? -1 : 1); + + let moves = []; + if (x + forward >= 0 && x + forward < sizeX) { + if (this.board[x + forward][y] == V.EMPTY) { + this.addPawnMoves([x, y], [x + forward, y], moves, promotions); + if ( + ((color == 'w' && x == 6) || (color == 'b' && x == 1)) && + this.board[x + 2 * forward][y] == V.EMPTY + ) { + moves.push(this.getBasicMove([x, y], [x + 2 * forward, y])); + } + } + for (let shiftY of [-1, 1]) { + if ( + y + shiftY >= 0 && y + shiftY < sizeY && + this.board[x + forward][y + shiftY] != V.EMPTY && + this.canTake([x, y], [x + forward, y + shiftY]) + ) { + this.addPawnMoves( + [x, y], [x + forward, y + shiftY], + moves, promotions + ); + } + } + } + Array.prototype.push.apply(moves, + this.getEnpassantCaptures([x, y], forward)); + return moves; + } + + postPlay(move) { + super.postPlay(move); + if (move.vanish[0].p == V.MESMERIST) + this.mesmerPos[move.vanish[0].c] = [move.appear[0].x, move.appear[0].y]; + if (move.vanish[0].c == this.turn) + this.hSquares.push({ x: move.appear[0].x, y: move.appear[0].y }); + else this.hSquares.push(null); + if (move.vanish.length == 2) { + if (move.vanish[1].p == V.KING) + this.kingPos[move.vanish[1].c] = [-1, -1]; + else if (move.vanish[1].p == V.MESMERIST) + this.mesmerPos[move.vanish[1].c] = [-1, -1] + } + } + postUndo(move) { + super.postUndo(move); + if (move.vanish[0].p == V.MESMERIST) + this.mesmerPos[move.vanish[0].c] = [move.vanish[0].x, move.vanish[0].y]; + this.hSquares.pop(); + if (move.vanish.length == 2) { + const v = move.vanish[1]; + if (v.p == V.KING) + this.kingPos[v.c] = [v.x, v.y]; + else if (v.p == V.MESMERIST) + this.mesmerPos[v.c] = [v.x, v.y]; + } + } + + getCheckSquares() { + return []; + } + filterValid(moves) { + return moves; + } + + getCurrentScore() { + const c = this.turn; + if (this.kingPos[c][0] < 0) return (c == 'w' ? "0-1" : "1-0"); + if (this.mesmerPos[c][0] < 0) return (c == 'w' ? "0-1" : "1-0"); + return "*"; + } + + static get SEARCH_DEPTH() { + return 2; + } + +}; diff --git a/server/db/populate.sql b/server/db/populate.sql index 3c703532..be0f09bc 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -69,6 +69,7 @@ insert or ignore into Variants (name, description) values ('Hamilton', 'Walk on a graph'), ('Hoppelpoppel', 'Knibis and Bisknis'), ('Horde', 'A pawns cloud'), + ('Hypnotic', 'Mind control (v1)'), ('Interweave', 'Interweaved colorbound teams'), ('Isardam', 'No paralyzed pieces'), ('Janggi', 'Korean Chess'), @@ -88,6 +89,7 @@ insert or ignore into Variants (name, description) values ('Makpong', 'Thai Chess (v2)'), ('Makruk', 'Thai Chess (v1)'), ('Maxima', 'Occupy the enemy palace'), + ('Mesmer', 'Mind control (v2)'), ('Minishogi', 'Shogi 5 x 5'), ('Minixiangqi', 'Xiangqi 7 x 7'), ('Monochrome', 'All of the same color'),