From: Benjamin Auder Date: Fri, 11 Dec 2020 00:04:40 +0000 (+0100) Subject: Add Xiangqi X-Git-Url: https://git.auder.net/%7B%7B%20asset%28%27mixstore/images/scripts/doc/current/git-logo.png?a=commitdiff_plain;h=7e107b8f9dc9a4e7d9676fd2c48ba19f6cfc1661;p=vchess.git Add Xiangqi --- diff --git a/client/package-lock.json b/client/package-lock.json index 22ed9bc4..355a62e0 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -897,9 +897,9 @@ } }, "vue-loader-v16": { - "version": "npm:vue-loader@16.0.0-rc.1", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.0.0-rc.1.tgz", - "integrity": "sha512-yR+BS90EOXTNieasf8ce9J3TFCpm2DGqoqdbtiwQ33hon3FyIznLX7sKavAq1VmfBnOeV6It0Htg4aniv8ph1g==", + "version": "npm:vue-loader@16.1.1", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.1.1.tgz", + "integrity": "sha512-wz/+HFg/3SBayHWAlZXARcnDTl3VOChrfW9YnxvAweiuyKX/7IGx1ad/4yJHmwhgWlOVYMAbTiI7GV8G33PfGQ==", "dev": true, "optional": true, "requires": { diff --git a/client/public/images/pieces/Shogi/LICENSE b/client/public/images/pieces/Shogi/LICENSE index 997500b0..3bdba9d5 100644 --- a/client/public/images/pieces/Shogi/LICENSE +++ b/client/public/images/pieces/Shogi/LICENSE @@ -1,2 +1,2 @@ -Permission of author (Daniel Lee) --> TODO: ask him. +Permission of author (Daniel Lee). Pieces also in use on pychess-variants. diff --git a/client/public/images/pieces/Xiangqi/SOURCE b/client/public/images/pieces/Xiangqi/SOURCE new file mode 100644 index 00000000..856214a3 --- /dev/null +++ b/client/public/images/pieces/Xiangqi/SOURCE @@ -0,0 +1,2 @@ +https://github.com/gbtami/pychess-variants/tree/master/static/images/pieces/xiangqi +(With author's permission). diff --git a/client/public/images/pieces/Xiangqi/ba.svg b/client/public/images/pieces/Xiangqi/ba.svg new file mode 100644 index 00000000..55f99256 --- /dev/null +++ b/client/public/images/pieces/Xiangqi/ba.svg @@ -0,0 +1,253 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/bc.svg b/client/public/images/pieces/Xiangqi/bc.svg new file mode 100644 index 00000000..a5e02fac --- /dev/null +++ b/client/public/images/pieces/Xiangqi/bc.svg @@ -0,0 +1,266 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/be.svg b/client/public/images/pieces/Xiangqi/be.svg new file mode 100644 index 00000000..979069fd --- /dev/null +++ b/client/public/images/pieces/Xiangqi/be.svg @@ -0,0 +1,210 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/bk.svg b/client/public/images/pieces/Xiangqi/bk.svg new file mode 100644 index 00000000..2fe58f9c --- /dev/null +++ b/client/public/images/pieces/Xiangqi/bk.svg @@ -0,0 +1,283 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/bn.svg b/client/public/images/pieces/Xiangqi/bn.svg new file mode 100644 index 00000000..c67d236c --- /dev/null +++ b/client/public/images/pieces/Xiangqi/bn.svg @@ -0,0 +1,229 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/bp.svg b/client/public/images/pieces/Xiangqi/bp.svg new file mode 100644 index 00000000..6855745a --- /dev/null +++ b/client/public/images/pieces/Xiangqi/bp.svg @@ -0,0 +1,215 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/br.svg b/client/public/images/pieces/Xiangqi/br.svg new file mode 100644 index 00000000..d78fe56e --- /dev/null +++ b/client/public/images/pieces/Xiangqi/br.svg @@ -0,0 +1,293 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/wa.svg b/client/public/images/pieces/Xiangqi/wa.svg new file mode 100644 index 00000000..2fcb3d9a --- /dev/null +++ b/client/public/images/pieces/Xiangqi/wa.svg @@ -0,0 +1,257 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/wc.svg b/client/public/images/pieces/Xiangqi/wc.svg new file mode 100644 index 00000000..a5c1e58a --- /dev/null +++ b/client/public/images/pieces/Xiangqi/wc.svg @@ -0,0 +1,322 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/we.svg b/client/public/images/pieces/Xiangqi/we.svg new file mode 100644 index 00000000..18394c03 --- /dev/null +++ b/client/public/images/pieces/Xiangqi/we.svg @@ -0,0 +1,244 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/wk.svg b/client/public/images/pieces/Xiangqi/wk.svg new file mode 100644 index 00000000..c898b301 --- /dev/null +++ b/client/public/images/pieces/Xiangqi/wk.svg @@ -0,0 +1,291 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/wn.svg b/client/public/images/pieces/Xiangqi/wn.svg new file mode 100644 index 00000000..dd2de955 --- /dev/null +++ b/client/public/images/pieces/Xiangqi/wn.svg @@ -0,0 +1,285 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/wp.svg b/client/public/images/pieces/Xiangqi/wp.svg new file mode 100644 index 00000000..4abd3a56 --- /dev/null +++ b/client/public/images/pieces/Xiangqi/wp.svg @@ -0,0 +1,271 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Xiangqi/wr.svg b/client/public/images/pieces/Xiangqi/wr.svg new file mode 100644 index 00000000..974a5c24 --- /dev/null +++ b/client/public/images/pieces/Xiangqi/wr.svg @@ -0,0 +1,353 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/variants/Xiangqi/Boards.png b/client/public/variants/Xiangqi/Boards.png new file mode 100644 index 00000000..6d2fa666 --- /dev/null +++ b/client/public/variants/Xiangqi/Boards.png @@ -0,0 +1 @@ +#$# git-fat 3028274bc0082cf1a376c0dee26caed0b5d64feb 763485 diff --git a/client/public/variants/Xiangqi/ElephantDiagram.png b/client/public/variants/Xiangqi/ElephantDiagram.png new file mode 100644 index 00000000..450c69ab --- /dev/null +++ b/client/public/variants/Xiangqi/ElephantDiagram.png @@ -0,0 +1 @@ +#$# git-fat 4ca235d26baaba6c2d38443262e9e7b26dd99c41 59999 diff --git a/client/public/variants/Xiangqi/HorseDiagram.png b/client/public/variants/Xiangqi/HorseDiagram.png new file mode 100644 index 00000000..fc2c9418 --- /dev/null +++ b/client/public/variants/Xiangqi/HorseDiagram.png @@ -0,0 +1 @@ +#$# git-fat c81242020ecffe542e81e359626dc922f88ef5d5 77328 diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 3cde6ed6..57dcd898 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -176,7 +176,7 @@ export const translations = { "Bishop versus pawns": "Bishop versus pawns", "Board upside down": "Board upside down", "Both sides of the mirror": "Both sides of the mirror", - "Burmese chess": "Burmese chess", + "Burmese Chess": "Burmese Chess", "Capture all of a kind": "Capture all of a kind", "Capture both colors": "Capture both colors", "Capture en passant": "Capture en passant", @@ -185,6 +185,7 @@ export const translations = { "Capture the princess": "Capture the princess", "Captures reborn": "Captures reborn", "Change colors": "Change colors", + "Chinese Chess": "Chinese Chess", "Convert & support (v1)": "Convert & support (v1)", "Convert & support (v2)": "Convert & support (v2)", "Dangerous captures": "Dangerous captures", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index 612b0e35..a7e3c9d4 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -176,7 +176,7 @@ export const translations = { "Bishop versus pawns": "Alfil contra peones", "Board upside down": "Tablero al revés", "Both sides of the mirror": "Ambos lados del espejo", - "Burmese chess": "Ajedrez birmano", + "Burmese Chess": "Ajedrez birmano", "Capture all of a kind": "Capturar todo del mismo tipo", "Capture both colors": "Captura ambos colores", "Capture en passant": "Capturar en passant", @@ -185,6 +185,7 @@ export const translations = { "Capture the princess": "Capturar a la princesa", "Captures reborn": "Las capturas renacen", "Change colors": "Cambiar colores", + "Chinese Chess": "Ajedrez chino", "Convert & support (v1)": "Convertir & apoyar (v1)", "Convert & support (v2)": "Convertir & apoyar (v2)", "Dangerous captures": "Capturas peligrosas", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 6163dc45..d971d6ba 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -176,7 +176,7 @@ export const translations = { "Bishop versus pawns": "Fou contre pions", "Board upside down": "Échiquier à l'envers", "Both sides of the mirror": "Les deux côté du miroir", - "Burmese chess": "Échecs birmans", + "Burmese Chess": "Échecs birmans", "Capture all of a kind": "Capturez tout d'un même type", "Capture both colors": "Capturer les deux couleurs", "Capture en passant": "Capturer en passant", @@ -185,6 +185,7 @@ export const translations = { "Capture the princess": "Capturer la princesse", "Captures reborn": "Les captures renaissent", "Change colors": "Changer les couleurs", + "Chinese Chess": "Échecs chinois", "Convert & support (v1)": "Convertir & soutenir (v1)", "Convert & support (v2)": "Convertir & soutenir (v2)", "Dangerous captures": "Captures dangeureuses", diff --git a/client/src/translations/rules/Xiangqi/en.pug b/client/src/translations/rules/Xiangqi/en.pug new file mode 100644 index 00000000..06e01086 --- /dev/null +++ b/client/src/translations/rules/Xiangqi/en.pug @@ -0,0 +1,66 @@ +p.boxed. + Chinese chess, with different pieces behaving + differently than orthodox chess. + +p. + Pieces stand on intersections rather than squares: this is purely aesthetic, + and doesn't affect movements' descriptions. + +figure + img.img-center(src="/variants/Xiangqi/Boards.png") + figcaption + | Xiangqi boards borrowed from + a(href="https://www.pychess.org/variant/xiangqi") pychess-variants + | . Chinese pieces on the right, international ones on the left. + +p. + The central area between rows 5 and 6 is called the "river". + It affects some pieces behavior. + +p. + Only the rooks behave exactly as in standard chess. + All other pieces are quite different: +ul + li. + The King moves orthogonaly by one square, and cannot leave the "palace" + consisting of the nine intersections of the grey rectangles + on the diagram above. + li. + The pieces surrounding the king are Advisors: they move by one square + at a time, diagonally only. They are also restricted to the palace. + li. + The pawns ("Soldiers") move - and capture - by going up one intersection. + After they reach the second half of the board, they can then also move + (and capture) by one square lateraly. + li. + The Elephant moves by two intersections diagonally. It cannot jump over + obstacles: the midpoint intersection should be vacant. + The elephants cannot cross the river. + li. + The knights ("Horses") also cannot jump over obstacles, although they + appear to move as in orthodox chess. They make an orthogonal step first, + and then a diagonal step so that they are at a regular "knight distance" + from the initial intersection. + However, the square after first step must be empty. + li. + The Cannons move like a rook but capture by jumping first over an + obstacle (friendly or enemy). After such a jump the cannon can only + capture (no normal moves). + +figure.diagram-container + .diagram.diag12 + img(src="/variants/Xiangqi/ElephantDiagram.png") + .diagram.diag22 + img(src="/variants/Xiangqi/HorseDiagram.png") + figcaption + | Elephant and Horse moves illustrated, from + a(href="https://www.pychess.org/variant/xiangqi") pychess-variants + | . + +h3 More information + +p + | Many resources are available online: just search with keywords Xiangqi + | or Chinese chess. You can play it on + a(href="https://www.pychess.org") pychess-variants + | , against humans or a (basic) bot. diff --git a/client/src/translations/rules/Xiangqi/es.pug b/client/src/translations/rules/Xiangqi/es.pug new file mode 100644 index 00000000..5e62a07c --- /dev/null +++ b/client/src/translations/rules/Xiangqi/es.pug @@ -0,0 +1,68 @@ +p.boxed. + Ajedrez chino, con diferentes piezas comportándose + diferente al ajedrez ortodoxo. + +p. + Las piezas se colocan en las intersecciones en lugar de en el centro de + las casillas. El efecto es solo estético, no hay impacto en la descripción + de los desplazamientos. + +figure + img.img-center(src="/variants/Xiangqi/Boards.png") + figcaption + | Meseta Xiangqi tomado de + a(href="https://www.pychess.org/variant/xiangqi") pychess-variants + | . Piezas chinas a la derecha, internacionales a la izquierda. + +p. + La parte central entre las filas 5 y 6 se llama "río". + Afecta el comportamiento de ciertas piezas. + +p. + Solo las torres se comportan como en el ajedrez estándar. + Todas las demás piezas son bastante diferentes: +ul + li. + El Rey se mueve ortogonalmente una casilla y no puede salir + el "palacio" que consta de las nueve intersecciones de rectángulos grises + en el diagrama de arriba. + li. + Las piezas que rodean al rey son los Consejeros: mueven una + casilla a la vez, solo en diagonal. + Ellos también están restringidos al palacio. + li. + Los peones ("Soldados") se mueven y capturan subiendo uno + intersección. Una vez que cruzan el río, también pueden + mover (y capturar) un cuadrado de lado. + li. + El Elefante se mueve desde dos intersecciones diagonales. No puede + saltar obstáculos: la intersección en el punto central debe + ser libre. Los elefantes no pueden cruzar el río. +li. + Los Caballos tampoco pueden saltar obstáculos, + aunque parecen moverse como los caballos ortodoxos. + Ellos primero dar un paso ortogonal, luego un paso diagonal de + para estar a una distancia de caballo "estándar" de la intersección + inicial. Sin embargo, la casilla después del primer paso debe estar vacía. + li. + Los Cañones se mueven como una torre pero capturan saltando primero + sobre un obstáculo (amigo o enemigo). Después de tal salto el cañón + solo puede capturar (sin movimientos normales). + +figure.diagram-container + .diagram.diag12 + img(src="/variants/Xiangqi/ElephantDiagram.png") + .diagram.diag22 + img(src="/variants/Xiangqi/HorseDiagram.png") + figcaption + | Jugadas del Elefante y del Caballo illustradas, de + a(href="https://www.pychess.org/variant/xiangqi") pychess-variants + | . + +h3 Más información + +p + | Hay muchos recursos disponibles en línea, solo busque + | con las palabras clave Xiangqi o Ajedrez chino. Puedes jugarlo en + a(href="https://www.pychess.org") pychess-variants + | , contra humanos o un programa (básico). diff --git a/client/src/translations/rules/Xiangqi/fr.pug b/client/src/translations/rules/Xiangqi/fr.pug new file mode 100644 index 00000000..05f8a402 --- /dev/null +++ b/client/src/translations/rules/Xiangqi/fr.pug @@ -0,0 +1,68 @@ +p.boxed. + Échecs chinois, avec différentes pièces se comportant + différemment des échecs orthodoxes. + +p. + Les pièces se positionnent sur les intersections plutôt qu'au centre des + cases. L'effet n'est qu'esthétique, il n'y a pas d'impact sur la description + des déplacements. + +figure + img.img-center(src="/variants/Xiangqi/Boards.png") + figcaption + | Plateau de Xiangqi empruntés à + a(href="https://www.pychess.org/variant/xiangqi") pychess-variants + | . Pièces chinoises à droite, internationales à gauche. + +p. + La partie centrale entre les rangées 5 et 6 est appelée la "rivière". + Elle affecte le comportement de certaines pièces. + +p. + Seules les tours se comportent comme aux échecs standards. + Toutes les autres pièces sont plutôt différentes : +ul + li. + Le Roi se déplace orthogonalement d'une case, et ne peut pas quitter + le "palais" consistant en les neuf intersections des rectangles gris + sur le diagramme ci-dessus. + li. + Les pièces entourant le roi sont les Conseillers : ils se déplacent d'une + case à la fois, en diagonale seulement. + Ils sont eux-aussi restreints au palais. + li. + Les pions ("Soldats") se déplacent - et capturent - en montant d'une + intersection. Une fois qu'ils on franchi la rivière, ils peuvent aussi + se déplacer (et capturer) d'une case latéralement. + li. + L'Éléphant se déplacent de deux intersections en diagonale. Il ne peut pas + sauter par dessus les obstacles : l'intersection au point central doit + être libre. Les éléphants ne peuvent traverser la rivière. + li. + Les cavaliers ("Chevaux") ne peuvent pas non plus sauter les obstacles, + bien qu'ils paraissent se déplacer comme les cavaliers orthodoxes. Ils + effecuent d'abord un pas orthogonal, puis ensuite un en diagonale de + manière à se retrouver à distance de cavalier "standard" de l'intersection + initiale. Cependant, la case après le premier pas doit être vide. + li. + Les Canons se déplacent comme une tour mais capturent en sautant d'abord + par dessus un obstacle (ami ou ennemi). Après un tel saut le canon + ne peut que capturer (pas de coups normaux). + +figure.diagram-container + .diagram.diag12 + img(src="/variants/Xiangqi/ElephantDiagram.png") + .diagram.diag22 + img(src="/variants/Xiangqi/HorseDiagram.png") + figcaption + | Coups de l'Élephant et du Cheval illustrés, depuis + a(href="https://www.pychess.org/variant/xiangqi") pychess-variants + | . + +h3 Plus d'information + +p + | Beaucoup de ressources sont disponibles en ligne : cherchez simplement + avec les mots-clé Xiangqi ou Échecs chinois. Vous pouvez y jouer sur + a(href="https://www.pychess.org") pychess-variants + | , contre des humains ou un programme (basique). diff --git a/client/src/variants/Xiangqi.js b/client/src/variants/Xiangqi.js new file mode 100644 index 00000000..3889b20a --- /dev/null +++ b/client/src/variants/Xiangqi.js @@ -0,0 +1,301 @@ +import { ChessRules } from "@/base_rules"; + +export class XiangqiRules extends ChessRules { + + static get Monochrome() { + return true; + } + + static get Notoodark() { + return true; + } + + static get Lines() { + let lines = []; + // Draw all inter-squares lines, shifted: + for (let i = 0; i < V.size.x; i++) + lines.push([[i+0.5, 0.5], [i+0.5, V.size.y-0.5]]); + for (let j = 0; j < V.size.y; j++) + lines.push([[0.5, j+0.5], [V.size.x-0.5, j+0.5]]); + // Add palaces: + lines.push([[0.5, 3.5], [2.5, 5.5]]); + lines.push([[0.5, 5.5], [2.5, 3.5]]); + lines.push([[9.5, 3.5], [7.5, 5.5]]); + lines.push([[9.5, 5.5], [7.5, 3.5]]); + // Show river: + lines.push([[4.5, 0.5], [5.5, 8.5]]); + lines.push([[5.5, 0.5], [4.5, 8.5]]); + return lines; + } + + static get HasFlags() { + return false; + } + + static get HasEnpassant() { + return false; + } + + static get ELEPHANT() { + return "e"; + } + + static get CANNON() { + return "c"; + } + + static get ADVISOR() { + return "a"; + } + + static get PIECES() { + return [V.PAWN, V.ROOK, V.KNIGHT, V.ELEPHANT, V.ADVISOR, V.KING, V.CANNON]; + } + + getPpath(b) { + return "Xiangqi/" + b; + } + + static get size() { + return { x: 10, y: 9}; + } + + getPotentialMovesFrom(sq) { + switch (this.getPiece(sq[0], sq[1])) { + case V.PAWN: return this.getPotentialPawnMoves(sq); + case V.ROOK: return super.getPotentialRookMoves(sq); + case V.KNIGHT: return this.getPotentialKnightMoves(sq); + case V.ELEPHANT: return this.getPotentialElephantMoves(sq); + case V.ADVISOR: return this.getPotentialAdvisorMoves(sq); + case V.KING: return this.getPotentialKingMoves(sq); + case V.CANNON: return this.getPotentialCannonMoves(sq); + } + return []; //never reached + } + + getPotentialPawnMoves([x, y]) { + const c = this.getColor(x, y); + const shiftX = (c == 'w' ? -1 : 1); + const crossedRiver = (c == 'w' && x <= 4 || c == 'b' && x >= 5); + const lastRank = (c == 'w' && x == 0 || c == 'b' && x == 9); + let steps = []; + if (!lastRank) steps.push([shiftX, 0]); + if (crossedRiver) { + if (y > 0) steps.push([0, -1]); + if (y < 9) steps.push([0, 1]); + } + return super.getSlideNJumpMoves([x, y], steps, "oneStep"); + } + + knightStepsFromRookStep(step) { + if (step[0] == 0) return [ [1, 2*step[1]], [-1, 2*step[1]] ]; + return [ [2*step[0], 1], [2*step[0], -1] ]; + } + + getPotentialKnightMoves([x, y]) { + let steps = []; + for (let rookStep of ChessRules.steps[V.ROOK]) { + const [i, j] = [x + rookStep[0], y + rookStep[1]]; + if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + Array.prototype.push.apply(steps, + // These moves might be impossible, but need to be checked: + this.knightStepsFromRookStep(rookStep)); + } + } + return super.getSlideNJumpMoves([x, y], steps, "oneStep"); + } + + getPotentialElephantMoves([x, y]) { + let steps = []; + const c = this.getColor(x, y); + for (let bishopStep of ChessRules.steps[V.BISHOP]) { + const [i, j] = [x + bishopStep[0], y + bishopStep[1]]; + if (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + const [newX, newY] = [x + 2*bishopStep[0], y + 2*bishopStep[1]]; + if ((c == 'w' && newX >= 5) || (c == 'b' && newX <= 4)) + // A priori valid (elephant don't cross the river) + steps.push(bishopStep.map(s => 2*s)); + // "out of board" checks delayed to next method + } + } + return super.getSlideNJumpMoves([x, y], steps, "oneStep"); + } + + insidePalace(x, y, c) { + return ( + (y >= 3 && y <= 5) && + ( + (c == 'w' && x >= 7) || + (c == 'b' && x <= 2) + ) + ); + } + + getPotentialAdvisorMoves([x, y]) { + // Diagonal steps inside palace + let steps = []; + const c = this.getColor(x, y); + for (let s of ChessRules.steps[V.BISHOP]) { + if (this.insidePalace(x + s[0], y + s[1], c)) steps.push(s); + } + return super.getSlideNJumpMoves([x, y], steps, "oneStep"); + } + + getPotentialKingMoves([x, y]) { + // Orthogonal steps inside palace + let steps = []; + const c = this.getColor(x, y); + for (let s of ChessRules.steps[V.ROOK]) { + if (this.insidePalace(x + s[0], y + s[1], c)) steps.push(s); + } + return super.getSlideNJumpMoves([x, y], steps, "oneStep"); + } + + // NOTE: duplicated from Shako (TODO?) + getPotentialCannonMoves([x, y]) { + const oppCol = V.GetOppCol(this.turn); + let moves = []; + // Look in every direction until an obstacle (to jump) is met + for (const step of V.steps[V.ROOK]) { + let i = x + step[0]; + let j = y + step[1]; + while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + moves.push(this.getBasicMove([x, y], [i, j])); + i += step[0]; + j += step[1]; + } + // Then, search for an enemy + i += step[0]; + j += step[1]; + while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + i += step[0]; + j += step[1]; + } + if (V.OnBoard(i, j) && this.getColor(i, j) == oppCol) + moves.push(this.getBasicMove([x, y], [i, j])); + } + return moves; + } + + // (King) Never attacked by advisor, since it stays in the palace + // Also, never attacked by elephants since they don't cross the river. + isAttacked(sq, color) { + return ( + this.isAttackedByPawn(sq, color) || + super.isAttackedByRook(sq, color) || + this.isAttackedByKnight(sq, color) || + this.isAttackedByCannon(sq, color) + ); + } + + isAttackedByPawn([x, y], color) { + // The pawn necessarily crossed the river (attack on king) + const shiftX = (color == 'w' ? 1 : -1); //shift from king + for (let s of [[shiftX, 0], [0, 1], [0, -1]]) { + const [i, j] = [x + s[0], y + s[1]]; + if ( + this.board[i][j] != V.EMPTY && + this.getColor(i, j) == color && + this.getPiece(i, j) == V.PAWN + ) { + return true; + } + } + return false; + } + + knightStepsFromBishopStep(step) { + return [ [2*step[0], step[1]], [step[0], 2*step[1]] ]; + } + + isAttackedByKnight([x, y], color) { + // Check bishop steps: if empty, look continuation knight step + let steps = []; + for (let s of ChessRules.steps[V.BISHOP]) { + const [i, j] = [x + s[0], y + s[1]]; + if ( + V.OnBoard(i, j) && + this.board[i][j] == V.EMPTY + ) { + Array.prototype.push.apply(steps, this.knightStepsFromBishopStep(s)); + } + } + return ( + super.isAttackedBySlideNJump([x, y], color, V.KNIGHT, steps, "oneStep") + ); + } + + // NOTE: duplicated from Shako (TODO?) + isAttackedByCannon([x, y], color) { + // Reversed process: is there an obstacle in line, + // and a cannon next in the same line? + for (const step of V.steps[V.ROOK]) { + let [i, j] = [x+step[0], y+step[1]]; + while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + i += step[0]; + j += step[1]; + } + if (V.OnBoard(i, j)) { + // Keep looking in this direction + i += step[0]; + j += step[1]; + while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + i += step[0]; + j += step[1]; + } + if ( + V.OnBoard(i, j) && + this.getPiece(i, j) == V.CANNON && + this.getColor(i, j) == color + ) { + return true; + } + } + } + return false; + } + + static get VALUES() { + return { + p: 1, + r: 9, + n: 4, + e: 2.5, + a: 2, + c: 4.5, + k: 1000 + }; + } + + evalPosition() { + let evaluation = 0; + for (let i = 0; i < V.size.x; i++) { + for (let j = 0; j < V.size.y; j++) { + if (this.board[i][j] != V.EMPTY) { + const c = this.getColor(i, j); + const sign = (c == 'w' ? 1 : -1); + const piece = this.getPiece(i, j); + let pieceEval = V.VALUES[this.getPiece(i, j)]; + if ( + piece == V.PAWN && + ( + (c == 'w' && i <= 4) || + (c == 'b' && i >= 5) + ) + ) { + // Pawn crossed the river: higher value + pieceEval++; + } + evaluation += sign * pieceEval; + } + } + } + return evaluation; + } + + static GenRandInitFen() { + // No randomization here (TODO?) + return "rneakaenr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNEAKAENR w 0"; + } + +}; diff --git a/server/db/populate.sql b/server/db/populate.sql index 7def7a62..b0596058 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -104,7 +104,7 @@ insert or ignore into Variants (name, description) values ('Shako', 'Non-conformism and utopia'), ('Shatranj', 'Ancient rules'), ('Shogi', 'Japanese Chess'), - ('Sittuyin', 'Burmese chess'), + ('Sittuyin', 'Burmese Chess'), ('Suicide', 'Lose all pieces'), ('Suction', 'Attract opposite king'), ('Swap', 'Dangerous captures'), @@ -119,4 +119,5 @@ insert or ignore into Variants (name, description) values ('Vchess', 'Pawns capture backward'), ('Wildebeest', 'Balanced sliders & leapers'), ('Wormhole', 'Squares disappear'), + ('Xiangqi', 'Chinese Chess'), ('Zen', 'Reverse captures');