From e3e2cc443054cfb273b28b3ba46f559117c5ceae Mon Sep 17 00:00:00 2001 From: Benjamin Auder Date: Mon, 24 Feb 2020 18:53:52 +0100 Subject: [PATCH] Draft Circular Chess --- client/src/base_rules.js | 3 +- client/src/translations/en.js | 1 + client/src/translations/es.js | 1 + client/src/translations/fr.js | 1 + client/src/translations/rules/Circular/en.pug | 26 +++ client/src/translations/rules/Circular/es.pug | 23 +++ client/src/translations/rules/Circular/fr.pug | 23 +++ client/src/variants/Circular.js | 195 ++++++++++++++++++ server/db/populate.sql | 1 + 9 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 client/src/translations/rules/Circular/en.pug create mode 100644 client/src/translations/rules/Circular/es.pug create mode 100644 client/src/translations/rules/Circular/fr.pug create mode 100644 client/src/variants/Circular.js diff --git a/client/src/base_rules.js b/client/src/base_rules.js index da9fab78..4b9a139f 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -274,12 +274,13 @@ export const ChessRules = class ChessRules { pieces[c][knight2Pos] = "n"; pieces[c][rook2Pos] = "r"; } + // Add turn + flags + enpassant return ( pieces["b"].join("") + "/pppppppp/8/8/8/8/PPPPPPPP/" + pieces["w"].join("").toUpperCase() + " w 0 1111 -" - ); //add turn + flags + enpassant + ); } // "Parse" FEN: just return untransformed string data diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 920e19cb..09fa92fe 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -147,6 +147,7 @@ export const translations = { "Pawns move diagonally": "Pawns move diagonally", "Reuse pieces": "Reuse pieces", "Reverse captures": "Reverse captures", + "Run forward": "Run forward", "Shared pieces": "Shared pieces", "Standard rules": "Standard rules", "Unidentified pieces": "Unidentified pieces" diff --git a/client/src/translations/es.js b/client/src/translations/es.js index d3ead1a0..286ae428 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -147,6 +147,7 @@ export const translations = { "Pawns move diagonally": "Peones se mueven en diagonal", "Reuse pieces": "Reutilizar piezas", "Reverse captures": "Capturas invertidas", + "Run forward": "Correr hacia adelante", "Shared pieces": "Piezas compartidas", "Standard rules": "Reglas estandar", "Unidentified pieces": "Piezas no identificadas" diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 5abf762c..6746e708 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -147,6 +147,7 @@ export const translations = { "Pawns move diagonally": "Les pions vont en diagonale", "Reuse pieces": "Réutiliser les pièces", "Reverse captures": "Captures inversées", + "Run forward": "Courir vers l'avant", "Shared pieces": "Pièces partagées", "Standard rules": "Règles usuelles", "Unidentified pieces": "Pièces non identifiées" diff --git a/client/src/translations/rules/Circular/en.pug b/client/src/translations/rules/Circular/en.pug new file mode 100644 index 00000000..7e78b2f8 --- /dev/null +++ b/client/src/translations/rules/Circular/en.pug @@ -0,0 +1,26 @@ +p https://www.chessvariants.com/d.betza/chessvar/race.html 8x8 race chess + +p See also: https://www.chessvariants.com/shape.dir/x_torus.html + +p.boxed + | If a piece captures one of the same kind, both disappear. + +p. + The defensive power of pawns is thus increased, because they don't fear + captures (by other pawns). + +p. + Endings are also affected quite a lot, and sometimes new threats occur: + on the diagram, 3.Bxg7 wins a pawn because 3...Bxg7 would make both + bishops disappear. + +figure.diagram-container + .diagram + | fen:r1bqkbnr/pp1ppppp/2n5/2p5/8/1P6/PBPPPPPP/RN1QKBNR: + figcaption After 1.b3 c5 2.Bb2 Nc6 + +h3 Source + +p + a(href="https://www.chessvariants.com/rules/antimatter-chess") Antimatter chess + |  on chessvariants.com. diff --git a/client/src/translations/rules/Circular/es.pug b/client/src/translations/rules/Circular/es.pug new file mode 100644 index 00000000..b6adc808 --- /dev/null +++ b/client/src/translations/rules/Circular/es.pug @@ -0,0 +1,23 @@ +p.boxed + | Si una pieza captura otra del mismo tipo, las dos desaparecen. + +p. + El poder defensivo de los peones aumenta así, ya que no temen + más capturas (por otros peones). + +p. + Las finales también se ven muy afectadas y, a veces, nuevas amenazas + ocurren: en el diagrama, 3.Bxg7 gana un peón porque 3...Bxg7 causaría + la desaparición de los dos alfiles. + +figure.diagram-container + .diagram + | fen:r1bqkbnr/pp1ppppp/2n5/2p5/8/1P6/PBPPPPPP/RN1QKBNR: + figcaption Después de 1.b3 c5 2.Bb2 Nc6 + +h3 Fuente + +p + | La + a(href="https://www.chessvariants.com/rules/antimatter-chess") variante Antimateria + |  en chessvariants.com. diff --git a/client/src/translations/rules/Circular/fr.pug b/client/src/translations/rules/Circular/fr.pug new file mode 100644 index 00000000..7b1cdfae --- /dev/null +++ b/client/src/translations/rules/Circular/fr.pug @@ -0,0 +1,23 @@ +p.boxed + | Si une pièce en capture une autre du même type, les deux disparaissent. + +p. + Le pouvoir défensif des pions est ainsi augmenté, puisqu'ils ne craignent + plus les captures (par d'autres pions). + +p. + Les finales sont aussi beaucoup affectées, et parfois de nouvelles menaces + surviennent : sur le diagramme, 3.Bxg7 gagne un pion car 3...Bxg7 provoquerait + la disparition des deux fous. + +figure.diagram-container + .diagram + | fen:r1bqkbnr/pp1ppppp/2n5/2p5/8/1P6/PBPPPPPP/RN1QKBNR: + figcaption After 1.b3 c5 2.Bb2 Nc6 + +h3 Source + +p + | La + a(href="https://www.chessvariants.com/rules/antimatter-chess") variante Antimatière + |  sur chessvariants.com. diff --git a/client/src/variants/Circular.js b/client/src/variants/Circular.js new file mode 100644 index 00000000..a5a5d644 --- /dev/null +++ b/client/src/variants/Circular.js @@ -0,0 +1,195 @@ +import { ChessRules, PiPo, Move } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { randInt, shuffle } from "@/utils/alea"; + +export const VariantRules = class CircularRules extends ChessRules { + static get HasFlags() { + return false; + } + + static get HasEnpassant() { + return false; + } + + // TODO: CanFlip --> also for racing kings (answer is false) + + // TODO: shuffle on 1st and 5th ranks + static GenRandInitFen() { + let pieces = { w: new Array(8), b: new Array(8) }; + // Shuffle pieces on first and last rank + for (let c of ["w", "b"]) { + let positions = ArrayFun.range(8); + + // Get random squares for bishops + let randIndex = 2 * randInt(4); + const bishop1Pos = positions[randIndex]; + // The second bishop must be on a square of different color + let randIndex_tmp = 2 * randInt(4) + 1; + const bishop2Pos = positions[randIndex_tmp]; + // Remove chosen squares + positions.splice(Math.max(randIndex, randIndex_tmp), 1); + positions.splice(Math.min(randIndex, randIndex_tmp), 1); + + // Get random squares for knights + randIndex = randInt(6); + const knight1Pos = positions[randIndex]; + positions.splice(randIndex, 1); + randIndex = randInt(5); + const knight2Pos = positions[randIndex]; + positions.splice(randIndex, 1); + + // Get random square for queen + randIndex = randInt(4); + const queenPos = positions[randIndex]; + positions.splice(randIndex, 1); + + // Rooks and king positions are now fixed, + // because of the ordering rook-king-rook + const rook1Pos = positions[0]; + const kingPos = positions[1]; + const rook2Pos = positions[2]; + + // Finally put the shuffled pieces in the board array + pieces[c][rook1Pos] = "r"; + pieces[c][knight1Pos] = "n"; + pieces[c][bishop1Pos] = "b"; + pieces[c][queenPos] = "q"; + pieces[c][kingPos] = "k"; + pieces[c][bishop2Pos] = "b"; + pieces[c][knight2Pos] = "n"; + pieces[c][rook2Pos] = "r"; + } + return ( + pieces["b"].join("") + + "/pppppppp/8/8/8/8/PPPPPPPP/" + + pieces["w"].join("").toUpperCase() + + " w 0" + ); + } + + // TODO: adapt this for a circular board + getSlideNJumpMoves([x, y], steps, oneStep) { + let moves = []; + outerLoop: for (let step of steps) { + 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])); + if (oneStep !== undefined) continue outerLoop; + i += step[0]; + j += step[1]; + } + if (V.OnBoard(i, j) && this.canTake([x, y], [i, j])) + moves.push(this.getBasicMove([x, y], [i, j])); + } + return moves; + } + + // TODO: adapt: all pawns go in thz same direction! + getPotentialPawnMoves([x, y]) { + const color = this.turn; + let moves = []; + const [sizeX, sizeY] = [V.size.x, V.size.y]; + const shiftX = color == "w" ? -1 : 1; + const firstRank = color == "w" ? sizeX - 1 : 0; + const startRank = color == "w" ? sizeX - 2 : 1; + const lastRank = color == "w" ? 0 : sizeX - 1; + const pawnColor = this.getColor(x, y); //can be different for checkered + + // NOTE: next condition is generally true (no pawn on last rank) + if (x + shiftX >= 0 && x + shiftX < sizeX) { + const finalPieces = + x + shiftX == lastRank + ? [V.ROOK, V.KNIGHT, V.BISHOP, V.QUEEN] + : [V.PAWN]; + // One square forward + if (this.board[x + shiftX][y] == V.EMPTY) { + for (let piece of finalPieces) { + moves.push( + this.getBasicMove([x, y], [x + shiftX, y], { + c: pawnColor, + p: piece + }) + ); + } + // Next condition because pawns on 1st rank can generally jump + if ( + [startRank, firstRank].includes(x) && + this.board[x + 2 * shiftX][y] == V.EMPTY + ) { + // Two squares jump + moves.push(this.getBasicMove([x, y], [x + 2 * shiftX, y])); + } + } + // Captures + for (let shiftY of [-1, 1]) { + if ( + y + shiftY >= 0 && + y + shiftY < sizeY && + this.board[x + shiftX][y + shiftY] != V.EMPTY && + this.canTake([x, y], [x + shiftX, y + shiftY]) + ) { + for (let piece of finalPieces) { + moves.push( + this.getBasicMove([x, y], [x + shiftX, y + shiftY], { + c: pawnColor, + p: piece + }) + ); + } + } + } + } + + return moves; + } + + // What are the king moves from square x,y ? + getPotentialKingMoves(sq) { + return this.getSlideNJumpMoves( + sq, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + } + + // TODO: check boundaries here as well + isAttackedByPawn([x, y], colors) { + for (let c of colors) { + let pawnShift = c == "w" ? 1 : -1; + if (x + pawnShift >= 0 && x + pawnShift < V.size.x) { + for (let i of [-1, 1]) { + if ( + y + i >= 0 && + y + i < V.size.y && + this.getPiece(x + pawnShift, y + i) == V.PAWN && + this.getColor(x + pawnShift, y + i) == c + ) { + return true; + } + } + } + } + return false; + } + + // TODO: adapt this function + isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) { + for (let step of steps) { + let rx = x + step[0], + ry = y + step[1]; + while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) { + rx += step[0]; + ry += step[1]; + } + if ( + V.OnBoard(rx, ry) && + this.getPiece(rx, ry) === piece && + colors.includes(this.getColor(rx, ry)) + ) { + return true; + } + } + return false; + } +}; diff --git a/server/db/populate.sql b/server/db/populate.sql index 384b7356..a86a8740 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -11,6 +11,7 @@ insert or ignore into Variants (name,description) values ('Berolina', 'Pawns move diagonally'), ('Checkered', 'Shared pieces'), ('Chess960', 'Standard rules'), + ('Circular', 'Run forward'), ('Crazyhouse', 'Captures reborn'), ('Dark', 'In the shadow'), ('Enpassant', 'Capture en passant'), -- 2.44.0