# New variants
-Finish first https://www.chessvariants.com/mvopponent.dir/dynamo.html
+Finish https://www.chessvariants.com/mvopponent.dir/dynamo.html
https://echekk.fr/spip.php?page=article&id_article=599
-Monster, Horde, Colorbound
+Colorbound
https://www.chessvariants.com/d.betza/chessvar/dan/colclob.html
-https://lichess.org/analysis/horde#0
-https://greenchess.net/rules.php?v=monster
-https://en.wikipedia.org/wiki/Monster_chess
https://en.wikipedia.org/wiki/Grotesque_(chess) (fun)
Maxima, Interweave, Roccoco
i = y;
do {
if (
- (!castleInCheck && this.isAttacked([x, i], oppCol)) ||
+ // NOTE: "castling" arg is used by some variants (Monster),
+ // where "isAttacked" is overloaded in an infinite-recursive way.
+ (!castleInCheck && this.isAttacked([x, i], oppCol, "castling")) ||
(this.board[x][i] != V.EMPTY &&
// NOTE: next check is enough, because of chessboard constraints
(this.getColor(x, i) != c ||
// Nothing on final squares, except maybe king and castling rook?
for (i = 0; i < 2; i++) {
if (
+ finalSquares[castleSide][i] != rookPos &&
this.board[x][finalSquares[castleSide][i]] != V.EMPTY &&
- this.getPiece(x, finalSquares[castleSide][i]) != V.KING &&
- finalSquares[castleSide][i] != rookPos
+ (
+ this.getPiece(x, finalSquares[castleSide][i]) != V.KING ||
+ this.getColor(x, finalSquares[castleSide][i]) != c
+ )
) {
continue castlingCheck;
}
});
}
- // Search for all valid moves considering current turn
- // (for engine and game end)
- getAllValidMoves() {
+ getAllPotentialMoves() {
const color = this.turn;
let potentialMoves = [];
for (let i = 0; i < V.size.x; i++) {
}
}
}
- return this.filterValid(potentialMoves);
+ return potentialMoves;
+ }
+
+ // Search for all valid moves considering current turn
+ // (for engine and game end)
+ getAllValidMoves() {
+ return this.filterValid(this.getAllPotentialMoves());
}
// Stop at the first move found
this.incheck = this.vr.getCheckSquares(this.vr.turn);
const score = this.vr.getCurrentScore();
if (L > 0 && this.moves[L - 1].notation != "...") {
- if (["1-0","0-1"].includes(score))
- this.moves[L - 1].notation += "#";
- else if (this.vr.getCheckSquares(this.vr.turn).length > 0)
- this.moves[L - 1].notation += "+";
+ if (["1-0","0-1"].includes(score)) this.moves[L - 1].notation += "#";
+ else if (this.incheck.length > 0) this.moves[L - 1].notation += "+";
}
},
positionCursorTo: function(index) {
const computeScore = () => {
const score = this.vr.getCurrentScore();
if (!navigate) {
- if (["1-0","0-1"].includes(score))
- this.lastMove.notation += "#";
- else if (this.vr.getCheckSquares(this.vr.turn).length > 0)
- this.lastMove.notation += "+";
+ if (["1-0","0-1"].includes(score)) this.lastMove.notation += "#";
+ else if (this.incheck.length > 0) this.lastMove.notation += "+";
}
if (score != "*" && this.game.mode == "analyze") {
const message = getScoreMessage(score);
"Captures reborn": "Captures reborn",
"Change colors": "Change colors",
"Dangerous collisions": "Dangerous collisions",
+ "Double moves": "Double moves",
"Each piece is unique": "Each piece is unique",
"Exotic captures": "Exotic captures",
"Explosive captures": "Explosive captures",
"Mongolian Horde": "Mongolian Horde",
"Move like a knight (v1)": "Move like a knight (v1)",
"Move like a knight (v2)": "Move like a knight (v2)",
- "Move twice": "Move twice",
"Neverending rows": "Neverending rows",
"No-check mode": "No-check mode",
"Pawns move diagonally": "Pawns move diagonally",
"Transform an essay": "Transform an essay",
"Two kings": "Two kings",
"Two royal pieces": "Two royal pieces",
- "Unidentified pieces": "Unidentified pieces"
+ "Unidentified pieces": "Unidentified pieces",
+ "White move twice": "White move twice"
};
"Captures reborn": "Las capturas renacen",
"Change colors": "Cambiar colores",
"Dangerous collisions": "Colisiones peligrosas",
+ "Double moves": "Jugadas doble",
"Each piece is unique": "Cada pieza es única",
"Exotic captures": "Capturas exóticas",
"Explosive captures": "Capturas explosivas",
"Mongolian Horde": "Horda mongol",
"Move like a knight (v1)": "Moverse como un caballo (v1)",
"Move like a knight (v2)": "Moverse como un caballo (v2)",
- "Move twice": "Mover dos veces",
"Neverending rows": "Filas interminables",
"No-check mode": "Modo sin jaque",
"Pawns move diagonally": "Peones se mueven en diagonal",
"Transform an essay": "Transformar un ensayo",
"Two kings": "Dos reyes",
"Two royal pieces": "Dos piezas reales",
- "Unidentified pieces": "Piezas no identificadas"
+ "Unidentified pieces": "Piezas no identificadas",
+ "White move twice": "Las blancas juegan dos veces"
};
"Captures reborn": "Les captures renaissent",
"Change colors": "Changer les couleurs",
"Dangerous collisions": "Collisions dangeureuses",
+ "Double moves": "Coups doubles",
"Each piece is unique": "Chaque pièce est unique",
"Exotic captures": "Captures exotiques",
"Explosive captures": "Captures explosives",
"Mongolian Horde": "Horde mongole",
"Move like a knight (v1)": "Bouger comme un cavalier (v1)",
"Move like a knight (v2)": "Bouger comme un cavalier (v2)",
- "Move twice": "Jouer deux coups",
"Neverending rows": "Rangées sans fin",
"No-check mode": "Mode sans échec",
"Pawns move diagonally": "Les pions vont en diagonale",
"Transform an essay": "Transformer un essai",
"Two kings": "Deux rois",
"Two royal pieces": "Deux pièces royales",
- "Unidentified pieces": "Pièces non identifiées"
+ "Unidentified pieces": "Pièces non identifiées",
+ "White move twice": "Les blancs jouent deux fois"
};
--- /dev/null
+p.boxed.
+ White has only four pawns and the king, but move twice at each turn.
+
+figure.diagram-container
+ .diagram
+ | fen:rnbqkbnr/pppppppp/8/8/8/8/2PPPP2/4K3:
+ figcaption Standard deterministic position
+
+p.
+ The white army can appear much too small, but the power to move twice in a
+ row shouldn't be underestimated. At each turn white plays two moves with
+ only one constraint: do not be under check in the end.
+ So if the white king attacks a defended piece, he can take it anyway by
+ coming back on its initial square on (sub)move 2.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:rbbknn1r/1p2pp1p/2p3qK/p2p2p1/2PPP3/8/5P2/8:
+ .diagram.diag22
+ | fen:3rq3/1p4p1/1k1pKp2/3P1P1n/p7/5n2/8/8:
+ figcaption Left: not a checkmate! Right: a "Monster-checkmate".
+
+p
+ | The diagram position on the left looks pretty much like a checkmate,
+ | but white can take the queen and come back to the h6 square. Finally,
+ | white can mate in an unusual way, like the following diagram found
+ a(href="https://en.wikipedia.org/wiki/Monster_chess") on Wikipedia
+ | . There is no way for the black king to avoid being captured since white
+ | plays twice (the threat is 2.d7,dxe8).
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:4k3/8/4P3/8/3P4/8/2q5/7K:
+ .diagram.diag22
+ | fen:4k3/8/3PP3/8/8/8/2q5/7K:
+ figcaption Left: before 1.d5,d6. Right: after this move:, it's checkmate.
+
+h3 More information
+
+p
+ | Ralph Betza analyses this variant and the double move on
+ a(href="https://www.chessvariants.com/d.betza/chessvar/muenster.html")
+ | this page
+ | . There seems to be a common belief that black should win with accurate
+ | play, but it's clearly hard to demonstrate. And if someone can show a
+ | winning strategy, we'll add some white material to balance this game.
+ | Meanwhile, the variant is also playable
+ a(href="https://greenchess.net/rules.php?v=monster") on greenchess.net
+ | .
--- /dev/null
+p.boxed.
+ Las blancas solo tienen cuatro peones y un rey, pero hacen dos movimientos
+ a cada turno.
+
+figure.diagram-container
+ .diagram
+ | fen:rnbqkbnr/pppppppp/8/8/8/8/2PPPP2/4K3:
+ figcaption Posición inicial estándar
+
+p.
+ El ejército blanco puede parecer demasiado pequeño, pero el poder de jugar
+ dos veces seguidas y no debe subestimarse. En cada turno las blancas juegan
+ dos jugadas con la única restricción de no estar en jaque hasta el final.
+ Entonces, si tu rey blanco ataca una habitación protegida, él puede
+ tómalo de todos modos y luego regresa a tu caso inicial en el segundo
+ (sub)movimiento.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:rbbknn1r/1p2pp1p/2p3qK/p2p2p1/2PPP3/8/5P2/8:
+ .diagram.diag22
+ | fen:3rq3/1p4p1/1k1pKp2/3P1P1n/p7/5n2/8/8:
+ figcaption Izquierda: no es una jaque mate! Derecha: un "Monster-mate".
+
+p
+ | La posición del diagrama de la izquierda se ve bien como un jaque mate,
+ | pero las blancas pueden tomar la dama y volver llevar en h6. Finalmente,
+ | las blancas a veces pueden dar jaque mate de una manera inusual como se
+ | muestra en el siguiente diagrama encontrado
+ a(href="https://en.wikipedia.org/wiki/Monster_chess") en Wikipedia
+ | . El rey negro no tiene forma de escapar de la captura porque las blancas
+ | hacen dos movimientos (la amenaza es 2.d7, dxe8).
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:4k3/8/4P3/8/3P4/8/2q5/7K:
+ .diagram.diag22
+ | fen:4k3/8/3PP3/8/8/8/2q5/7K:
+ figcaption.
+ Izquierda: antes de 1.d5,d6.
+ Derecha: después de esta jugada, es jaque mate.
+
+h3 Más información
+
+p
+ | Ralph Betza analiza esta variante y el doble movimiento en
+ a(href="https://www.chessvariants.com/d.betza/chessvar/muenster.html")
+ | esta página
+ | . La opinión general parece indicar que las negras deben ganar con un
+ | juego precisa, pero es claramente difícil de demostrar. Y si es necesario,
+ | se podría agregar material a las blancas para reequilibrar el juego.
+ | Dicho esto, la variante también es jugable
+ a(href="https://greenchess.net/rules.php?v=monster") en greenchess.net
+ | .
--- /dev/null
+p.boxed.
+ Les blancs n'ont que quatre pions et un roi, mais jouent deux coups à chaque
+ tour.
+
+figure.diagram-container
+ .diagram
+ | fen:rnbqkbnr/pppppppp/8/8/8/8/2PPPP2/4K3:
+ figcaption Position initiale standard
+
+p.
+ L'armée blanche peut paraître bien trop réduite, mais le pouvoir de jouer
+ deux fois d'affilée ne doit pas être sous-estimé. À chaque tour les
+ blancs jouent deux coups avec pour seule contrainte de ne pas être en échec
+ à la fin. Ainsi, si le roi blanc attaque une pièce protégée, il peut la
+ prendre quand-même puis revenir sur sa case initiale au second (sous)coup.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:rbbknn1r/1p2pp1p/2p3qK/p2p2p1/2PPP3/8/5P2/8:
+ .diagram.diag22
+ | fen:3rq3/1p4p1/1k1pKp2/3P1P1n/p7/5n2/8/8:
+ figcaption Gauche : pas un mat ! Droite : un "Monster-mat".
+
+p
+ | La position du diagramme à gauche ressemble bien à un mat, mais les blancs
+ | peuvent prendre la dame et revenir en h6. Enfin, les blancs peuvent parfois
+ | mater d'une manière inhabituelle comme le montre le diagramme suivant
+ | trouvé
+ a(href="https://en.wikipedia.org/wiki/Monster_chess") sur Wikipedia
+ | . Le roi noir n'a aucun moyen d'échapper à la capture puisque les blancs
+ | jouent deux coups (la menace est 2.d7,dxe8).
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:4k3/8/4P3/8/3P4/8/2q5/7K:
+ .diagram.diag22
+ | fen:4k3/8/3PP3/8/8/8/2q5/7K:
+ figcaption Gauche : avant 1.d5,d6. Droite : après ce coup, c'est mat.
+
+h3 Plus d'information
+
+p
+ | Ralph Betza analyse cette variante et le double coup sur
+ a(href="https://www.chessvariants.com/d.betza/chessvar/muenster.html")
+ | cette page
+ | . L'avis général semble indiquer que les noirs doivent gagner avec un jeu
+ | précis, mais c'est clairement difficile à démontrer. Et le cas échéant, on
+ | pourrait ajouter du matériel aux blancs pour rééquilibrer le jeu.
+ | Ceci dit, la variante est jouable également
+ a(href="https://greenchess.net/rules.php?v=monster") sur greenchess.net
+ | .
import { ChessRules } from "@/base_rules";
+
export class Chess960Rules extends ChessRules {
// Standard rules
};
if (this.subTurn == 2) {
this.subTurn = 1;
this.movesCount--;
- }
- else {
+ } else {
// subTurn == 1 (after a move played)
this.turn = V.GetOppCol(this.turn);
this.subTurn = 2;
--- /dev/null
+import { ChessRules } from "@/base_rules";
+import { randInt } from "@/utils/alea";
+
+export class MonsterRules extends ChessRules {
+ static IsGoodFlags(flags) {
+ // Only black can castle
+ return !!flags.match(/^[a-z]{2,2}$/);
+ }
+
+ static GenRandInitFen(randomness) {
+ if (randomness == 2) randomness--;
+ const fen = ChessRules.GenRandInitFen(randomness);
+ return (
+ // 26 first chars are 6 rows + 6 slashes
+ fen.substr(0, 26)
+ // En passant available, and "half-castle"
+ .concat("2PPPP2/4K3 w 0 ")
+ .concat(fen.substr(-6, 2))
+ .concat(" -")
+ );
+ }
+
+ getFlagsFen() {
+ return this.castleFlags['b'].map(V.CoordToColumn).join("");
+ }
+
+ setFlags(fenflags) {
+ this.castleFlags = { 'b': [-1, -1] };
+ for (let i = 0; i < 2; i++)
+ this.castleFlags['b'][i] = V.ColumnToCoord(fenflags.charAt(i));
+ }
+
+ setOtherVariables(fen) {
+ super.setOtherVariables(fen);
+ this.subTurn = 1;
+ }
+
+ getPotentialKingMoves([x, y]) {
+ if (this.getColor(x, y) == 'b') return super.getPotentialKingMoves([x, y]);
+ // White doesn't castle:
+ return this.getSlideNJumpMoves(
+ [x, y],
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]),
+ "oneStep"
+ );
+ }
+
+ isAttacked(sq, color, castling) {
+ const singleMoveAttack = super.isAttacked(sq, color);
+ if (singleMoveAttack) return true;
+ if (color == 'b' || !!castling) return singleMoveAttack;
+ // Attacks by white: double-move allowed
+ const curTurn = this.turn;
+ this.turn = 'w';
+ const w1Moves = super.getAllPotentialMoves();
+ this.turn = curTurn;
+ for (let move of w1Moves) {
+ this.play(move);
+ const res = super.isAttacked(sq, 'w');
+ this.undo(move);
+ if (res) return res;
+ }
+ return false;
+ }
+
+ play(move) {
+ move.flags = JSON.stringify(this.aggregateFlags());
+ if (this.turn == 'b' || this.subTurn == 2)
+ this.epSquares.push(this.getEpSquare(move));
+ else this.epSquares.push(null);
+ V.PlayOnBoard(this.board, move);
+ if (this.turn == 'w') {
+ if (this.subTurn == 1) this.movesCount++;
+ else this.turn = 'b';
+ this.subTurn = 3 - this.subTurn;
+ } else {
+ this.turn = 'w';
+ this.movesCount++;
+ }
+ this.postPlay(move);
+ }
+
+ updateCastleFlags(move, piece) {
+ // Only black can castle:
+ const firstRank = 0;
+ if (piece == V.KING && move.appear[0].c == 'b')
+ this.castleFlags['b'] = [8, 8];
+ else if (
+ move.start.x == firstRank &&
+ this.castleFlags['b'].includes(move.start.y)
+ ) {
+ const flagIdx = (move.start.y == this.castleFlags['b'][0] ? 0 : 1);
+ this.castleFlags['b'][flagIdx] = 8;
+ }
+ else if (
+ move.end.x == firstRank &&
+ this.castleFlags['b'].includes(move.end.y)
+ ) {
+ const flagIdx = (move.end.y == this.castleFlags['b'][0] ? 0 : 1);
+ this.castleFlags['b'][flagIdx] = 8;
+ }
+ }
+
+ postPlay(move) {
+ // Definition of 'c' in base class doesn't work:
+ const c = move.vanish[0].c;
+ const piece = move.vanish[0].p;
+ if (piece == V.KING && move.appear.length > 0) {
+ this.kingPos[c][0] = move.appear[0].x;
+ this.kingPos[c][1] = move.appear[0].y;
+ return;
+ }
+ this.updateCastleFlags(move, piece);
+ }
+
+ undo(move) {
+ this.epSquares.pop();
+ this.disaggregateFlags(JSON.parse(move.flags));
+ V.UndoOnBoard(this.board, move);
+ if (this.turn == 'w') {
+ if (this.subTurn == 2) this.subTurn = 1;
+ else this.turn = 'b';
+ this.movesCount--;
+ } else {
+ this.turn = 'w';
+ this.subTurn = 2;
+ }
+ this.postUndo(move);
+ }
+
+ filterValid(moves) {
+ if (this.turn == 'w' && this.subTurn == 1) {
+ return moves.filter(m1 => {
+ this.play(m1);
+ // NOTE: no recursion because next call will see subTurn == 2
+ const res = super.atLeastOneMove();
+ this.undo(m1);
+ return res;
+ });
+ }
+ return super.filterValid(moves);
+ }
+
+ static get SEARCH_DEPTH() {
+ return 1;
+ }
+
+ getComputerMove() {
+ const color = this.turn;
+ if (color == 'w') {
+ // Generate all sequences of 2-moves
+ const moves1 = this.getAllValidMoves();
+ moves1.forEach(m1 => {
+ m1.eval = -V.INFINITY;
+ m1.move2 = null;
+ this.play(m1);
+ const moves2 = this.getAllValidMoves();
+ moves2.forEach(m2 => {
+ this.play(m2);
+ const eval2 = this.evalPosition();
+ this.undo(m2);
+ if (eval2 > m1.eval) {
+ m1.eval = eval2;
+ m1.move2 = m2;
+ }
+ });
+ this.undo(m1);
+ });
+ moves1.sort((a, b) => b.eval - a.eval);
+ let candidates = [0];
+ for (
+ let i = 1;
+ i < moves1.length && moves1[i].eval == moves1[0].eval;
+ i++
+ ) {
+ candidates.push(i);
+ }
+ const idx = candidates[randInt(candidates.length)];
+ const move2 = moves1[idx].move2;
+ delete moves1[idx]["move2"];
+ return [moves1[idx], move2];
+ }
+ // For black at depth 1, super method is fine:
+ return super.getComputerMove();
+ }
+};
('Knightrelay2', 'Move like a knight (v2)'),
('Losers', 'Get strong at self-mate'),
('Magnetic', 'Laws of attraction'),
- ('Marseille', 'Move twice'),
+ ('Marseille', 'Double moves'),
+ ('Monster', 'White move twice'),
('Orda', 'Mongolian Horde'),
('Parachute', 'Landing on the board'),
('Perfect', 'Powerful pieces'),