+++ /dev/null
-Here are some binary images used for some variants.
-Currently only Orda.
--- /dev/null
+Here are some extra files used for some variants.
--- /dev/null
+[Site "vchess.club"]
+[Variant "Swap"]
+[Date "2004-01-01"]
+[White "Joao Neto"]
+[Black "Bill Taylor"]
+[Fen "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0 ahah - -"]
+[Result "0-1"]
+
+1.d4 Nc6,Bg7 2.Nf3,Bg2 Bxd4,Ba7 3.Nxd4,Ne2 f6,Qd7 4.Bxc6,Ra2 Qxc6,Qh1 5.Rxc7,Rc2 dxc7,Qh2 6.Rxa7,Ra1 Qxh1,Rh7 7.Bf4,Nd4 Bh3,Re7 8.Kd2,Nd2 Qxf1,Qf2 9.Nb5,Qd2 Qxf1,0-0 10.Be3,e3 Qxe2,Qb5
+
+1.w d2.d4 wpd4/wpd2
+1.b b8.c6 bnc6/bnb8,f8.g7 bbg7.bpf8/bbf8.bpg7
+2.w g1.f3 wnf3/wng1,f1.g2 wbg2.wpf1/wbf1.wpg2
+2.b g7.d4 bbd4/bbg7.wpd4,d4.a7 bba7.bpd4/bbd4.bpa7
+3.w f3.d4 wnd4/wnf3.bpd4,d4.e2 wne2.wpd4/wnd4.wpe2
+3.b f7.f6 bpf6/bpf7,d8.d7 bqd7.bpd8/bqd8.bpd7
+4.w g2.c6 wbc6/wbg2.bnc6,a1.a2 wra2.wpa1/wra1.wpa2
+4.b d7.c6 bqc6/bqd7.wbc6,c6.h1 bqh1.wrc6/bqc6.wrh1
+5.w c6.c7 wrc7/wrc6.bpc7,c7.c2 wrc2.wpc7/wrc7.wpc2
+5.b d8.c7 bpc7/bpd8.wpc7,h1.h2 bqh2.wph1/bqh1.wph2
+6.w a2.a7 wra7/wra2.bba7,a7.a1 wra1.wpa7/wra7.wpa1
+6.b h2.h1 bqh1/bqh2.wph1,h8.h7 brh7.bph8/brh8.bph7
+7.w c1.f4 wbf4/wbc1,e2.d4 wnd4.wpe2/wne2.wpd4
+7.b c8.h3 bbh3/bbc8,h7.e7 bre7.bph7/brh7.bpe7
+8.w e1.d2 wkd2/wke1,b1.d2 wnd2.wkb1/wnb1.wkd2
+8.b h1.f1 bqf1/bqh1.wpf1,f1.f2 bqf2.wpf1/bqf1.wpf2
+9.w d4.b5 wnb5/wnd4,d1.d2 wqd2.wnd1/wqd1.wnd2
+9.b f2.f1 bqf1/bqf2.wpf1,e8.f8 bkf8.bpe8/bke8.bpf8
+10.w f4.e3 wbe3/wbf4,e2.e3 wpe3.wbe2/wpe2.wbe3
+10.b f1.e2 bqe2/bqf1.wbe2,e2.b5 bqb5.wne2/bqe2.wnb5
piece = move.appear[0].p;
// 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 (piece == V.KING && move.appear.length > 0)
+ this.kingPos[c] = [move.appear[0].x, move.appear[0].y];
if (V.HasCastle) this.updateCastleFlags(move, piece);
}
"Change colors": "Change colors",
"Convert & support (v1)": "Convert & support (v1)",
"Convert & support (v2)": "Convert & support (v2)",
+ "Dangerous captures": "Dangerous captures",
"Dangerous collisions": "Dangerous collisions",
"Double moves (v1)": "Double moves (v1)",
"Double moves (v2)": "Double moves (v2)",
"Each piece is unique": "Each piece is unique",
+ "Exchange pieces' positions": "Exchange pieces' positions",
"Exotic captures": "Exotic captures",
"Explosive captures": "Explosive captures",
"Four new pieces": "Four new pieces",
"Protect your pawns": "Protect your pawns",
"Push and pull": "Push and pull",
"Queen disguised as a pawn": "Queen disguised as a pawn",
+ "Reach the last rank": "Reach the last rank",
"Reposition pieces": "Reposition pieces",
"Reuse pieces": "Reuse pieces",
"Reverse captures": "Reverse captures",
"Change colors": "Cambiar colores",
"Convert & support (v1)": "Convertir & apoyar (v1)",
"Convert & support (v2)": "Convertir & apoyar (v2)",
+ "Dangerous captures": "Capturas peligrosas",
"Dangerous collisions": "Colisiones peligrosas",
"Double moves (v1)": "Jugadas doble (v1)",
"Double moves (v2)": "Jugadas doble (v2)",
"Each piece is unique": "Cada pieza es única",
+ "Exchange pieces' positions": "Intercambiar posiciones de piezas",
"Exotic captures": "Capturas exóticas",
"Explosive captures": "Capturas explosivas",
"Four new pieces": "Quatro nuevas piezas",
"Protect your pawns": "Protege tus peones",
"Push and pull": "Empujar y tirar",
"Queen disguised as a pawn": "Reina disfrazada de peón",
+ "Reach the last rank": "Llegar a la última fila",
"Reposition pieces": "Reposicionar las piezas",
"Reuse pieces": "Reutilizar piezas",
"Reverse captures": "Capturas invertidas",
"Change colors": "Changer les couleurs",
"Convert & support (v1)": "Convertir & soutenir (v1)",
"Convert & support (v2)": "Convertir & soutenir (v2)",
+ "Dangerous captures": "Captures dangeureuses",
"Dangerous collisions": "Collisions dangeureuses",
"Double moves (v1)": "Coups doubles (v1)",
"Double moves (v2)": "Coups doubles (v2)",
"Each piece is unique": "Chaque pièce est unique",
+ "Exchange pieces' positions": "Échangez les positions des pièces",
"Exotic captures": "Captures exotiques",
"Explosive captures": "Captures explosives",
"Four new pieces": "Quatre nouvelles pièces",
"Protect your pawns": "Protégez vos pions",
"Push and pull": "Pousser et tirer",
"Queen disguised as a pawn": "Reine déguisée en pion",
+ "Reach the last rank": "Atteignez la dernière rangée",
"Reposition pieces": "Replacer les pièces",
"Reuse pieces": "Réutiliser les pièces",
"Reverse captures": "Captures inversées",
p.
At the very first move of the game, white make only one move - as usual.
However, after that and for all the game each side must play twice at
- every turn (even if the first move captures the enemy king).
+ every turn (except if the first move captures the enemy king).
figure.diagram-container
.diagram.diag12
Al comienzo del juego, las blancas solo juegan un movimiento, como
en general. Sin embargo, después de eso y por el resto del juego,
cada lado debe jugar dos jugadas cada turno
- (incluso si la primera captura al rey contrario).
+ (a menos que la primera captura al rey contrario).
figure.diagram-container
.diagram.diag12
Au tout début de la partie les blancs ne jouent qu'un seul coup, comme
d'habitude. Cependant, après cela et ce pour tout le reste de la partie
chaque camp doit jouer deux coups à chaque tour
- (même si le premier capture le roi adverse).
+ (sauf si le premier capture le roi adverse).
figure.diagram-container
.diagram.diag12
| The player in second has a different set of pieces,
| moving roughly like "augmented knights".
-img.img-center(src="/images/variants/Orda/Orda.png")
+img.img-center(src="/variants/Orda/Orda.png")
p
| Orda Chess is a chess variant designed in 2020 by Couch Tomato.
h4 Yurt (Y)
-img.img-center(src="/images/variants/Orda/Yurt.png")
+img.img-center(src="/variants/Orda/Yurt.png")
p.
The Yurt moves and captures one space diagonally or one space forward.
h4 Kheshig (H)
-img.img-center(src="/images/variants/Orda/Kheshig.png")
+img.img-center(src="/variants/Orda/Kheshig.png")
p.
The Kheshig is a hybrid piece that moves and captures as a knight and
h4 Horse Archer (A)
-img.img-center(src="/images/variants/Orda/Archer.png")
+img.img-center(src="/variants/Orda/Archer.png")
p.
The Horse Archer, or simply abbreviated Archer, is a unique "pseudohybrid"
h4 Lancer (L)
-img.img-center(src="/images/variants/Orda/Lancer.png")
+img.img-center(src="/variants/Orda/Lancer.png")
p.
The Lancer is a unique "pseudohybrid" piece that moves and attacks
| El segundo jugador tiene un conjunto diferente de piezas,
| moviéndose aproximadamente como "caballos aumentados".
-img.img-center(src="/images/variantes/Orda/Orda.png")
+img.img-center(src="/variants/Orda/Orda.png")
p
| Orda Chess es una variante inventada en 2020 por Couch Tomato
h4 Yurta (Y)
-img.img-center(src="/images/variantes/Orda/Yurt.png")
+img.img-center(src="/variants/Orda/Yurt.png")
p.
La yurta se mueve y captura una casilla diagonal o un cuadrado hacia
h4 Kheshig (H)
-img.img-center(src="/images/variantes/Orda/Kheshig.png")
+img.img-center(src="/variants/Orda/Kheshig.png")
p.
El kheshig es una pieza híbrida que se mueve y captura como un rey y un
h4 Arquero a caballo (A)
-img.img-center(src="/images/variantes/Orda/Archer.png")
+img.img-center(src="/variants/Orda/Archer.png")
p.
El arquero (a caballo) es una pieza única "pseudo-híbrida" que se mueve
h4 Lancero (L)
-img.img-center(src="/images/variantes/Orda/Lancer.png")
+img.img-center(src="/variants/Orda/Lancer.png")
p.
El lancero es una pieza única "pseudo-híbrida" que se mueve y ataca
| Le joueur en second dispose d'un jeu de pièces différent,
| se déplaçant en gros comme des "cavaliers augmentés".
-img.img-center(src="/images/variants/Orda/Orda.png")
+img.img-center(src="/variants/Orda/Orda.png")
p
| Orda Chess est une variante inventée en 2020 par Couch Tomato
h4 Yourte (Y)
-img.img-center(src="/images/variants/Orda/Yurt.png")
+img.img-center(src="/variants/Orda/Yurt.png")
p.
La yourte se déplace et capture d'une case en diagonale ou une case vers
h4 Kheshig (H)
-img.img-center(src="/images/variants/Orda/Kheshig.png")
+img.img-center(src="/variants/Orda/Kheshig.png")
p.
Le kheshig est une pièce hybride qui bouge et capture comme roi et cavalier
h4 Archer à cheval (A)
-img.img-center(src="/images/variants/Orda/Archer.png")
+img.img-center(src="/variants/Orda/Archer.png")
p.
L'archer (à cheval) est une pièce unique "pseudo-hybride" qui se déplace
h4 Lancier (L)
-img.img-center(src="/images/variants/Orda/Lancer.png")
+img.img-center(src="/variants/Orda/Lancer.png")
p.
Le lancier est une pièce unique "pseudo-hybride" qui se déplace et attaque
--- /dev/null
+p.boxed
+ | Only pawns. Standard rules. The first to promote wins.
+
+p.
+ This game may appear simple, but since you cannot play any waiting move,
+ beware the Zugzwang ;)
+
+figure.diagram-container
+ .diagram
+ | fen:8/5p1p/2p1p1p1/1p1p2P1/p2P1P1P/P1P1P3/1P6/8:
+ figcaption The side to play will lose.
+
+p.
+ Also, space is important. On the following diagram any move of a white pawn
+ which is not on the edge wins.
+
+figure.diagram-container
+ .diagram
+ | fen:8/8/pppppppp/8/PPPPPPPP/8/8/8:
+ figcaption Any central advance wins.
--- /dev/null
+p.boxed
+ | Solo peones. Regla estándar.
+ | El primero que realiza una promoción gana.
+
+p.
+ Este juego puede sonar simple, pero como no tienes movimientos de espera,
+ ten cuidado con Zugzwang;)
+
+figure.diagram-container
+ .diagram
+ | fen:8/5p1p/2p1p1p1/1p1p2P1/p2P1P1P/P1P1P3/1P6/8:
+ figcaption El campamento al turno perderá.
+
+p.
+ El espacio también es importante. En el siguiente diagrama cualquier
+ movimiento de peón blanco no en el borde gana.
+
+figure.diagram-container
+ .diagram
+ | fen:8/8/pppppppp/8/PPPPPPPP/8/8/8:
+ figcaption Cualquier avance central gana.
--- /dev/null
+p.boxed
+ | Seulement des pions. Règle standard.
+ | Le premier à effectuer une promotion gagne.
+
+p.
+ Ce jeu peut paraître simple, mais puisque vous ne disposez d'aucun coup
+ d'attente, faites attention au Zugzwang ;)
+
+figure.diagram-container
+ .diagram
+ | fen:8/5p1p/2p1p1p1/1p1p2P1/p2P1P1P/P1P1P3/1P6/8:
+ figcaption Le camp au trait perdra.
+
+p.
+ L'espace est important aussi. Sur le diagramme suivant n'importe quel coup
+ de pion blanc ne se trouvant pas sur le bord gagne.
+
+figure.diagram-container
+ .diagram
+ | fen:8/8/pppppppp/8/PPPPPPPP/8/8/8:
+ figcaption N'importe quelle avancée centrale gagne.
--- /dev/null
+p.boxed.
+ At each turn play first a normal move, and then exchange two pieces'
+ positions by making a capture.
+
+ol
+ li.
+ Each turn is made of a standard move and a swap move (see 2).
+ At the first white move, only the regular move is done.
+ li.
+ A swap move is a two piece swap of positions. One piece is a player's
+ piece, and the other is on a square where the first piece could move
+ (if the square was empty) or take (if the square had an opponent piece).
+ li If a player cannot make the regular move or the swap, he loses.
+
+p.
+ On the diagram the queen first takes the g7 pawn, and then swap its
+ position with the g8 knight: checkmate.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:r1pqk1n1/pn1pppp1/2p5/8/8/2PPPK2/BP4Q1/N1R2P2:
+ .diagram.diag22
+ | fen:r1pqk1Q1/pn1pppn1/2p5/8/8/2PPPK2/BP6/N1R2P2:
+ figcaption Before and after 1.Qxg7,Sg7g8#
+
+h3 Source
+
+p
+ a(href="https://www.chessvariants.com/diffmove.dir/balancedswap.html")
+ | Balanced Swap Chess
+ | on chessvariants.com. Here is an
+ a(href="/variants/Swap/game.pgn") example game
+ | played by the variant's creator.
+
+p Inventor: Joao P. Neto (1998)
--- /dev/null
+p.boxed.
+ En cada turno, primero haga un movimiento normal, luego cambie las posiciones de
+ dos piezas por capturar.
+
+ol
+ li.
+ Cada ronda consta de un movimiento estándar seguido de un movimiento de
+ intercambio (ver 2). Durante el primer movimiento blanco, solo se realiza
+ el movimiento normal.
+ li.
+ Un intercambio es un intercambio de posiciones de dos piezas. Una de las
+ dos es la pieza del jugador, y la otra está en una casilla donde la
+ primera podría ir (si la casilla estaba vacía) o capturar (si la
+ casilla estaba ocupada por el oponente).
+ li.
+ Si un jugador no puede hacer un movimiento estándar o un movimiento de
+ intercambio, pierde.
+
+p.
+ En el siguiente diagrama, la dama primero toma el peón g7, luego
+ intercambia su posición con el caballo g8: jaque mate.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:r1pqk1n1/pn1pppp1/2p5/8/8/2PPPK2/BP4Q1/N1R2P2:
+ .diagram.diag22
+ | fen:r1pqk1Q1/pn1pppn1/2p5/8/8/2PPPK2/BP6/N1R2P2:
+ figcaption Antes y después 1.Qxg7,Sg7g8#
+
+h3 Fuente
+
+p
+ | La
+ a(href="https://www.chessvariants.com/diffmove.dir/balancedswap.html")
+ | variante Balanced Swap
+ | en chessvariants.com. Aquí hay una
+ a(href="/variants/Swap/game.pgn") partida ejemplo
+ | jugada por el creador de la variante.
+
+p Inventor: Joao P. Neto (1998)
--- /dev/null
+p.boxed.
+ À chaque tour jouez d'abord un coup normal, puis échangez les positions de
+ deux pièces en effectuant une capture.
+
+ol
+ li.
+ Chaque tour consiste en un coup standard suivi d'un coup d'échange
+ (voir 2). Lors du premier coup blanc, seul le coup normal est joué.
+ li.
+ Un coup d'échange est un échange des positions de deux pièces. Une des
+ deux est une pièce du joueur, et l'autre est sur une case où la première
+ pourrait aller (si la case était vide) ou capturer (si la case était
+ occupée par l'adversaire).
+ li Si un joueur ne peut effectuer de coup standard ou d'échange, il perd.
+
+p.
+ Sur le diagramme suivant la dame prend d'abord le pion g7, puis échange sa
+ position avec le cavalier g8 : échec et mat.
+
+figure.diagram-container
+ .diagram.diag12
+ | fen:r1pqk1n1/pn1pppp1/2p5/8/8/2PPPK2/BP4Q1/N1R2P2:
+ .diagram.diag22
+ | fen:r1pqk1Q1/pn1pppn1/2p5/8/8/2PPPK2/BP6/N1R2P2:
+ figcaption Avant et après 1.Qxg7,Sg7g8#
+
+h3 Source
+
+p
+ | La
+ a(href="https://www.chessvariants.com/diffmove.dir/balancedswap.html")
+ | variante Balanced Swap
+ | sur chessvariants.com. Voici une
+ a(href="/variants/Swap/game.pgn") partie exemple
+ | jouée par le créateur de la variante.
+
+p Inventeur : Joao P. Neto (1998)
--- /dev/null
+p.boxed
+ | In addition to standard moves, a piece can be exchanged
+ | with an adjacent friendly unit.
+
+p.
+ Instead of a normal move, a piece may be exchanged with an adjacent
+ friendly one. Switching can move pawns until rank 1 or final rank.
+ In the first case a 2-squares jump is possible, and in the second a
+ promotion occurs.
+ Switching must involves two different units.
+ Switching while the king is under check is not allowed.
+
+p.
+ Notation: a switch move is marked by the capital letter 'S' followed by
+ the exchanged squares. Example in diagram: Sb8c8.
+
+figure.diagram-container
+ .diagram
+ | fen:1RB3k1/5ppp/8/8/8/8/8/K7:
+ figcaption White can mate in 1 by switching b8-c8
+
+p.
+ Note: to trigger a switch move involving the king, select the non-royal
+ piece first (to not interfere with castling).
+
+h3 Source
+
+p
+ a(href="https://www.chessvariants.com/diffmove.dir/switching.html") Switching chess
+ | on chessvariants.com.
+
+p Inventor: Tony Quintanilla (2004)
--- /dev/null
+p.boxed
+ | Además de los movimientos habituales,
+ | se puede cambiar una pieza con una vecina.
+
+p.
+ En lugar de hacer un movimiento normal, se puede cambiar una pieza con una
+ pieza amiga contigua.
+ El intercambio puede mover peones en la primera o última fila:
+ en el primer caso es posible un salto de dos casillas, y en el
+ segundo se lleva a cabo una promoción.
+ El intercambio debe involucrar dos piezas de diferentes naturalezas.
+ El intercambio está prohibido cuando el rey está en jaque.
+
+p.
+ Notación: un movimiento de cambio se indica con la letra mayúscula 'S'
+ seguido por las casillas intercambiadas. Por ejemplo en el diagrama: Sb8c8.
+
+figure.diagram-container
+ .diagram
+ | fen:1RB3k1/5ppp/8/8/8/8/8/K7:
+ figcaption Las blancas pueden dar jaque mate en 1 por intercambio b8-c8.
+
+p.
+ Nota: para activar un intercambio que involucre al rey, seleccione la pieza
+ no real primero (para no interferir con el enroque).
+
+h3 Fuente
+
+p
+ | La
+ a(href="https://www.chessvariants.com/diffmove.dir/switching.html")
+ | variante Switching
+ | en chessvariants.com.
+
+p Inventor: Tony Quintanilla (2004)
--- /dev/null
+p.boxed
+ | En plus des coups habituels, une pièce peut être échangée avec une voisine.
+
+p.
+ Au lieu d'effectuer un coup normal, une pièce peut être échangée avec une
+ pièce amie adjacente.
+ L'échange peut déplacer des pions sur la première ou dernière rangée :
+ dans le premier cas un saut de deux cases est alors possible, et dans le
+ second une promotion s'effectue.
+ L'échange doit impliquer deux pièces de différentes natures.
+ L'échange est interdit quand le roi est en échec.
+
+p.
+ Notation : un coup d'échange est indiqué par la lettre majuscule 'S'
+ suivie des cases échangées. Par exemple sur le diagramme : Sb8c8.
+
+figure.diagram-container
+ .diagram
+ | fen:1RB3k1/5ppp/8/8/8/8/8/K7:
+ figcaption Les blancs peuvent mater en 1 par l'échange b8-c8
+
+p.
+ Note : pour déclencher un échange impliquant le roi, sélectionnez la pièce
+ non royale d'abord (pour ne pas interférer avec le roque).
+
+h3 Source
+
+p
+ | La
+ a(href="https://www.chessvariants.com/diffmove.dir/switching.html")
+ | variante Switching
+ | sur chessvariants.com.
+
+p Inventeur : Tony Quintanilla (2004)
play(move) {
move.flags = JSON.stringify(this.aggregateFlags());
- move.turn = this.turn + this.subTurn;
+ move.turn = [this.turn, this.subTurn];
V.PlayOnBoard(this.board, move);
const epSq = this.getEpSquare(move);
if (this.movesCount == 0) {
this.epSquares.push([epSq]);
move.checkOnSubturn1 = true;
this.movesCount++;
- } else {
+ }
+ else {
if (this.subTurn == 2) {
this.turn = V.GetOppCol(this.turn);
let lastEpsq = this.epSquares[this.epSquares.length - 1];
lastEpsq.push(epSq);
- } else {
+ }
+ else {
this.epSquares.push([epSq]);
this.movesCount++;
}
}
postPlay(move) {
- const c = move.turn.charAt(0);
+ const c = move.turn[0];
const piece = move.vanish[0].p;
const firstRank = c == "w" ? V.size.x - 1 : 0;
) {
const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1);
this.castleFlags[c][flagIdx] = V.size.y;
- } else if (
+ }
+ else if (
move.end.x == oppFirstRank && //we took opponent rook?
this.castleFlags[oppCol].includes(move.end.y)
) {
this.epSquares.pop();
// Moves counter was just incremented:
this.movesCount--;
- } else {
+ }
+ else {
// Undo the second half of a move
let lastEpsq = this.epSquares[this.epSquares.length - 1];
lastEpsq.pop();
}
this.turn = move.turn[0];
- this.subTurn = parseInt(move.turn[1]);
+ this.subTurn = move.turn[1];
super.postUndo(move);
}
else {
this.epSquares.push([epSq]);
this.movesCount++;
- if (this.movesCount == 1) this.turn = "b";
+ if (
+ this.movesCount == 1 ||
+ // King is captured at subTurn 1?
+ (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+ ) {
+ this.turn = "b";
+ }
}
if (this.movesCount > 1) this.subTurn = 3 - this.subTurn;
this.postPlay(move);
return;
}
const oppCol = V.GetOppCol(c);
- if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+ if (move.vanish.length == 2 && move.vanish[1].p == V.KING) {
// Opponent's king is captured, game over
this.kingPos[oppCol] = [-1, -1];
+ move.captureKing = true; //for undo
+ }
const oppFirstRank = V.size.x - 1 - firstRank;
if (
move.start.x == firstRank && //our rook moves?
undo(move) {
this.disaggregateFlags(JSON.parse(move.flags));
V.UndoOnBoard(this.board, move);
- if (this.subTurn == 2 || this.movesCount == 1) {
+ if (this.subTurn == 2 || this.movesCount == 1 || !!move.captureKing) {
this.epSquares.pop();
this.movesCount--;
if (this.movesCount == 0) this.turn = "w";
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 {
+ if (
+ this.subTurn == 2 ||
+ // King captured
+ (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+ ) {
+ this.turn = 'b';
+ this.subTurn = 1;
+ }
+ else this.subTurn = 2;
+ }
+ else {
this.turn = 'w';
this.movesCount++;
}
const piece = move.vanish[0].p;
if (piece == V.KING)
this.kingPos[c] = [move.appear[0].x, move.appear[0].y];
- if (move.vanish.length == 2 && move.vanish[1].p == V.KING)
+ if (move.vanish.length == 2 && move.vanish[1].p == V.KING) {
// Opponent's king is captured, game over
this.kingPos[move.vanish[1].c] = [-1, -1];
+ move.captureKing = true; //for undo
+ }
this.updateCastleFlags(move, piece);
}
}
else {
this.turn = 'w';
- this.subTurn = 2;
+ this.subTurn = (!move.captureKing ? 2 : 1);
}
this.postUndo(move);
}
--- /dev/null
+import { ChessRules } from "@/base_rules";
+
+export class PawnsRules extends ChessRules {
+ static get PawnSpecs() {
+ return Object.assign(
+ {},
+ ChessRules.PawnSpecs,
+ // The promotion piece doesn't matter, the game is won
+ { promotions: [V.QUEEN] }
+ );
+ }
+
+ static get HasFlags() {
+ return false;
+ }
+
+ static GenRandInitFen() {
+ return "8/pppppppp/8/8/8/8/PPPPPPPP/8 w 0 -";
+ }
+
+ filterValid(moves) {
+ return moves;
+ }
+
+ getCheckSquares() {
+ return [];
+ }
+
+ getCurrentScore() {
+ const oppCol = V.GetOppCol(this.turn);
+ if (this.board.some(b =>
+ b.some(cell => cell[0] == oppCol && cell[1] != V.PAWN))
+ ) {
+ return (oppCol == 'w' ? "1-0" : "0-1");
+ }
+ if (!this.atLeastOneMove()) return "1/2";
+ return "*";
+ }
+};
import { ChessRules, PiPo, Move } from "@/base_rules";
+import { SuicideRules } from "@/variants/Suicide";
export class SuctionRules extends ChessRules {
static get PawnSpecs() {
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);
static GenRandInitFen(randomness) {
// Add empty cmove:
- return ChessRules.GenRandInitFen(randomness).slice(0, -6) + "- -";
+ return SuicideRules.GenRandInitFen(randomness) + " -";
}
getCmoveFen() {
--- /dev/null
+import { ChessRules, PiPo } from "@/base_rules";
+import { randInt } from "@/utils/alea";
+
+export class SwapRules extends ChessRules {
+ setOtherVariables(fen) {
+ super.setOtherVariables(fen);
+ // Local stack of swaps
+ this.swaps = [];
+ const smove = V.ParseFen(fen).smove;
+ if (smove == "-") this.swaps.push(null);
+ else {
+ this.swaps.push({
+ start: ChessRules.SquareToCoords(smove.substr(0, 2)),
+ end: ChessRules.SquareToCoords(smove.substr(2))
+ });
+ }
+ this.subTurn = 1;
+ }
+
+ static ParseFen(fen) {
+ return Object.assign(
+ ChessRules.ParseFen(fen),
+ { smove: fen.split(" ")[5] }
+ );
+ }
+
+ 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;
+ }
+
+ getPPpath(m) {
+ if (m.vanish.length == 1) return super.getPPpath(m);
+ // Swap promotion:
+ return m.appear[1].c + m.appear[1].p;
+ }
+
+ getSwapMoves([x1, y1], [x2, y2]) {
+ let move = super.getBasicMove([x1, y1], [x2, y2]);
+ move.appear.push(
+ new PiPo({
+ x: x1,
+ y: y1,
+ c: this.getColor(x2, y2),
+ p: this.getPiece(x2, y2)
+ })
+ );
+ const lastRank = (move.appear[1].c == 'w' ? 0 : 7);
+ if (move.appear[1].p == V.PAWN && move.appear[1].x == lastRank) {
+ // Promotion:
+ move.appear[1].p = V.ROOK;
+ let moves = [move];
+ [V.KNIGHT, V.BISHOP, V.QUEEN].forEach(p => {
+ let m = JSON.parse(JSON.stringify(move));
+ m.appear[1].p = p;
+ moves.push(m);
+ });
+ return moves;
+ }
+ return [move];
+ }
+
+ getPotentialMovesFrom([x, y]) {
+ if (this.subTurn == 1) return super.getPotentialMovesFrom([x, y]);
+ // SubTurn == 2: only swaps
+ let moves = [];
+ const color = this.turn;
+ const piece = this.getPiece(x, y);
+ const addSmoves = (i, j) => {
+ if (this.getPiece(i, j) != piece)
+ Array.prototype.push.apply(moves, this.getSwapMoves([x, y], [i, j]));
+ };
+ switch (piece) {
+ case V.PAWN: {
+ const forward = (color == 'w' ? -1 : 1);
+ const startRank = (color == 'w' ? [6, 7] : [0, 1]);
+ if (
+ x == startRank &&
+ this.board[x + forward][y] == V.EMPTY &&
+ this.board[x + 2 * forward][y] != V.EMPTY
+ ) {
+ // Swap using 2 squares move
+ addSmoves(x + 2 * forward, y);
+ }
+ for (let shift of [-1, 0, 1]) {
+ const [i, j] = [x + forward, y + shift];
+ if (V.OnBoard(i, j) && this.board[i][j] != V.EMPTY) addSmoves(i, j);
+ }
+ break;
+ }
+ case V.KNIGHT:
+ V.steps[V.KNIGHT].forEach(s => {
+ const [i, j] = [x + s[0], y + s[1]];
+ if (V.OnBoard(i, j) && this.board[i][j] != V.EMPTY) addSmoves(i, j);
+ });
+ break;
+ case V.KING:
+ V.steps[V.ROOK].concat(V.steps[V.BISHOP]).forEach(s => {
+ const [i, j] = [x + s[0], y + s[1]];
+ if (V.OnBoard(i, j) && this.board[i][j] != V.EMPTY) addSmoves(i, j);
+ });
+ break;
+ case V.ROOK:
+ case V.BISHOP:
+ case V.QUEEN: {
+ const steps =
+ piece != V.QUEEN
+ ? V.steps[piece]
+ : V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
+ steps.forEach(s => {
+ let [i, j] = [x + s[0], y + s[1]];
+ while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) {
+ i += s[0];
+ j += s[1];
+ }
+ if (V.OnBoard(i, j) && this.board[i][j] != V.EMPTY) addSmoves(i, j);
+ });
+ break;
+ }
+ }
+ return moves;
+ }
+
+ // Does m2 un-do m1 ? (to disallow undoing swaps)
+ oppositeSwaps(m1, m2) {
+ return (
+ !!m1 &&
+ 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) {
+ const fmoves = super.filterValid(moves);
+ if (this.subTurn == 1) return fmoves;
+ return fmoves.filter(m => {
+ const L = this.swaps.length; //at least 1: init from FEN
+ return !this.oppositeSwaps(this.swaps[L - 1], m);
+ });
+ }
+
+ static GenRandInitFen(randomness) {
+ // Add empty smove:
+ return ChessRules.GenRandInitFen(randomness) + " -";
+ }
+
+ getSmoveFen() {
+ const L = this.swaps.length;
+ return (
+ !this.swaps[L - 1]
+ ? "-"
+ : ChessRules.CoordsToSquare(this.swaps[L - 1].start) +
+ ChessRules.CoordsToSquare(this.swaps[L - 1].end)
+ );
+ }
+
+ getFen() {
+ return super.getFen() + " " + this.getSmoveFen();
+ }
+
+ getFenForRepeat() {
+ return super.getFenForRepeat() + "_" + this.getSmoveFen();
+ }
+
+ getCurrentScore() {
+ const L = this.swaps.length;
+ if (this.movesCount >= 2 && !this.swaps[L-1])
+ // Opponent had no swap moves: I win
+ return (this.turn == "w" ? "1-0" : "0-1");
+ if (this.atLeastOneMove()) return "*";
+ // No valid move: I lose
+ return (this.turn == "w" ? "0-1" : "1-0");
+ }
+
+ noSwapAfter(move) {
+ this.subTurn = 2;
+ const res = !this.atLeastOneMove();
+ this.subTurn = 1;
+ return res;
+ }
+
+ play(move) {
+ move.flags = JSON.stringify(this.aggregateFlags());
+ move.turn = [this.turn, this.subTurn];
+ V.PlayOnBoard(this.board, move);
+ let epSq = undefined;
+ if (this.subTurn == 1) epSq = this.getEpSquare(move);
+ if (this.movesCount == 0) {
+ // First move in game
+ this.turn = "b";
+ this.epSquares.push(epSq);
+ this.movesCount = 1;
+ }
+ // Any swap available after move? If no, skip subturn 2
+ else if (this.subTurn == 1 && this.noSwapAfter(move)) {
+ this.turn = V.GetOppCol(this.turn);
+ this.epSquares.push(epSq);
+ move.noSwap = true;
+ this.movesCount++;
+ }
+ else {
+ if (this.subTurn == 2) {
+ this.turn = V.GetOppCol(this.turn);
+ this.swaps.push({ start: move.start, end: move.end });
+ }
+ else {
+ this.epSquares.push(epSq);
+ this.movesCount++;
+ }
+ this.subTurn = 3 - this.subTurn;
+ }
+ this.postPlay(move);
+ }
+
+ postPlay(move) {
+ const firstRank = { 7: 'w', 0: 'b' };
+ // Did some king move?
+ move.appear.forEach(a => {
+ if (a.p == V.KING) {
+ this.kingPos[a.c] = [a.x, a.y];
+ this.castleFlags[a.c] = [V.size.y, V.size.y];
+ }
+ });
+ for (let coords of [move.start, move.end]) {
+ if (
+ Object.keys(firstRank).includes(coords.x) &&
+ this.castleFlags[firstRank[coords.x]].includes(coords.y)
+ ) {
+ const c = firstRank[coords.x];
+ const flagIdx = (coords.y == this.castleFlags[c][0] ? 0 : 1);
+ this.castleFlags[c][flagIdx] = V.size.y;
+ }
+ }
+ }
+
+ undo(move) {
+ this.disaggregateFlags(JSON.parse(move.flags));
+ V.UndoOnBoard(this.board, move);
+ if (move.turn[1] == 1) {
+ // The move may not be full, but is fully undone:
+ this.epSquares.pop();
+ // Moves counter was just incremented:
+ this.movesCount--;
+ }
+ else
+ // Undo the second half of a move
+ this.swaps.pop();
+ this.turn = move.turn[0];
+ this.subTurn = move.turn[1];
+ this.postUndo(move);
+ }
+
+ postUndo(move) {
+ // Did some king move?
+ move.vanish.forEach(v => {
+ if (v.p == V.KING) this.kingPos[v.c] = [v.x, v.y];
+ });
+ }
+
+ // No alpha-beta here, just adapted min-max at depth 2(+1)
+ getComputerMove() {
+ const maxeval = V.INFINITY;
+ const color = this.turn;
+ const oppCol = V.GetOppCol(this.turn);
+
+ // Search best (half) move for opponent turn (TODO: a bit too slow)
+// const getBestMoveEval = () => {
+// let score = this.getCurrentScore();
+// if (score != "*") return maxeval * (score == "1-0" ? 1 : -1);
+// let moves = this.getAllValidMoves();
+// let res = (oppCol == "w" ? -maxeval : maxeval);
+// for (let m of moves) {
+// this.play(m);
+// score = this.getCurrentScore();
+// // Now turn is oppCol,2 if m allow a swap and movesCount >= 2
+// // Otherwise it's color,1. In both cases the next test makes sense
+// if (score != "*") {
+// // Game over
+// this.undo(m);
+// return maxeval * (score == "1-0" ? 1 : -1);
+// }
+// const evalPos = this.evalPosition();
+// res = oppCol == "w" ? Math.max(res, evalPos) : Math.min(res, evalPos);
+// this.undo(m);
+// }
+// return res;
+// };
+
+ const moves11 = this.getAllValidMoves();
+ if (this.movesCount == 0)
+ // Just play first move at random:
+ return moves11[randInt(moves11.length)];
+ let bestMove = undefined;
+ // Rank moves using a min-max at depth 2
+ for (let i = 0; i < moves11.length; i++) {
+ this.play(moves11[i]);
+ if (!!moves11[i].noSwap) {
+ // I lose
+ if (!bestMove) bestMove = {
+ moves: moves11[i],
+ eval: (color == 'w' ? -maxeval : maxeval)
+ };
+ }
+ else {
+ let moves12 = this.getAllValidMoves();
+ for (let j = 0; j < moves12.length; j++) {
+ this.play(moves12[j]);
+// const evalMove = getBestMoveEval() + 0.05 - Math.random() / 10;
+ const evalMove = this.evalPosition() + 0.05 - Math.random() / 10;
+ if (
+ !bestMove ||
+ (color == 'w' && evalMove > bestMove.eval) ||
+ (color == 'b' && evalMove < bestMove.eval)
+ ) {
+ bestMove = {
+ moves: [moves11[i], moves12[j]],
+ eval: evalMove
+ };
+ }
+ this.undo(moves12[j]);
+ }
+ }
+ this.undo(moves11[i]);
+ }
+ return bestMove.moves;
+ }
+
+ getNotation(move) {
+ if (move.appear.length == 1)
+ // Normal move
+ return super.getNotation(move);
+ if (move.appear[0].p == V.KING && move.appear[1].p == V.ROOK)
+ // Castle
+ return (move.end.y < move.start.y ? "0-0-0" : "0-0");
+ // Swap
+ return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end);
+ }
+};
--- /dev/null
+import { ChessRules, Move, PiPo } from "@/base_rules";
+
+export class SwitchingRules extends ChessRules {
+ // Build switch move between squares x1,y1 and x2,y2
+ getSwitchMove_s([x1, y1], [x2, y2]) {
+ const c = this.getColor(x1, y1); //same as color at square 2
+ const p1 = this.getPiece(x1, y1);
+ const p2 = this.getPiece(x2, y2);
+ let move = new Move({
+ appear: [
+ new PiPo({ x: x2, y: y2, c: c, p: p1 }),
+ new PiPo({ x: x1, y: y1, c: c, p: p2 })
+ ],
+ vanish: [
+ new PiPo({ x: x1, y: y1, c: c, p: p1 }),
+ new PiPo({ x: x2, y: y2, c: c, p: p2 })
+ ]
+ });
+ // Move completion: promote switched pawns (as in Magnetic)
+ const lastRank = (c == "w" ? 0 : V.size.x - 1);
+ let moves = [];
+ if ((p1 == V.PAWN && x2 == lastRank) || (p2 == V.PAWN && x1 == lastRank)) {
+ const idx = (p1 == V.PAWN ? 0 : 1);
+ move.appear[idx].p = V.ROOK;
+ moves.push(move);
+ for (let piece of [V.KNIGHT, V.BISHOP, V.QUEEN]) {
+ let cmove = JSON.parse(JSON.stringify(move));
+ cmove.appear[idx].p = piece;
+ moves.push(cmove);
+ }
+ if (idx == 1) {
+ // Swap moves[i].appear[0] and [1] for moves presentation [TODO...]
+ moves.forEach(m => {
+ let tmp = m.appear[0];
+ m.appear[0] = m.appear[1];
+ m.appear[1] = tmp;
+ });
+ }
+ }
+ else
+ // Other cases
+ moves.push(move);
+ return moves;
+ }
+
+ getPotentialMovesFrom([x,y]) {
+ let moves = super.getPotentialMovesFrom([x,y]);
+ const piece = this.getPiece(x,y);
+ const color = this.turn;
+ const oppCol = V.GetOppCol(color);
+ const kp = this.kingPos[color];
+ // Add switches (if not under check, from anything but the king)
+ if (piece != V.KING && !this.isAttacked(kp, oppCol)) {
+ const steps = V.steps[V.ROOK].concat(V.steps[V.BISHOP]);
+ for (let step of steps) {
+ const [i, j] = [x+step[0], y+step[1]];
+ if (
+ V.OnBoard(i, j) &&
+ this.board[i][j] != V.EMPTY &&
+ this.getColor(i,j) == color &&
+ this.getPiece(i,j) != piece
+ ) {
+ const switchMove_s = this.getSwitchMove_s([x,y], [i,j]);
+ Array.prototype.push.apply(moves, switchMove_s);
+ }
+ }
+ }
+ return moves;
+ }
+
+ postPlay(move) {
+ // Did some king move?
+ move.appear.forEach(a => {
+ if (a.p == V.KING) {
+ this.kingPos[a.c] = [a.x, a.y];
+ this.castleFlags[a.c] = [V.size.y, V.size.y];
+ }
+ });
+ for (let coords of [move.start, move.end]) {
+ if (
+ Object.keys(firstRank).includes(coords.x) &&
+ this.castleFlags[firstRank[coords.x]].includes(coords.y)
+ ) {
+ const c = firstRank[coords.x];
+ const flagIdx = (coords.y == this.castleFlags[c][0] ? 0 : 1);
+ this.castleFlags[c][flagIdx] = V.size.y;
+ }
+ }
+ }
+
+ postUndo(move) {
+ // Did some king move?
+ move.vanish.forEach(v => {
+ if (v.p == V.KING) this.kingPos[v.c] = [v.x, v.y];
+ });
+ }
+
+ static get SEARCH_DEPTH() {
+ // Branching factor is quite high
+ return 2;
+ }
+
+ getAllPotentialMoves() {
+ // Since this function is used only for computer play,
+ // remove duplicate switches:
+ return super.getAllPotentialMoves().filter(m => {
+ return (
+ m.appear.length == 1 ||
+ (move.appear[0].p == V.KING && move.appear[1].p == V.ROOK) ||
+ (m.appear[1].x <= m.vanish[1].x && m.appear[1].y <= m.vanish[1].y)
+ );
+ });
+ }
+
+ getNotation(move) {
+ if (move.appear.length == 1)
+ // Normal move
+ return super.getNotation(move);
+ if (move.appear[0].p == V.KING && move.appear[1].p == V.ROOK)
+ // Castle
+ return (move.end.y < move.start.y ? "0-0-0" : "0-0");
+ // Switch
+ return "S" + V.CoordsToSquare(move.start) + V.CoordsToSquare(move.end);
+ }
+}
span {{ st.tr["Participant(s):"] }}
span(
v-for="p in Object.values(people)"
- v-if="participateInChat(p)"
+ v-if="!!p.name"
)
| {{ p.name }}
span.anonymous(v-if="someAnonymousPresent()") + @nonymous
)
);
},
- participateInChat: function(p) {
- return Object.keys(p.tmpIds).some(x => p.tmpIds[x].focus) && !!p.name;
- },
someAnonymousPresent: function() {
return (
Object.values(this.people).some(p =>
('Pacifist1', 'Convert & support (v1)'),
('Pacifist2', 'Convert & support (v2)'),
('Parachute', 'Landing on the board'),
+ ('Pawns', 'Reach the last rank'),
('Perfect', 'Powerful pieces'),
('Racingkings', 'Kings cross the 8x8 board'),
('Rampage', 'Move under cover'),
('Sittuyin', 'Burmese chess'),
('Suicide', 'Lose all pieces'),
('Suction', 'Attract opposite king'),
+ ('Swap', 'Dangerous captures'),
+ ('Switching', 'Exchange pieces'' positions'),
('Takenmake', 'Prolongated captures'),
('Teleport', 'Reposition pieces'),
('Tencubed', 'Four new pieces'),