From: Benjamin Auder Date: Fri, 17 Apr 2020 17:16:30 +0000 (+0200) Subject: Add Hamilton Chess X-Git-Url: https://git.auder.net/doc/current/%7B%7B%20asset%28%27mixstore/css/user/DESCRIPTION?a=commitdiff_plain;h=22053c2c72b452cba3768f7ec86acd015806c141;p=vchess.git Add Hamilton Chess --- diff --git a/TODO b/TODO index 293d78a2..cfc62fc9 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,7 @@ Chakart :) +https://www.chessvariants.com/d.betza/chessvar/trapdoor.html https://www.chessvariants.com/crossover.dir/koopachess.html ---> Can a stunned piece capture? Maybe not. -Donkey pose une banane soit en début de coup soit à la fin, en diagonale, et explosion orthogonale. -Wario pareil mais pose orthogonale et explosion diagonale. +--> Can a stunned piece capture? Maybe not. ...recover? After 5 moves? Never? Diamond Chess [Rynd] (J. A. Porterfield Rynd, 1886) --> Berolina2 ? @@ -28,15 +27,6 @@ the NOST combination games. (Nost-algia 150, also Nost-algia 112 ‘not seen’) --> pourquoi pas, mais faudra pouvoir tracer des lignes sur plateau (Ball, Koth, Sittuyin, celle-là, Rococo) -Contact (quoted by David Silverman, 1971). -White puts a knight on any square of an empty -chessboard. Black moves the knight and -places a marker on the square vacated. Play -alternates. The knight may only be moved to -vacant squares. The object is to make the last -move. The game can also be played with any -of the other pieces. (Your Move) - https://www.chessvariants.com/diffmove.dir/checkers.html --> move forward (Multhopp) in 1974 by Hans Multhopp https://www.chessvariants.com/diffmove.dir/checkers.html diff --git a/client/public/images/pieces/Hamilton/hole.svg b/client/public/images/pieces/Hamilton/hole.svg new file mode 100644 index 00000000..7c96145d --- /dev/null +++ b/client/public/images/pieces/Hamilton/hole.svg @@ -0,0 +1,110 @@ + + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/src/translations/en.js b/client/src/translations/en.js index e9bd52c1..520c4823 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -233,5 +233,6 @@ export const translations = { "Two kings": "Two kings", "Two royal pieces": "Two royal pieces", "Unidentified pieces": "Unidentified pieces", + "Walk on a graph": "Walk on a graph", "White move twice": "White move twice" }; diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 27ad31c8..f803df2c 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -233,5 +233,6 @@ export const translations = { "Two kings": "Dos reyes", "Two royal pieces": "Dos piezas reales", "Unidentified pieces": "Piezas no identificadas", + "Walk on a graph": "Camino en un gráfico", "White move twice": "Las blancas juegan dos veces" }; diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 5d2ef3be..e73e2e50 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -233,5 +233,6 @@ export const translations = { "Two kings": "Deux rois", "Two royal pieces": "Deux pièces royales", "Unidentified pieces": "Pièces non identifiées", + "Walk on a graph": "Marche sur un graphe", "White move twice": "Les blancs jouent deux fois" }; diff --git a/client/src/translations/rules/Hamilton/en.pug b/client/src/translations/rules/Hamilton/en.pug new file mode 100644 index 00000000..813c4205 --- /dev/null +++ b/client/src/translations/rules/Hamilton/en.pug @@ -0,0 +1,27 @@ +p.boxed + | Click on an empty square to start the game. + | The first player unable to move the knight loses. + +p. + White puts a knight on any square of an empty chessboard. + Black moves the knight, and its initial square vanish: + the knight will no longer be able to land on that square. + Play alternates, always with the vanishing initial square condition. + The first player unable to make a move loses. + +figure.diagram-container + .diagram + | fen:3xx2x/1x1xNxx1/2xxxxxx/xxxxxx2/2xxxxx1/x1xxxx2/1xxxxx2/1x2xx2 g8,c8: + figcaption Ng8 wins, but Nc8 loses because it allows Na7. + +p + | Note about the name: when the game ends, the knight has walked on an + a(href="https://en.wikipedia.org/wiki/Hamiltonian_path") Hamiltonian path + |  considering the subgraph formed by all the removed squares + | plus the final square. + +p + | Inventor: David Silverman (1971) according to + a(href="https://www.jsbeasley.co.uk/encyc.htm") + | The Classified Encyclopedia of Chess Variants + | . The game idea is probably quite older. diff --git a/client/src/translations/rules/Hamilton/es.pug b/client/src/translations/rules/Hamilton/es.pug new file mode 100644 index 00000000..8acf042c --- /dev/null +++ b/client/src/translations/rules/Hamilton/es.pug @@ -0,0 +1,29 @@ +p.boxed + | Haz clic en una casilla vacía para comenzar el juego. + | El primer jugador que no puede mover al caballo ha perdido. + +p. + White pone el caballo en cualquier casilla de un tablero vacío. + Las negras mueven a este caballo, y su casilla inicial desaparece: + el caballo ya no puede llegar a este espacio. + El juego continúa, siempre con la condición de desaparezca de la casilla + inicial. El primer jugador que no puede ejecutar un movimiento ha perdido. + +figure.diagram-container + .diagram + | fen:3xx2x/1x1xNxx1/2xxxxxx/xxxxxx2/2xxxxx1/x1xxxx2/1xxxxx2/1x2xx2 g8,c8: + figcaption Ng8 gana, pero Nc8 pierde porque permite Na7. + +p + | Nota sobre el nombre: cuando termina el juego, el caballo ha caminado + | sobre un + a(href="https://es.wikipedia.org/wiki/Camino_hamiltoniano") + | camino hamiltoniano + |  considerando el sub-gráfico que consiste en los cuadros eliminados y + | la casilla final. + +p + | Inventor: David Silverman (1971) después + a(href="https://www.jsbeasley.co.uk/encyc.htm") + | The Classified Encyclopedia of Chess Variants + | . La idea del juego es probablemente más antigua. diff --git a/client/src/translations/rules/Hamilton/fr.pug b/client/src/translations/rules/Hamilton/fr.pug new file mode 100644 index 00000000..a3696187 --- /dev/null +++ b/client/src/translations/rules/Hamilton/fr.pug @@ -0,0 +1,28 @@ +p.boxed + | Cliquez sur une case vide pour démarrer la partie. + | Le premier joueur incapable de déplacer le cavalier a perdu. + +p. + Les blancs posent le cavalier sur n'importe quelle case d'un échiquier vide. + Les noirs déplacent ce cavalier, et sa case de départ disparaît : + le cavalier ne pourra plus arriver sur cette case. + Le jeu continue, toujours avec la condition de disparition de la case + initiale. Le premier joueur ne pouvant effectuer un coup a perdu. + +figure.diagram-container + .diagram + | fen:3xx2x/1x1xNxx1/2xxxxxx/xxxxxx2/2xxxxx1/x1xxxx2/1xxxxx2/1x2xx2 g8,c8: + figcaption Ng8 gagne, mais Nc8 perd car cela permet Na7. + +p + | Note au sujet du nom : quand la partie s'achève, le cavalier s'est promené + | sur un + a(href="https://fr.wikipedia.org/wiki/Graphe_hamiltonien") chemin hamiltonien + |  considérant le sous-graphe constitué des cases supprimées et de la + | case finale. + +p + | Inventeur : David Silverman (1971) d'après + a(href="https://www.jsbeasley.co.uk/encyc.htm") + | The Classified Encyclopedia of Chess Variants + | . L'idée du jeu est probablement plus ancienne. diff --git a/client/src/variants/Hamilton.js b/client/src/variants/Hamilton.js new file mode 100644 index 00000000..8c3a1bd2 --- /dev/null +++ b/client/src/variants/Hamilton.js @@ -0,0 +1,153 @@ +import { ChessRules, Move, PiPo } from "@/base_rules"; +import { randInt } from "@/utils/alea"; + +export class HamiltonRules extends ChessRules { + static get HasFlags() { + return false; + } + + static get HasEnpassant() { + return false; + } + + static get HOLE() { + return "xx"; + } + + static board2fen(b) { + if (b[0] == 'x') return 'x'; + return ChessRules.board2fen(b); + } + + static fen2board(f) { + if (f == 'x') return V.HOLE; + return ChessRules.fen2board(f); + } + + getPpath(b) { + if (b[0] == 'x') return "Hamilton/hole"; + return b; + } + + static get PIECES() { + return [ChessRules.KNIGHT]; + } + + static IsGoodPosition(position) { + if (position.length == 0) return false; + const rows = position.split("/"); + if (rows.length != V.size.x) return false; + for (let row of rows) { + let sumElts = 0; + for (let i = 0; i < row.length; i++) { + if (['x'].concat(V.PIECES).includes(row[i].toLowerCase())) sumElts++; + else { + const num = parseInt(row[i]); + if (isNaN(num)) return false; + sumElts += num; + } + } + if (sumElts != V.size.y) return false; + } + return true; + } + + static GenRandInitFen() { + return "8/8/8/8/8/8/8/8 w 0"; + } + + canIplay(side, [x, y]) { + return side == this.turn; + } + + // Initiate the game by choosing a square for the knight: + doClick(square) { + if (this.movesCount > 0) return null; + return new Move({ + appear: [ + new PiPo({ x: square[0], y: square[1], c: 'w', p: V.KNIGHT }) + ], + vanish: [], + start: { x: -1, y: -1 } + }); + } + + getAllPotentialMoves() { + if (this.movesCount == 0) { + return [...Array(64).keys()].map(k => { + const i = k % 8; + const j = (k - i) / 8; + return new Move({ + appear: [ + new PiPo({ x: i, y: j, c: 'w', p: V.KNIGHT }) + ], + vanish: [], + start: { x: -1, y: -1 } + }); + }); + } + for (let i=0; i<8; i++) { + for (let j=0; j<8; j++) { + if (!([V.EMPTY, V.HOLE].includes(this.board[i][j]))) + return this.getPotentialKnightMoves([i, j]); + } + } + return []; + } + + getPotentialKnightMoves([x, y]) { + return ( + V.steps[V.KNIGHT].filter( + s => { + const [i, j] = [x + s[0], y + s[1]]; + return (V.OnBoard(i, j) && this.board[i][j] != V.HOLE); + } + ).map(s => { + return this.getBasicMove([x, y], [x + s[0], y + s[1]]); + }) + ); + } + + atLeastOneMove() { + if (this.movesCount == 0) return true; + for (let i=0; i<8; i++) { + for (let j=0; j<8; j++) { + if (!([V.EMPTY, V.HOLE].includes(this.board[i][j]))) + return this.getPotentialKnightMoves([i, j]).length > 0; + } + } + return false; + } + + filterValid(moves) { + return moves; + } + + static PlayOnBoard(board, move) { + if (move.vanish.length > 0) + board[move.vanish[0].x][move.vanish[0].y] = V.HOLE; + for (let psq of move.appear) board[psq.x][psq.y] = psq.c + psq.p; + } + + getCheckSquares() { + return []; + } + + getCurrentScore() { + if (this.atLeastOneMove()) return "*"; + // No valid move: I lose + return this.turn == "w" ? "0-1" : "1-0"; + } + + getComputerMove() { + const moves = this.getAllValidMoves(); + // Just a random mover for now... + return moves[randInt(moves.length)]; + } + + getNotation(move) { + if (move.vanish.length > 0) return super.getNotation(move); + // First game move: + return "N@" + V.CoordsToSquare(move.end); + } +}; diff --git a/server/db/populate.sql b/server/db/populate.sql index a3479206..48f97c03 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -41,6 +41,7 @@ insert or ignore into Variants (name, description) values ('Extinction', 'Capture all of a kind'), ('Grand', 'Big board'), ('Grasshopper', 'Long jumps over pieces'), + ('Hamilton', 'Walk on a graph'), ('Horde', 'A pawns cloud'), ('Interweave', 'Interweaved colorbound teams'), ('Knightmate', 'Mate the knight'),