From: Benjamin Auder Date: Thu, 25 Feb 2021 10:47:01 +0000 (+0100) Subject: Add 'Connect' variant X-Git-Url: https://git.auder.net/?p=vchess.git;a=commitdiff_plain;h=7d7e947f74907222f1d98e17a0042fdac08c6769 Add 'Connect' variant --- diff --git a/client/src/translations/en.js b/client/src/translations/en.js index 4d93adc7..acd3fdbc 100644 --- a/client/src/translations/en.js +++ b/client/src/translations/en.js @@ -172,6 +172,7 @@ export const translations = { "Absorb powers": "Absorb powers", "African Draughts": "African Draughts", "Align five stones": "Align five stones", + "Align four pawns": "Align four pawns", "All of the same color": "All of the same color", "Ancient rules": "Ancient rules", "As in the movie": "As in the movie", diff --git a/client/src/translations/es.js b/client/src/translations/es.js index a07a5c12..a648854a 100644 --- a/client/src/translations/es.js +++ b/client/src/translations/es.js @@ -171,6 +171,7 @@ export const translations = { "A wizard in the corner": "Un mago en la esquina", "Absorb powers": "Absorber poderes", "African Draughts": "Damas africanas", + "Align four pawns": "Alinea cuatro peones", "Align five stones": "Alinea cinco piedras", "All of the same color": "Todo el mismo color", "Ancient rules": "Viejas reglas", diff --git a/client/src/translations/fr.js b/client/src/translations/fr.js index 247b46f0..282117af 100644 --- a/client/src/translations/fr.js +++ b/client/src/translations/fr.js @@ -171,6 +171,7 @@ export const translations = { "A wizard in the corner": "Un sorcier dans le coin", "Absorb powers": "Absorber les pouvoirs", "African Draughts": "Dames africaines", + "Align four pawns": "Alignez quatre pions", "Align five stones": "Alignez cinq pierres", "All of the same color": "Tout de la même couleur", "Ancient rules": "Règles anciennes", diff --git a/client/src/translations/rules/Connect/en.pug b/client/src/translations/rules/Connect/en.pug new file mode 100644 index 00000000..c583d9b2 --- /dev/null +++ b/client/src/translations/rules/Connect/en.pug @@ -0,0 +1,10 @@ +p.boxed + | Black wins by lining up 4 pawns. Infinite pawns supply. + +p. + Black initially has only the king, and can only land pawns on the board + (or play a regular move); if four are aligned in any direction, it's a win. + Of course black could also win by checkmate, but it's rather unlikely. + White wins by checkmating the enemy king. + +p This variant was invented by madi (2021) on vchess Discord. diff --git a/client/src/translations/rules/Connect/es.pug b/client/src/translations/rules/Connect/es.pug new file mode 100644 index 00000000..697a0df9 --- /dev/null +++ b/client/src/translations/rules/Connect/es.pug @@ -0,0 +1,11 @@ +p.boxed + | Las negras ganan alineando 4 peones. Fondo de peones infinito. + +p. + Las negras eran solo su rey, y solo podían lanzarse en paracaídas + peones en el tablero de ajedrez (o jugar un movimiento normal); si cuatro + están alineados en cualquier dirección, se gana. Por supuesto que las negras + podrían también gana por jaque mate, pero eso es bastante improbable. + Las blancas ganan por matar al rey contrario. + +P Esta variante fue inventada por madi (2021) en el Discord vchess. diff --git a/client/src/translations/rules/Connect/fr.pug b/client/src/translations/rules/Connect/fr.pug new file mode 100644 index 00000000..e16615a7 --- /dev/null +++ b/client/src/translations/rules/Connect/fr.pug @@ -0,0 +1,11 @@ +p.boxed + | Les noirs gagnent en alignant 4 pions. Réserve de pions infinie. + +p. + Les noirs n'ont initialement que leur roi, et ne peuvent parachuter que des + pions sur l'échiquier (ou jouer un coup normal) ; si quatre sont alignés + dans n'importe quelle direction, c'est gagné. Bien sûr les noirs pourraient + aussi gagner par échec et mat, mais c'est assez improbable. + Les blancs gagnent en matant le roi adverse. + +p Cette variante a été inventée par madi (2021) sur le Discord vchess. diff --git a/client/src/translations/variants/en.pug b/client/src/translations/variants/en.pug index 966f96f5..672e5a53 100644 --- a/client/src/translations/variants/en.pug +++ b/client/src/translations/variants/en.pug @@ -462,6 +462,7 @@ p. "Ambiguous", "Bario", "Bicolour", + "Connect", "Convert", "Evolution", "Forward", diff --git a/client/src/translations/variants/es.pug b/client/src/translations/variants/es.pug index 90196f68..2435c567 100644 --- a/client/src/translations/variants/es.pug +++ b/client/src/translations/variants/es.pug @@ -472,6 +472,7 @@ p. "Ambiguous", "Bario", "Bicolour", + "Connect", "Convert", "Evolution", "Forward", diff --git a/client/src/translations/variants/fr.pug b/client/src/translations/variants/fr.pug index 7729aaf0..6eb7776e 100644 --- a/client/src/translations/variants/fr.pug +++ b/client/src/translations/variants/fr.pug @@ -470,6 +470,7 @@ p. "Ambiguous", "Bario", "Bicolour", + "Connect", "Convert", "Evolution", "Forward", diff --git a/client/src/variants/Connect.js b/client/src/variants/Connect.js new file mode 100644 index 00000000..0e205c8e --- /dev/null +++ b/client/src/variants/Connect.js @@ -0,0 +1,201 @@ +import { ChessRules, Move, PiPo } from "@/base_rules"; + +export class ConnectRules extends ChessRules { + + static GenRandInitFen(randomness) { + const baseFen = ChessRules.GenRandInitFen(Math.min(randomness, 1)); + return "4k3/8" + baseFen.substring(17, 50) + " -"; + } + + getReservePpath() { + return "bp"; + } + + static get RESERVE_PIECES() { + return [V.PAWN]; //only black pawns + } + + getColor(i, j) { + if (i >= V.size.x) return "b"; + return this.board[i][j].charAt(0); + } + + getPiece(i, j) { + if (i >= V.size.x) return V.PAWN; + return this.board[i][j].charAt(1); + } + + static IsGoodFlags(flags) { + // Only white can castle + return !!flags.match(/^[a-z]{2,2}$/); + } + + getFlagsFen() { + return this.castleFlags['w'].map(V.CoordToColumn).join(""); + } + + setFlags(fenflags) { + this.castleFlags = { 'w': [-1, -1] }; + for (let i = 0; i < 2; i++) + this.castleFlags['w'][i] = V.ColumnToCoord(fenflags.charAt(i)); + } + + setOtherVariables(fen) { + super.setOtherVariables(fen); + this.reserve = { b: { [V.PAWN]: 1 } }; + } + + getReserveMoves() { + if (this.turn != 'b') return []; + let moves = []; + for (let i = 1; i <= 6; i++) { + for (let j = 0; j < V.size.y; j++) { + if (this.board[i][j] == V.EMPTY) { + let mv = new Move({ + appear: [ + new PiPo({ + x: i, + y: j, + c: 'b', + p: 'p' + }) + ], + vanish: [], + start: { x: 9, y: 0 }, + end: { x: i, y: j } + }); + moves.push(mv); + } + } + } + return moves; + } + + getPotentialMovesFrom(sq) { + if (sq[0] >= V.size.x) return this.getReserveMoves(); + return super.getPotentialMovesFrom(sq); + } + + getPotentialKingMoves([x, y]) { + if (this.getColor(x, y) == 'w') return super.getPotentialKingMoves([x, y]); + // Black doesn't castle: + return super.getSlideNJumpMoves( + [x, y], + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + } + + getAllValidMoves() { + return ( + super.getAllValidMoves().concat( + super.filterValid(this.getReserveMoves())) + ); + } + + atLeastOneMove() { + if (super.atLeastOneMove()) return true; + // Search one reserve move + if (this.filterValid(this.getReserveMoves()).length > 0) return true; + return false; + } + + updateCastleFlags(move, piece) { + // Only white can castle: + const firstRank = 7; + if (piece == V.KING && move.appear[0].c == 'w') + this.castleFlags['w'] = [8, 8]; + else if ( + move.start.x == firstRank && + this.castleFlags['w'].includes(move.start.y) + ) { + const flagIdx = (move.start.y == this.castleFlags['w'][0] ? 0 : 1); + this.castleFlags['w'][flagIdx] = 8; + } + else if ( + move.end.x == firstRank && + this.castleFlags['w'].includes(move.end.y) + ) { + const flagIdx = (move.end.y == this.castleFlags['w'][0] ? 0 : 1); + this.castleFlags['w'][flagIdx] = 8; + } + } + + getCurrentScore() { + const score = super.getCurrentScore(); + if (score != "*") return score; + // Check pawns connection: + 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 && + this.getColor(i, j) == 'b' && + this.getPiece(i, j) == V.PAWN + ) { + // Exploration "rightward + downward" is enough + for (let step of [[1, 0], [0, 1], [1, 1], [-1, 1]]) { + let [ii, jj] = [i + step[0], j + step[1]]; + let kounter = 1; + while ( + V.OnBoard(ii, jj) && + this.board[ii][jj] != V.EMPTY && + this.getColor(ii, jj) == 'b' && + this.getPiece(ii, jj) != V.KING + ) { + kounter++; + ii += step[0]; + jj += step[1]; + } + if (kounter == 4) return "0-1"; + } + } + } + } + return "*"; + } + + evalPosition() { + let evaluation = 0; + // Count white material + check pawns alignments + let maxAlign = 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 piece = this.getPiece(i, j); + if (piece != V.KING) { + const color = this.getColor(i, j); + if (color == 'w') evaluation += V.VALUES[piece]; + else { + // Exploration "rightward + downward" is enough + for (let step of [[1, 0], [0, 1], [1, 1], [-1, 1]]) { + let [ii, jj] = [i + step[0], j + step[1]]; + let kounter = 1; + while ( + V.OnBoard(ii, jj) && + this.board[ii][jj] != V.EMPTY && + this.getColor(ii, jj) == 'b' && + this.getPiece(ii, jj) != V.KING + ) { + kounter++; + ii += step[0]; + jj += step[1]; + } + if (kounter > maxAlign) maxAlign = kounter; + } + } + } + } + } + } + // -1 for two aligned pawns, -3 for 3 aligned pawns. + if ([1, 2].includes(maxAlign)) maxAlign--; + return evaluation - maxAlign; + } + + getNotation(move) { + if (move.vanish.length == 0) return "@" + V.CoordsToSquare(move.end); + return super.getNotation(move); + } + +}; + diff --git a/server/db/populate.sql b/server/db/populate.sql index c08dc9e3..3208b594 100644 --- a/server/db/populate.sql +++ b/server/db/populate.sql @@ -47,6 +47,7 @@ insert or ignore into Variants (name, description) values ('Circular', 'Run forward'), ('Clorange', 'A Clockwork Orange'), ('Colorbound', 'The colorbound clobberers'), + ('Connect', 'Align four pawns'), ('Convert', 'Convert enemy pieces'), ('Coregal', 'Two royal pieces'), ('Coronation', 'Long live the Queen'),