From: Benjamin Auder Date: Wed, 25 Mar 2020 13:52:22 +0000 (+0100) Subject: Some fixes, and add 2 variants: Checkless and Parachute X-Git-Url: https://git.auder.net/img/app_dev.php/assets/parser.js?a=commitdiff_plain;h=0d5335de5c94d780e03ac0aa3279b731c69455cc;p=vchess.git Some fixes, and add 2 variants: Checkless and Parachute --- diff --git a/TODO b/TODO index 7d46a80c..8e81cc7a 100644 --- a/TODO +++ b/TODO @@ -1,17 +1,7 @@ # New variants -Landing pieces from empty board: -https://www.chessvariants.com/diffsetup.dir/unachess.html -Parachute v1 & 2 - -Generator variant, called "Matrix" ? -Peces on first rank never move but generate new pieces. Pawn don't generate. -A generator captured and replaced by a similar piece does not generate. -King does not generate. No castling. En passant possible? -Goal is still checkmate. +Maxima, Interweave, Roccoco Take(a)n(d)make : if capture a piece, take its power for the last of the turn and make a move like it. -If a pawn taken: direction of the capturer. +If a pawn taken: direction of the capturer, can capture enemy. -Dynamo chess - -Maxima, Interweave, Roccoco +Dynamo chess --> dur... diff --git a/client/src/base_rules.js b/client/src/base_rules.js index e1428399..37e756e9 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -224,9 +224,11 @@ export const ChessRules = class ChessRules { const s = move.start, e = move.end; if ( - Math.abs(s.x - e.x) == 2 && s.y == e.y && - (move.appear.length > 0 && move.appear[0].p == V.PAWN) + Math.abs(s.x - e.x) == 2 && + // Next conditions for variants like Atomic or Rifle, Recycle... + (move.appear.length > 0 && move.appear[0].p == V.PAWN) && + (move.vanish.length > 0 && move.vanish[0].p == V.PAWN) ) { return { x: (s.x + e.x) / 2, @@ -941,6 +943,7 @@ export const ChessRules = class ChessRules { } // Stop at the first move found + // TODO: not really, it explores all moves from a square but one would suffice. atLeastOneMove() { const color = this.turn; for (let i = 0; i < V.size.x; i++) { diff --git a/client/src/translations/about/en.pug b/client/src/translations/about/en.pug index 6d551949..6e888b9b 100644 --- a/client/src/translations/about/en.pug +++ b/client/src/translations/about/en.pug @@ -50,4 +50,5 @@ h3 Related links div a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr span  (in French) + a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net a(href="https://brainking.com/") brainking.com diff --git a/client/src/translations/about/es.pug b/client/src/translations/about/es.pug index 66aa4298..0986e016 100644 --- a/client/src/translations/about/es.pug +++ b/client/src/translations/about/es.pug @@ -47,4 +47,5 @@ h3 Enlaces relacionados div a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr span  (en francés) + a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net a(href="https://brainking.com/") brainking.com diff --git a/client/src/translations/about/fr.pug b/client/src/translations/about/fr.pug index b4236030..849aad13 100644 --- a/client/src/translations/about/fr.pug +++ b/client/src/translations/about/fr.pug @@ -46,4 +46,5 @@ h3 Liens connexes a(href="https://musketeerchess.net/home/index.html") musketeerchess.net a(href="https://schemingmind.com/") schemingmind.com a(href="https://echekk.fr/spip.php?page=rubrique&id_rubrique=1") echekk.fr + a(href="http://www.strategems.net/sections/fairy_defs.html") strategems.net a(href="https://brainking.com/") brainking.com diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 75d9b89e..7ce045ff 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -177,6 +177,7 @@ export const translations = { "Keep antiking in check (v2)": "Keep antiking in check (v2)", "Kings cross the 8x8 board": "Kings cross the 8x8 board", "Kings cross the 11x11 board": "Kings cross the 11x11 board", + "Landing on the board": "Landing on the board", "Laws of attraction": "Laws of attraction", "Long jumps over pieces": "Long jumps over pieces", "Lose all pieces": "Lose all pieces", @@ -189,6 +190,7 @@ export const translations = { "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", "Play at the same time": "Play at the same time", "Powerful pieces": "Powerful pieces", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 465f39fb..40ba5c87 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -177,6 +177,7 @@ export const translations = { "Keep antiking in check (v2)": "Mantener el antirey en jaque (v2)", "Kings cross the 8x8 board": "Los reyes cruzan el 8x8 tablero", "Kings cross the 11x11 board": "Los reyes cruzan el 11x11 tablero", + "Landing on the board": "Aterrizando en el tablero", "Laws of attraction": "Las leyes de las atracciones", "Long jumps over pieces": "Saltos largos sobre las piezas", "Lose all pieces": "Perder todas las piezas", @@ -189,6 +190,7 @@ export const translations = { "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", "Play at the same time": "Jugar al mismo tiempo", "Powerful pieces": "Piezas poderosas", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 62dd3ba0..8954f7b1 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -177,6 +177,7 @@ export const translations = { "Keep antiking in check (v2)": "Gardez l'antiroi en échec (v2)", "Kings cross the 8x8 board": "Les rois traversent l'échiquier 8x8", "Kings cross the 11x11 board": "Les rois traversent l'échiquier 11x11", + "Landing on the board": "Débarquement sur l'échiquier", "Laws of attraction": "Les lois de l'attraction", "Long jumps over pieces": "Sauts longs par dessus les pièces", "Lose all pieces": "Perdez toutes les pièces", @@ -189,6 +190,7 @@ export const translations = { "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", "Play at the same time": "Jouer en même temps", "Powerful pieces": "Pièces puissantes", diff --git a/client/src/translations/rules/Checkless/en.pug b/client/src/translations/rules/Checkless/en.pug new file mode 100644 index 00000000..ce3ca48e --- /dev/null +++ b/client/src/translations/rules/Checkless/en.pug @@ -0,0 +1,42 @@ +p.boxed + | Giving check is forbidden, unless it is a checkmate. + +p. + Neither player is allowed to give a check, with the exception of checkmate. + Thus, the king is much more powerful than in orthodox chess: as long as + he can (potentially) escape, he doesn't fear attacks. + So the king can be used to defend pieces in an unusual way. + +p. + On the following diagram, 1.Qxa6 threatens 2.Bb6 + with a mate to follow by Qxa7. + The black rook cannot take because it would check the white king. + +figure.diagram-container + .diagram + | fen:1k1r1b2/rP1b2p1/pQ1pp1nq/K4p1p/P4PnP/2PN2P1/3PP1B1/R1RN2B1: + figcaption 1.Qxd8+ is forbidden because 1...Bc8 would be possible. + +h3 Disambiguation + +p. + 1.Qf7# is checkmate on the left diagram, because if the king takes + then the rook on h8 gives check but not checkmate. + However, on the right diagram 1.Qf7+ runs into 1...Kxf7#, which is now + a legal move because the white king is checkmated. + +figure.diagram-container + .diagram.diag12 + | fen:K5kr/8/5Q2/8/8/8/8/8: + .diagram.diag22 + | fen:K5kr/RB6/5Q2/8/8/8/8/7b: + figcaption 1.Qf7 mates on the left, but not on the right. + +h3 Source + +p + a(href="https://www.chessvariants.com/usualeq.dir/checklss.html") + | Checkless chess + |  on chessvariants.com, and the + a(href="https://en.wikipedia.org/wiki/Checkless_chess") Wikipedia page + | . diff --git a/client/src/translations/rules/Checkless/es.pug b/client/src/translations/rules/Checkless/es.pug new file mode 100644 index 00000000..1f408604 --- /dev/null +++ b/client/src/translations/rules/Checkless/es.pug @@ -0,0 +1,43 @@ +p.boxed + | Se prohíbe el jaque, a menos que sea un jaque mate. + +p. + Ningún jugador tiene derecho a dar jaque, excepto el jaque mate. + Por lo tanto, el rey es claramente más poderoso que en el ajedrez ortodoxo: + mientras pueda (potencialmente) escapar, no tiene miedo a los ataques. + El rey puede ser usado para defender las piezas de una manera inusual. + +p. + En el siguiente diagrama, 1.Qxa6 amenaza a 2.Bb6 con un jaque mate + seguido de Qxa7. + La torre negra no puede tomar porque daría jaque al rey blanco. + +figure.diagram-container + .diagram + | fen:1k1r1b2/rP1b2p1/pQ1pp1nq/K4p1p/P4PnP/2PN2P1/3PP1B1/R1RN2B1: + figcaption 1.Qxd8+ está prohibido porque 1...Bc8 sería posible. + +h3 Desambiguación + +p. + 1.Qf7# mat en el diagrama de la izquierda, porque si el rey toma + la torre h8 da jaque pero no jaque mate. + Sin embargo, en el diagrama de la derecha 1.Qf7+ permite 1...Kxf7#, + que ahora es legal porque el rey blanco es en jaque. + +figure.diagram-container + .diagram.diag12 + | fen:K5kr/8/5Q2/8/8/8/8/8: + .diagram.diag22 + | fen:K5kr/RB6/5Q2/8/8/8/8/7b: + figcaption 1.Qf7 mate a la izquierda, pero no a la derecha. + +h3 Fuente + +p + | La + a(href="https://www.chessvariants.com/usualeq.dir/checklss.html") + | variante Checkless + |  en chessvariants.com, y la + a(href="https://en.wikipedia.org/wiki/Checkless_chess") página wikipedia + | . diff --git a/client/src/translations/rules/Checkless/fr.pug b/client/src/translations/rules/Checkless/fr.pug new file mode 100644 index 00000000..d42c9371 --- /dev/null +++ b/client/src/translations/rules/Checkless/fr.pug @@ -0,0 +1,43 @@ +p.boxed + | Donner échec est interdit, sauf si c'est un échec et mat. + +p. + Aucun joueur n'a le droit de donner échec, à l'exception du mat. + Ainsi, le roi est nettement plus puissant qu'aux échecs orthodoxes : + tant qu'il peut (potentiellement) s'échapper, il ne craint pas les attaques. + Le roi peut alors être utilisé pour défendre les pièces d'une manière + inhabituelle. + + p. + Sur le diagramme suivant, 1.Qxa6 menaçe 2.Bb6 avec un mat à suivre par Qxa7. + La tour noire ne peut pas prendre car elle mettrait le roi blanc en échec. + +figure.diagram-container + .diagram + | fen:1k1r1b2/rP1b2p1/pQ1pp1nq/K4p1p/P4PnP/2PN2P1/3PP1B1/R1RN2B1: + figcaption 1.Qxd8+ est interdit car 1...Bc8 serait possible. + +h3 Désambiguïsation + +p. + 1.Qf7# fait mat sur le diagramme de gauche, car si le roi prend alors + la tour h8 donne échec mais pas mat. + Cependant, sur le diagramme de droite 1.Qf7+ permet 1...Kxf7#, + qui est maintenant légal car le roi blanc est mat. + +figure.diagram-container + .diagram.diag12 + | fen:K5kr/8/5Q2/8/8/8/8/8: + .diagram.diag22 + | fen:K5kr/RB6/5Q2/8/8/8/8/7b: + figcaption 1.Qf7 mate à gauche, mais pas à droite. + +h3 Source + +p + | La + a(href="https://www.chessvariants.com/usualeq.dir/checklss.html") + | variante Checkless + |  sur chessvariants.com, et la + a(href="https://en.wikipedia.org/wiki/Checkless_chess") page Wikipedia + | . diff --git a/client/src/translations/rules/Parachute/en.pug b/client/src/translations/rules/Parachute/en.pug new file mode 100644 index 00000000..61ef9451 --- /dev/null +++ b/client/src/translations/rules/Parachute/en.pug @@ -0,0 +1,35 @@ +p.boxed + | The board is initially empty. + | Add a piece (not giving check) or move one at each turn. + +p. + The king can be added at any moment, but while he hasn't landed + no capture can be done. So you could move or land your king "into check" + if your opponent didn't land his king yet. + +p. + Giving check with a landed piece is forbidden + (assuming both kings are on the board). + +p. + Pawns can be landed on the four first ranks only. A pawn on the first + rank can jump two squares, and be captured en passant. + +figure.diagram-container + .diagram.diag12 + | fen:8/8/3b2r1/3R4/k3Q3/3R4/8/8: + .diagram.diag22 + | fen:8/8/K2b2r1/3R4/k1N1Q3/3R4/5q2/8: + figcaption. + Left: no white king, so the black king is safe. + Right: black to move, there is no way to avoid mate. + +h3 Source + +p + a(href="https://www.chessvariants.com/diffsetup.dir/unachess.html") + | Unachess II + |  on chessvariants.com. Unachess I gives a too large advantage + | to white, in the few games I could play. + +p Inventors: Jeff Miller and Edward Jackman (1995) diff --git a/client/src/translations/rules/Parachute/es.pug b/client/src/translations/rules/Parachute/es.pug new file mode 100644 index 00000000..fe04d4fe --- /dev/null +++ b/client/src/translations/rules/Parachute/es.pug @@ -0,0 +1,37 @@ +p.boxed + | El tablero está inicialmente vacío. + | Agregue una pieza (sin dar jaque) o mueva una cada turno. + +p. + Se puede agregar el rey en cualquier momento, pero hasta que haya aterrizado + las capturas están prohibidas. Para que puedas moverte o dejarte caer + su rey "en jaque" si el oponente aún no ha colocado el suyo. + +p. + No puedes dar jaque con una pieza en paracaídas + (asumiendo que los dos reyes están en el tablero). + +p. + Los peones se pueden soltar solo en las primeras cuatro filas. + Un peón en la primera fila puede saltar dos espacios, + y quedar atrapado en passant. + +figure.diagram-container + .diagram.diag12 + | fen:8/8/3b2r1/3R4/k3Q3/3R4/8/8: + .diagram.diag22 + | fen:8/8/K2b2r1/3R4/k1N1Q3/3R4/5q2/8: + figcaption. + Izquierda: no hay rey blanco, por lo que el rey negro está en seguridad. + Derecha: juegan las negras, no puede evitar el jaque mate. + +h3 Fuente + +p + | La + a(href="https://www.chessvariants.com/diffsetup.dir/unachess.html") + | variante Unachess II + |  en chessvariants.com. Unachess I da demasiada ventaja + | a las blancas, en los pocos juegos que he podido jugar. + +p Inventores: Jeff Miller y Edward Jackman (1995) diff --git a/client/src/translations/rules/Parachute/fr.pug b/client/src/translations/rules/Parachute/fr.pug new file mode 100644 index 00000000..c0a2a9f5 --- /dev/null +++ b/client/src/translations/rules/Parachute/fr.pug @@ -0,0 +1,37 @@ +p.boxed + | L'échiquier est initialement vide. + | Ajoutez une pièce (sans donner échec) ou déplacez-en une à chaque tour. + +p. + Le roi peut être ajouté à tout moment, mais tant qu'il n'a pas atterri + les captures sont interdites. Ainsi vous pourriez déplacer ou parachuter + votre roi "en échec" si l'adversaire n'a pas encore posé le sien. + +p. + On ne peut pas donner échec avec une pièce parachutée + (supposant que les deux rois sont sur l'échiquier). + +p. + Les pions peuvent être parachutés sur les quatre premières rangées seulement. + Un pion sur la première rangée peut sauter de deux cases, + et être capturé en passant. + +figure.diagram-container + .diagram.diag12 + | fen:8/8/3b2r1/3R4/k3Q3/3R4/8/8: + .diagram.diag22 + | fen:8/8/K2b2r1/3R4/k1N1Q3/3R4/5q2/8: + figcaption. + Gauche : pas de roi blanc, donc le roi noir est en sécurité. + Droite : trait aux noirs, on ne peut pas éviter le mat. + +h3 Source + +p + | La + a(href="https://www.chessvariants.com/diffsetup.dir/unachess.html") + | variante Unachess II + |  sur chessvariants.com. Unachess I donne un trop grand avantage + aux blancs, dans les quelques parties que j'ai pu jouer. + +p Inventeurs : Jeff Miller et Edward Jackman (1995) diff --git a/client/src/variants/Atomic.js b/client/src/variants/Atomic.js index 889f2dff..90089e8b 100644 --- a/client/src/variants/Atomic.js +++ b/client/src/variants/Atomic.js @@ -1,13 +1,6 @@ import { ChessRules, PiPo } from "@/base_rules"; export class AtomicRules extends ChessRules { - getEpSquare(moveOrSquare) { - if (typeof moveOrSquare !== "object" || moveOrSquare.appear.length > 0) - return super.getEpSquare(moveOrSquare); - // Capturing move: no en-passant - return undefined; - } - getPotentialMovesFrom([x, y]) { let moves = super.getPotentialMovesFrom([x, y]); diff --git a/client/src/variants/Ball.js b/client/src/variants/Ball.js index cd504a92..85e33e61 100644 --- a/client/src/variants/Ball.js +++ b/client/src/variants/Ball.js @@ -14,9 +14,6 @@ export class BallRules extends ChessRules { static get HasFlags() { return false; } - static get HasCastle() { - return false; - } static get CHAMPION() { return 'c'; diff --git a/client/src/variants/Baroque.js b/client/src/variants/Baroque.js index 92bae77f..031ea72c 100644 --- a/client/src/variants/Baroque.js +++ b/client/src/variants/Baroque.js @@ -7,10 +7,6 @@ export class BaroqueRules extends ChessRules { return false; } - static get HasCastle() { - return false; - } - static get HasEnpassant() { return false; } diff --git a/client/src/variants/Checkless.js b/client/src/variants/Checkless.js new file mode 100644 index 00000000..88612143 --- /dev/null +++ b/client/src/variants/Checkless.js @@ -0,0 +1,44 @@ +import { ChessRules } from "@/base_rules"; + +export class ChecklessRules extends ChessRules { + // Cannot use super.atLeastOneMove: lead to infinite recursion + atLeastOneMove_aux() { + const color = this.turn; + const oppCol = V.GetOppCol(color); + for (let i = 0; i < V.size.x; i++) { + for (let j = 0; j < V.size.y; j++) { + if (this.getColor(i, j) == color) { + const moves = this.getPotentialMovesFrom([i, j]); + if (moves.length > 0) { + for (let k = 0; k < moves.length; k++) { + let res = false; + this.play(moves[k]); + res = !this.underCheck(color) && !this.underCheck(oppCol); + this.undo(moves[k]); + if (res) return true; + } + } + } + } + } + return false; + } + + filterValid(moves) { + if (moves.length == 0) return []; + const color = this.turn; + const oppCol = V.GetOppCol(color); + return moves.filter(m => { + this.play(m); + // Move shouldn't result in self-check: + let res = !this.underCheck(color); + if (res) { + const checkOpp = this.underCheck(oppCol); + // If checking the opponent, he shouldn't have any legal move: + res = !checkOpp || checkOpp && !this.atLeastOneMove_aux(); + } + this.undo(m); + return res; + }); + } +}; diff --git a/client/src/variants/Crazyhouse.js b/client/src/variants/Crazyhouse.js index 2307eaff..576f7d2b 100644 --- a/client/src/variants/Crazyhouse.js +++ b/client/src/variants/Crazyhouse.js @@ -31,13 +31,6 @@ export class CrazyhouseRules extends ChessRules { ); } - getEpSquare(moveOrSquare) { - if (typeof moveOrSquare !== "object" || moveOrSquare.vanish.length > 0) - return super.getEpSquare(moveOrSquare); - // Landing move: no en-passant - return undefined; - } - static GenRandInitFen(randomness) { return ChessRules.GenRandInitFen(randomness) + " 0000000000 -"; } diff --git a/client/src/variants/Dynamo.js b/client/src/variants/Dynamo.js new file mode 100644 index 00000000..c76c040b --- /dev/null +++ b/client/src/variants/Dynamo.js @@ -0,0 +1,5 @@ +import { ChessRules } from "@/base_rules"; + +export class DynamoRules extends ChessRules { + // TODO +}; diff --git a/client/src/variants/Hidden.js b/client/src/variants/Hidden.js index ef5b0bb7..2fcab249 100644 --- a/client/src/variants/Hidden.js +++ b/client/src/variants/Hidden.js @@ -7,10 +7,6 @@ export class HiddenRules extends ChessRules { return false; } - static get HasCastle() { - return false; - } - static get HasEnpassant() { return false; } diff --git a/client/src/variants/Marseille.js b/client/src/variants/Marseille.js index ed9965b0..940d9ba9 100644 --- a/client/src/variants/Marseille.js +++ b/client/src/variants/Marseille.js @@ -124,7 +124,7 @@ export class MarseilleRules extends ChessRules { if (piece == V.KING && move.appear.length > 0) { this.kingPos[c][0] = move.appear[0].x; this.kingPos[c][1] = move.appear[0].y; - if (V.HasCastle) this.castleFlags[c] = [V.size.y, V.size.y]; + this.castleFlags[c] = [V.size.y, V.size.y]; return; } const oppCol = V.GetOppCol(c); diff --git a/client/src/variants/Parachute.js b/client/src/variants/Parachute.js new file mode 100644 index 00000000..07186352 --- /dev/null +++ b/client/src/variants/Parachute.js @@ -0,0 +1,210 @@ +import { ChessRules, PiPo, Move } from "@/base_rules"; + +export class ParachuteRules extends ChessRules { + static get HasFlags() { + return false; + } + + static IsGoodFen(fen) { + if (!ChessRules.IsGoodFen(fen)) return false; + const fenParsed = V.ParseFen(fen); + // 5) Check reserves + if (!fenParsed.reserve || !fenParsed.reserve.match(/^[0-9]{12,12}$/)) + return false; + return true; + } + + static ParseFen(fen) { + const fenParts = fen.split(" "); + return Object.assign( + ChessRules.ParseFen(fen), + { reserve: fenParts[4] } + ); + } + + static GenRandInitFen() { + // ChessRules.PIECES order is P, R, N, B, Q, K: + return "8/8/8/8/8/8/8/8 w 0 - 822211822211"; + } + + getFen() { + return super.getFen() + " " + this.getReserveFen(); + } + + getFenForRepeat() { + return super.getFenForRepeat() + "_" + this.getReserveFen(); + } + + getReserveFen() { + let counts = new Array(12); + for (let i = 0; i < V.PIECES.length; i++) { + counts[i] = this.reserve["w"][V.PIECES[i]]; + counts[6 + i] = this.reserve["b"][V.PIECES[i]]; + } + return counts.join(""); + } + + setOtherVariables(fen) { + super.setOtherVariables(fen); + const fenParsed = V.ParseFen(fen); + // Also init reserves (used by the interface to show landable pieces) + this.reserve = { + w: { + [V.PAWN]: parseInt(fenParsed.reserve[0]), + [V.ROOK]: parseInt(fenParsed.reserve[1]), + [V.KNIGHT]: parseInt(fenParsed.reserve[2]), + [V.BISHOP]: parseInt(fenParsed.reserve[3]), + [V.QUEEN]: parseInt(fenParsed.reserve[4]), + [V.KING]: parseInt(fenParsed.reserve[5]) + }, + b: { + [V.PAWN]: parseInt(fenParsed.reserve[6]), + [V.ROOK]: parseInt(fenParsed.reserve[7]), + [V.KNIGHT]: parseInt(fenParsed.reserve[8]), + [V.BISHOP]: parseInt(fenParsed.reserve[9]), + [V.QUEEN]: parseInt(fenParsed.reserve[10]), + [V.KING]: parseInt(fenParsed.reserve[11]) + } + }; + } + + getColor(i, j) { + if (i >= V.size.x) return i == V.size.x ? "w" : "b"; + return this.board[i][j].charAt(0); + } + + getPiece(i, j) { + if (i >= V.size.x) return V.RESERVE_PIECES[j]; + return this.board[i][j].charAt(1); + } + + // Used by the interface: + getReservePpath(index, color) { + return color + V.RESERVE_PIECES[index]; + } + + // Ordering on reserve pieces (matching V.PIECES order) + static get RESERVE_PIECES() { + return [V.PAWN, V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN, V.KING]; + } + + getReserveMoves([x, y]) { + const color = this.turn; + const oppCol = V.GetOppCol(color); + const p = V.RESERVE_PIECES[y]; + if (this.reserve[color][p] == 0) return []; + let moves = []; + let boundary = + p == V.PAWN + // Pawns can land only on 4 first ranks: + ? (color == 'w' ? [4, 8] : [0, 4]) + : [0, 8]; + for (let i = boundary[0]; i < boundary[1]; i++) { + for (let j = 0; j < 8; j++) { + if (this.board[i][j] == V.EMPTY) { + let mv = new Move({ + appear: [ + new PiPo({ + x: i, + y: j, + c: color, + p: p + }) + ], + vanish: [], + start: { x: x, y: y }, //a bit artificial... + end: { x: i, y: j } + }); + this.play(mv); + // Landing with check is forbidden: + if (!this.underCheck(oppCol)) moves.push(mv); + this.undo(mv); + } + } + } + return moves; + } + + getPotentialMovesFrom([x, y]) { + let moves = + x >= 8 + ? this.getReserveMoves([x, y]) + : super.getPotentialMovesFrom([x, y]); + // Forbid captures if king not landed yet: + if (x < 8 && moves.length > 0 && this.kingPos[moves[0].appear[0].c][0] < 0) + moves = moves.filter(m => m.vanish.length == 1); + return moves; + } + + getAllValidMoves() { + let moves = super.getAllValidMoves(); + const color = this.turn; + for (let i = 0; i < V.RESERVE_PIECES.length; i++) + moves = moves.concat( + this.getReserveMoves([V.size.x + (color == "w" ? 0 : 1), i]) + ); + return this.filterValid(moves); + } + + isAttacked(sq, color) { + // While the king hasn't landed, nothing is attacked: + if (this.kingPos[color][0] < 0) return false; + return super.isAttacked(sq, color); + } + + atLeastOneMove() { + if (!super.atLeastOneMove()) { + // Search one reserve move + for (let i = 0; i < V.RESERVE_PIECES.length; i++) { + let moves = this.filterValid( + this.getReserveMoves([V.size.x + (this.turn == "w" ? 0 : 1), i]) + ); + if (moves.length > 0) return true; + } + return false; + } + return true; + } + + prePlay(move) { + super.prePlay(move); + if (move.vanish.length == 0) this.reserve[this.turn][move.appear[0].p]--; + } + + postUndo(move) { + if (move.vanish.length == 0) this.reserve[this.turn][move.appear[0].p]++; + // (Potentially) Reset king position + if (move.appear[0].p == V.KING) { + const c = move.appear[0].c; + if (move.vanish.length == 0) + // Landing king + this.kingPos[c] = [-1, -1]; + else + // King movement + this.kingPos[c] = [move.start.x, move.start.y]; + } + } + + static get SEARCH_DEPTH() { + return 1; + } + + evalPosition() { + let evaluation = super.evalPosition(); + // Add reserves: + for (let i = 0; i < V.RESERVE_PIECES.length; i++) { + const p = V.RESERVE_PIECES[i]; + evaluation += this.reserve["w"][p] * V.VALUES[p]; + evaluation -= this.reserve["b"][p] * V.VALUES[p]; + } + return evaluation; + } + + getNotation(move) { + if (move.vanish.length > 0) return super.getNotation(move); + // Parachutage: + const piece = + move.appear[0].p != V.PAWN ? move.appear[0].p.toUpperCase() : ""; + return piece + "@" + V.CoordsToSquare(move.end); + } +}; diff --git a/client/src/variants/Recycle.js b/client/src/variants/Recycle.js index 8b070fac..adaceb04 100644 --- a/client/src/variants/Recycle.js +++ b/client/src/variants/Recycle.js @@ -27,13 +27,6 @@ export class RecycleRules extends ChessRules { ); } - getEpSquare(moveOrSquare) { - if (typeof moveOrSquare !== "object" || moveOrSquare.vanish.length > 0) - return super.getEpSquare(moveOrSquare); - // Landing move: no en-passant - return undefined; - } - static GenRandInitFen(randomness) { return ChessRules.GenRandInitFen(randomness) + " 0000000000"; } diff --git a/client/src/variants/Rifle.js b/client/src/variants/Rifle.js index 98661ae7..94a6d058 100644 --- a/client/src/variants/Rifle.js +++ b/client/src/variants/Rifle.js @@ -1,13 +1,6 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; export class RifleRules extends ChessRules { - getEpSquare(moveOrSquare) { - if (typeof moveOrSquare !== "object" || moveOrSquare.appear.length > 0) - return super.getEpSquare(moveOrSquare); - // Capturing move: no en-passant - return undefined; - } - getBasicMove([sx, sy], [ex, ey], tr) { let mv = new Move({ appear: [], diff --git a/client/src/variants/Royalrace.js b/client/src/variants/Royalrace.js index 39ee02aa..21a62c89 100644 --- a/client/src/variants/Royalrace.js +++ b/client/src/variants/Royalrace.js @@ -7,10 +7,6 @@ export class RoyalraceRules extends ChessRules { return false; } - static get HasCastle() { - return false; - } - static get HasEnpassant() { return false; } diff --git a/client/src/variants/Schess.js b/client/src/variants/Schess.js index ba5730e6..07556a50 100644 --- a/client/src/variants/Schess.js +++ b/client/src/variants/Schess.js @@ -50,7 +50,7 @@ export class SchessRules extends ChessRules { setFlags(fenflags) { super.setFlags(fenflags); //castleFlags this.pieceFlags = { - w: [...Array(8)], //pawns can move 2 squares? + w: [...Array(8)], //pieces can generate Hawk or Elephant? b: [...Array(8)] }; const flags = fenflags.substr(4); //skip first 4 letters, for castle @@ -293,7 +293,7 @@ export class SchessRules extends ChessRules { } this.updateCastleFlags(move, piece); - const oppCol = V.GetOppCol(color); + const oppCol = this.turn; const firstRank = (color == 'w' ? 7 : 0); const oppFirstRank = 7 - firstRank; // Does this move turn off a piece init square flag? diff --git a/client/src/variants/Shatranj.js b/client/src/variants/Shatranj.js index 00b14564..105331f9 100644 --- a/client/src/variants/Shatranj.js +++ b/client/src/variants/Shatranj.js @@ -5,10 +5,6 @@ export class ShatranjRules extends ChessRules { return false; } - static get HasCastle() { - return false; - } - static get HasEnpassant() { return false; } diff --git a/client/src/variants/Suicide.js b/client/src/variants/Suicide.js index c81d3551..2693f84f 100644 --- a/client/src/variants/Suicide.js +++ b/client/src/variants/Suicide.js @@ -7,10 +7,6 @@ export class SuicideRules extends ChessRules { return false; } - static get HasCastle() { - return false; - } - static get PawnSpecs() { return Object.assign( {}, diff --git a/client/src/variants/Upsidedown.js b/client/src/variants/Upsidedown.js index 20768240..abffd8f9 100644 --- a/client/src/variants/Upsidedown.js +++ b/client/src/variants/Upsidedown.js @@ -7,10 +7,6 @@ export class UpsidedownRules extends ChessRules { return false; } - static get HasCastle() { - return false; - } - static get HasEnpassant() { return false; } @@ -73,8 +69,9 @@ export class UpsidedownRules extends ChessRules { pieces["w"].join("").toUpperCase() + "/PPPPPPPP/8/8/8/8/pppppppp/" + pieces["b"].join("") + + // No castle, no en-passant: " w 0" - ); //no castle, no en-passant + ); } static get SEARCH_DEPTH() { diff --git a/client/src/views/Game.vue b/client/src/views/Game.vue index 31718554..e5b45ecd 100644 --- a/client/src/views/Game.vue +++ b/client/src/views/Game.vue @@ -474,10 +474,12 @@ export default { // TODO: shuffling and random filtering on server, // if the room is really crowded. Object.keys(data.sockIds).forEach(sid => { - // TODO: test sid != user.sid was already done on server if (sid != this.st.user.sid) { - this.people[sid] = { tmpIds: data.sockIds[sid] }; this.send("askidentity", { target: sid }); + this.people[sid] = { tmpIds: data.sockIds[sid] }; + } else { + // Complete my tmpIds: + Object.assign(this.people[sid].tmpIds, data.sockIds[sid]); } }); break; diff --git a/client/src/views/Hall.vue b/client/src/views/Hall.vue index 132c31ba..5c5dc33e 100644 --- a/client/src/views/Hall.vue +++ b/client/src/views/Hall.vue @@ -588,7 +588,6 @@ export default { // TODO: shuffling and random filtering on server, // if the room is really crowded. Object.keys(data.sockIds).forEach(sid => { - // TODO: test sid != user.sid was already done on server if (sid != this.st.user.sid) { // Pick a target tmpId (+page) at random: const pt = Object.values(data.sockIds[sid]); diff --git a/server/db/populate.sql b/server/db/populate.sql index 28d186cb..18957773 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -16,6 +16,7 @@ insert or ignore into Variants (name,description) values ('Cannibal', 'Capture powers'), ('Capture', 'Mandatory captures'), ('Checkered', 'Shared pieces'), + ('Checkless', 'No-check mode'), ('Chess960', 'Standard rules'), ('Circular', 'Run forward'), ('Coregal', 'Two royal pieces'), @@ -36,6 +37,7 @@ insert or ignore into Variants (name,description) values ('Losers', 'Get strong at self-mate'), ('Magnetic', 'Laws of attraction'), ('Marseille', 'Move twice'), + ('Parachute', 'Landing on the board'), ('Perfect', 'Powerful pieces'), ('Racingkings', 'Kings cross the 8x8 board'), ('Rifle', 'Shoot pieces'), diff --git a/server/models/User.js b/server/models/User.js index e3e5408c..ab826d91 100644 --- a/server/models/User.js +++ b/server/models/User.js @@ -149,6 +149,7 @@ const UserModel = { // Remove users unlogged for > 24h if (!u.sessionToken && tsNow - u.created > day) { + toRemove.push(u.id); UserModel.notify( u, "Your account has been deleted because " + diff --git a/server/sockets.js b/server/sockets.js index 65e41635..25abadb7 100644 --- a/server/sockets.js +++ b/server/sockets.js @@ -124,13 +124,12 @@ module.exports = function(wss) { // From Game let sockIds = {}; Object.keys(clients[page]).forEach(k => { - // Avoid polling myself: no new information to get - if (k != sid) { - sockIds[k] = {}; - Object.keys(clients[page][k]).forEach(x => { + sockIds[k] = {}; + Object.keys(clients[page][k]).forEach(x => { + // Avoid polling my tmpId: no information to get + if (k != sid || x != tmpId) sockIds[k][x] = { focus: clients[page][k][x].focus }; - }); - } + }); }); send(socket, { code: "pollclients", sockIds: sockIds }); break; @@ -139,30 +138,30 @@ module.exports = function(wss) { // From Hall let sockIds = {}; Object.keys(clients["/"]).forEach(k => { - // Avoid polling myself: no new information to get - if (k != sid) { - sockIds[k] = {}; - Object.keys(clients[page][k]).forEach(x => { + sockIds[k] = {}; + Object.keys(clients[page][k]).forEach(x => { + // Avoid polling my tmpId: no information to get + if (k != sid || x != tmpId) { sockIds[k][x] = { page: "/", focus: clients[page][k][x].focus }; - }); - } + } + }); }); // NOTE: a "gamer" could also just be an observer Object.keys(clients).forEach(p => { if (p.indexOf("/game/") >= 0) { Object.keys(clients[p]).forEach(k => { - if (k != sid) { - if (!sockIds[k]) sockIds[k] = {}; - Object.keys(clients[p][k]).forEach(x => { + if (!sockIds[k]) sockIds[k] = {}; + Object.keys(clients[p][k]).forEach(x => { + if (k != sid || x != tmpId) { sockIds[k][x] = { page: p, focus: clients[p][k][x].focus }; - }); - } + } + }); }); } });