From: Benjamin Auder Date: Sun, 13 Dec 2020 01:49:08 +0000 (+0100) Subject: Almost completed Empire Chess. Next is Synochess (+ rules translations) X-Git-Url: https://git.auder.net/variants/Chakart/doc/html/scripts/common.css?a=commitdiff_plain;h=1269441e90bb8437bc63cc6cca63ca050f1e2aae;p=vchess.git Almost completed Empire Chess. Next is Synochess (+ rules translations) --- diff --git a/client/public/images/pieces/Empire/wc.svg b/client/public/images/pieces/Empire/wc.svg new file mode 100644 index 00000000..74ce101d --- /dev/null +++ b/client/public/images/pieces/Empire/wc.svg @@ -0,0 +1,96 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Empire/wd.svg b/client/public/images/pieces/Empire/wd.svg new file mode 100644 index 00000000..d6b8d8b7 --- /dev/null +++ b/client/public/images/pieces/Empire/wd.svg @@ -0,0 +1,115 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Empire/we.svg b/client/public/images/pieces/Empire/we.svg new file mode 100644 index 00000000..5a977168 --- /dev/null +++ b/client/public/images/pieces/Empire/we.svg @@ -0,0 +1,118 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Empire/wk.svg b/client/public/images/pieces/Empire/wk.svg new file mode 100644 index 00000000..8f885c20 --- /dev/null +++ b/client/public/images/pieces/Empire/wk.svg @@ -0,0 +1,127 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Empire/wp.svg b/client/public/images/pieces/Empire/wp.svg new file mode 100644 index 00000000..89ce4af1 --- /dev/null +++ b/client/public/images/pieces/Empire/wp.svg @@ -0,0 +1,95 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Empire/wq.svg b/client/public/images/pieces/Empire/wq.svg new file mode 100644 index 00000000..30346653 --- /dev/null +++ b/client/public/images/pieces/Empire/wq.svg @@ -0,0 +1,99 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff --git a/client/public/images/pieces/Empire/ws.svg b/client/public/images/pieces/Empire/ws.svg new file mode 100644 index 00000000..edefbefa --- /dev/null +++ b/client/public/images/pieces/Empire/ws.svg @@ -0,0 +1,88 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/client/public/images/pieces/Empire/wt.svg b/client/public/images/pieces/Empire/wt.svg new file mode 100644 index 00000000..68001024 --- /dev/null +++ b/client/public/images/pieces/Empire/wt.svg @@ -0,0 +1,125 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/client/public/variants/Empire/Board.png b/client/public/variants/Empire/Board.png new file mode 100644 index 00000000..efbfec85 --- /dev/null +++ b/client/public/variants/Empire/Board.png @@ -0,0 +1 @@ +#$# git-fat 759292776cfaf7e3ed788fe32d14b99c138fb211 34156 diff --git a/client/public/variants/Empire/EagleMoves.png b/client/public/variants/Empire/EagleMoves.png new file mode 100644 index 00000000..0f264b92 --- /dev/null +++ b/client/public/variants/Empire/EagleMoves.png @@ -0,0 +1 @@ +#$# git-fat 471867cda344bda2feee24069e7eb8f1af3029c3 15689 diff --git a/client/src/translations/rules/Empire/en.pug b/client/src/translations/rules/Empire/en.pug new file mode 100644 index 00000000..d0fbaa6a --- /dev/null +++ b/client/src/translations/rules/Empire/en.pug @@ -0,0 +1,118 @@ +p.boxed + | The player in first has a different set of pieces, + | moving generally like a queen but capturing (almost) as usual. + +figure + img.img-center(src="/variants/Empire/Board.png") + figcaption.text-center Initial deterministic setup. + +p. + Empire Chess is a chess variant designed in 2020 by Couch Tomato, the third + asymmetric game. This game pits the army of the "Empire" (gold) against the + original chess army (the "Kingdom", black). The Empire's unique feature is + that its pieces move like queens but attack differently. + +h3 General Rules + +ol + li The Empire (gold) always moves first. + li The Empire cannot castle. + li. + As the Empire's pawns start on the third rank, they do not have the + option to move two spaces or be captured by en passant. Kingdom pawns + retain the ability to move two spaces initially and to be captured via + en passant. + li Pawns on either side can only promote to a queen. + li. + An additional method of victory is available: called campmate. + Campmate is achieved by moving one's king into the final rank + without being check. + li Stalemate and repetition are both losses. + li. + Facing kings — The King and Kaiser (Imperial King) may not face + each other on a file or rank, similar to Xiangqi. + +h3 Imperial Pieces + +p. + There are five new units unique to the Empire: 2 Siege Towers, 2 Eagles, + 2 Cardinals, 1 Duke, and 2 Soldiers. The bottom rank are all stronger than + their Kingdom counterparts with the exception of the Duke. + +ul + li. + Kaiser (K) — The Imperial king is called the Kaiser and has a + different symbol, but is essentially the same as the Kingdom's King, + also using the same abbreviation (K) — the change is purely + aesthetic and thematic. + li. + Soldier (S) — The Soldiers are the two pawn-like pieces replacing + the two middle pawns. These act like promoted soldiers in Xiangqi and + can move one square orthogonally in any direction except backwards. + +p. + The remaining pieces are divergent pieces, pieces that move differently + than they attack (such as pawns). Imperial pieces all move as queens, + but attack like their Kingdom counterpart except for the Duke. + +ul + li. + Siege Tower (T) — The Siege Tower, or Tower for short, moves like a + Queen but attacks like a Rook. + li Eagle (E) — The Eagle moves like a Queen but attacks like a Knight. + li. + Cardinal (C) — The Cardinal moves like a Queen + but attacks like a Bishop. + li Duke (D) — The Duke moves like a Queen but attacks like a King. + +figure + img.img-center(src="/variants/Empire/EagleMoves.png") + figcaption.text-center Eagle's moves (green) & captures (red). + +h3 Piece valuation + +p The following simplified values are used by the bot: + +table + tr + th Kingdom piece + th Value + th Imperial piece + th Value + tr + td Pawn + td 1 + td Pawn + td 1 + tr + td Queen + td 9 + td Duke + td 4 + tr + td Bishop + td 3 + td Cardinal + td 4 + tr + td Knight + td 3 + td Eagle + td 7 + tr + td Rook + td 5 + td Siege Tower + td 7 + tr + td Soldier + td 2 + td - + td - + +h3 More information + +p + | See + a(href="https://www.pychess.org/variant/empirechess") Empire Chess + |  on pychess-variants, where you can also play this variant. diff --git a/client/src/translations/rules/Empire/es.pug b/client/src/translations/rules/Empire/es.pug new file mode 100644 index 00000000..21203baa --- /dev/null +++ b/client/src/translations/rules/Empire/es.pug @@ -0,0 +1 @@ +p.boxed TODO diff --git a/client/src/translations/rules/Empire/fr.pug b/client/src/translations/rules/Empire/fr.pug new file mode 100644 index 00000000..21203baa --- /dev/null +++ b/client/src/translations/rules/Empire/fr.pug @@ -0,0 +1 @@ +p.boxed TODO diff --git a/client/src/translations/rules/Synochess/en.pug b/client/src/translations/rules/Synochess/en.pug new file mode 100644 index 00000000..21203baa --- /dev/null +++ b/client/src/translations/rules/Synochess/en.pug @@ -0,0 +1 @@ +p.boxed TODO diff --git a/client/src/translations/rules/Synochess/es.pug b/client/src/translations/rules/Synochess/es.pug new file mode 100644 index 00000000..21203baa --- /dev/null +++ b/client/src/translations/rules/Synochess/es.pug @@ -0,0 +1 @@ +p.boxed TODO diff --git a/client/src/translations/rules/Synochess/fr.pug b/client/src/translations/rules/Synochess/fr.pug new file mode 100644 index 00000000..21203baa --- /dev/null +++ b/client/src/translations/rules/Synochess/fr.pug @@ -0,0 +1 @@ +p.boxed TODO diff --git a/client/src/variants/Empire.js b/client/src/variants/Empire.js new file mode 100644 index 00000000..2ce84930 --- /dev/null +++ b/client/src/variants/Empire.js @@ -0,0 +1,436 @@ +import { ChessRules } from "@/base_rules"; + +export class EmpireRules extends ChessRules { + + static get PawnSpecs() { + return Object.assign( + {}, + ChessRules.PawnSpecs, + { promotions: [V.QUEEN] } + ); + } + + static get LoseOnRepetition() { + return true; + } + + static IsGoodFlags(flags) { + // Only black can castle + return !!flags.match(/^[a-z]{2,2}$/); + } + + getPpath(b) { + return (b[0] == 'w' ? "Empire/" : "") + b; + } + + static GenRandInitFen(randomness) { + if (randomness == 0) + return "rnbqkbnr/pppppppp/8/8/8/PPPSSPPP/8/TECDKCET w 0 ah -"; + + // Mapping kingdom --> empire: + const piecesMap = { + 'R': 'T', + 'N': 'E', + 'B': 'C', + 'Q': 'D', + 'K': 'K' + }; + + const baseFen = ChessRules.GenRandInitFen(randomness); + return ( + baseFen.substr(0, 24) + "PPPSSPPP/8/" + + baseFen.substr(35, 8).split('').map(p => piecesMap[p]).join('') + + baseFen.substr(43, 5) + baseFen.substr(50) + ); + } + + getFlagsFen() { + return this.castleFlags['b'].map(V.CoordToColumn).join(""); + } + + setFlags(fenflags) { + this.castleFlags = { 'b': [-1, -1] }; + for (let i = 0; i < 2; i++) + this.castleFlags['b'][i] = V.ColumnToCoord(fenflags.charAt(i)); + } + + static get TOWER() { + return 't'; + } + static get EAGLE() { + return 'e'; + } + static get CARDINAL() { + return 'c'; + } + static get DUKE() { + return 'd'; + } + static get SOLDIER() { + return 's'; + } + // Kaiser is technically a King, so let's keep things simple. + + static get PIECES() { + return ChessRules.PIECES.concat( + [V.TOWER, V.EAGLE, V.CARDINAL, V.DUKE, V.SOLDIER]); + } + + getPotentialMovesFrom(sq) { + let moves = []; + const piece = this.getPiece(sq[0], sq[1]); + switch (piece) { + case V.TOWER: + moves = this.getPotentialTowerMoves(sq); + break; + case V.EAGLE: + moves = this.getPotentialEagleMoves(sq); + break; + case V.CARDINAL: + moves = this.getPotentialCardinalMoves(sq); + break; + case V.DUKE: + moves = this.getPotentialDukeMoves(sq); + break; + case V.SOLDIER: + moves = this.getPotentialSoldierMoves(sq); + break; + default: + moves = super.getPotentialMovesFrom(sq); + } + if ( + piece != V.KING && + this.kingPos['w'][0] != this.kingPos['b'][0] && + this.kingPos['w'][1] != this.kingPos['b'][1] + ) { + return moves; + } + // TODO: factor two next "if" into one (rank/column...) + if (this.kingPos['w'][1] == this.kingPos['b'][1]) { + const colKing = this.kingPos['w'][1]; + let intercept = 0; //count intercepting pieces + let [kingPos1, kingPos2] = [this.kingPos['w'][0], this.kingPos['b'][0]]; + if (kingPos1 > kingPos2) [kingPos1, kingPos2] = [kingPos2, kingPos1]; + for (let i = kingPos1 + 1; i < kingPos2; i++) { + if (this.board[i][colKing] != V.EMPTY) intercept++; + } + if (intercept >= 2) return moves; + // intercept == 1 (0 is impossible): + // Any move not removing intercept is OK + return moves.filter(m => { + return ( + // From another column? + m.start.y != colKing || + // From behind a king? (including kings themselves!) + m.start.x <= kingPos1 || + m.start.x >= kingPos2 || + // Intercept piece moving: must remain in-between + ( + m.end.y == colKing && + m.end.x > kingPos1 && + m.end.x < kingPos2 + ) + ); + }); + } + if (this.kingPos['w'][0] == this.kingPos['b'][0]) { + const rowKing = this.kingPos['w'][0]; + let intercept = 0; //count intercepting pieces + let [kingPos1, kingPos2] = [this.kingPos['w'][1], this.kingPos['b'][1]]; + if (kingPos1 > kingPos2) [kingPos1, kingPos2] = [kingPos2, kingPos1]; + for (let i = kingPos1 + 1; i < kingPos2; i++) { + if (this.board[rowKing][i] != V.EMPTY) intercept++; + } + if (intercept >= 2) return moves; + // intercept == 1 (0 is impossible): + // Any move not removing intercept is OK + return moves.filter(m => { + return ( + // From another row? + m.start.x != rowKing || + // From "behind" a king? (including kings themselves!) + m.start.y <= kingPos1 || + m.start.y >= kingPos2 || + // Intercept piece moving: must remain in-between + ( + m.end.x == rowKing && + m.end.y > kingPos1 && + m.end.y < kingPos2 + ) + ); + }); + } + // piece == king: check only if move.end.y == enemy king column, + // or if move.end.x == enemy king rank. + const color = this.getColor(sq[0], sq[1]); + const oppCol = V.GetOppCol(color); + // check == -1 if (row, or col) unchecked, 1 if checked and occupied, + // 0 if checked and clear + let check = [-1, -1]; + return moves.filter(m => { + if ( + m.end.y != this.kingPos[oppCol][1] && + m.end.x != this.kingPos[oppCol][0] + ) { + return true; + } + // TODO: factor two next "if"... + if (m.end.x == this.kingPos[oppCol][0]) { + if (check[0] < 0) { + // Do the check: + check[0] = 0; + let [kingPos1, kingPos2] = + [this.kingPos[color][1], this.kingPos[oppCol][1]]; + if (kingPos1 > kingPos2) [kingPos1, kingPos2] = [kingPos2, kingPos1]; + for (let i = kingPos1 + 1; i < kingPos2; i++) { + if (this.board[m.end.x][i] != V.EMPTY) { + check[0]++; + break; + } + } + return check[0] == 1; + } + // Check already done: + return check[0] == 1; + } + //if (m.end.y == this.kingPos[oppCol][1]) //true... + if (check[1] < 0) { + // Do the check: + check[1] = 0; + let [kingPos1, kingPos2] = + [this.kingPos[color][0], this.kingPos[oppCol][0]]; + if (kingPos1 > kingPos2) [kingPos1, kingPos2] = [kingPos2, kingPos1]; + for (let i = kingPos1 + 1; i < kingPos2; i++) { + if (this.board[i][m.end.y] != V.EMPTY) { + check[1]++; + break; + } + } + return check[1] == 1; + } + // Check already done: + return check[1] == 1; + }); + } + + getSlideNJumpMoves_([x, y], steps, oneStep) { + let moves = []; + outerLoop: for (let step of steps) { + const s = step.s; + let i = x + s[0]; + let j = y + s[1]; + while (V.OnBoard(i, j) && this.board[i][j] == V.EMPTY) { + if (!step.onlyTake) moves.push(this.getBasicMove([x, y], [i, j])); + // NOTE: (bad) HACK here, since onlyTake is true only for Eagle + // capturing moves, which are oneStep... + if (!!oneStep || !!step.onlyTake) continue outerLoop; + i += s[0]; + j += s[1]; + } + if (V.OnBoard(i, j) && this.canTake([x, y], [i, j]) && !step.onlyMove) + moves.push(this.getBasicMove([x, y], [i, j])); + } + return moves; + } + + static get steps() { + return ( + Object.assign( + { + t: [ + { s: [-1, 0] }, + { s: [1, 0] }, + { s: [0, -1] }, + { s: [0, 1] }, + { s: [-1, -1], onlyMove: true }, + { s: [-1, 1], onlyMove: true }, + { s: [1, -1], onlyMove: true }, + { s: [1, 1], onlyMove: true } + ], + c: [ + { s: [-1, 0], onlyMove: true }, + { s: [1, 0], onlyMove: true }, + { s: [0, -1], onlyMove: true }, + { s: [0, 1], onlyMove: true }, + { s: [-1, -1] }, + { s: [-1, 1] }, + { s: [1, -1] }, + { s: [1, 1] } + ], + e: [ + { s: [-1, 0], onlyMove: true }, + { s: [1, 0], onlyMove: true }, + { s: [0, -1], onlyMove: true }, + { s: [0, 1], onlyMove: true }, + { s: [-1, -1], onlyMove: true }, + { s: [-1, 1], onlyMove: true }, + { s: [1, -1], onlyMove: true }, + { s: [1, 1], onlyMove: true }, + { s: [-2, -1], onlyTake: true }, + { s: [-2, 1], onlyTake: true }, + { s: [-1, -2], onlyTake: true }, + { s: [-1, 2], onlyTake: true }, + { s: [1, -2], onlyTake: true }, + { s: [1, 2], onlyTake: true }, + { s: [2, -1], onlyTake: true }, + { s: [2, 1], onlyTake: true } + ] + }, + ChessRules.steps + ) + ); + } + + getPotentialTowerMoves(sq) { + return this.getSlideNJumpMoves_(sq, V.steps[V.TOWER]); + } + + getPotentialCardinalMoves(sq) { + return this.getSlideNJumpMoves_(sq, V.steps[V.CARDINAL]); + } + + getPotentialEagleMoves(sq) { + return this.getSlideNJumpMoves_(sq, V.steps[V.EAGLE]); + } + + getPotentialDukeMoves([x, y]) { + // Anything to capture around? mark other steps to explore after + let steps = []; + const oppCol = V.GetOppCol(this.getColor(x, y)); + let moves = []; + for (let s of V.steps[V.ROOK].concat(V.steps[V.BISHOP])) { + const [i, j] = [x + s[0], y + s[1]]; + if ( + V.OnBoard(i, j) && + this.board[i][j] != V.EMPTY && + this.getColor(i, j) == oppCol + ) { + moves.push(super.getBasicMove([x, y], [i, j])); + } + else steps.push({ s: s, onlyMove: true }); + } + if (steps.length > 0) { + const noncapturingMoves = this.getSlideNJumpMoves_([x, y], steps); + Array.prototype.push.apply(moves, noncapturingMoves); + } + return moves; + } + + getPotentialKingMoves([x, y]) { + if (this.getColor(x, y) == 'b') return super.getPotentialKingMoves([x, y]); + // Empire doesn't castle: + return super.getSlideNJumpMoves( + [x, y], + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), + "oneStep" + ); + } + + getPotentialSoldierMoves([x, y]) { + const c = this.getColor(x, y); + const shiftX = (c == 'w' ? -1 : 1); + const lastRank = (c == 'w' && x == 0 || c == 'b' && x == 9); + let steps = []; + if (!lastRank) steps.push([shiftX, 0]); + if (y > 0) steps.push([0, -1]); + if (y < 9) steps.push([0, 1]); + return super.getSlideNJumpMoves([x, y], steps, "oneStep"); + } + + isAttacked(sq, color) { + if (color == 'b') return super.isAttacked(sq, color); + // Empire: only pawn and king (+ queen if promotion) in common: + return ( + super.isAttackedByPawn(sq, color) || + this.isAttackedByTower(sq, color) || + this.isAttackedByEagle(sq, color) || + this.isAttackedByCardinal(sq, color) || + this.isAttackedByDuke(sq, color) || + this.isAttackedBySoldier(sq, color) || + super.isAttackedByKing(sq, color) || + super.isAttackedByQueen(sq, color) + ); + } + + isAttackedByTower(sq, color) { + return super.isAttackedBySlideNJump(sq, color, V.TOWER, V.steps[V.ROOK]); + } + + isAttackedByEagle(sq, color) { + return super.isAttackedBySlideNJump( + sq, color, V.EAGLE, V.steps[V.KNIGHT], "oneStep"); + } + + isAttackedByCardinal(sq, color) { + return super.isAttackedBySlideNJump( + sq, color, V.CARDINAL, V.steps[V.BISHOP]); + } + + isAttackedByDuke(sq, color) { + return ( + super.isAttackedBySlideNJump( + sq, color, V.DUKE, + V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep" + ) + ); + } + + isAttackedBySoldier([x, y], color) { + const shiftX = (color == 'w' ? 1 : -1); //shift from king + return super.isAttackedBySlideNJump( + [x, y], color, V.SOLDIER, [[shiftX, 0], [0, 1], [0, -1]], "oneStep"); + } + + updateCastleFlags(move, piece) { + // Only black can castle: + const firstRank = 0; + if (piece == V.KING && move.appear[0].c == 'b') + this.castleFlags['b'] = [8, 8]; + else if ( + move.start.x == firstRank && + this.castleFlags['b'].includes(move.start.y) + ) { + const flagIdx = (move.start.y == this.castleFlags['b'][0] ? 0 : 1); + this.castleFlags['b'][flagIdx] = 8; + } + else if ( + move.end.x == firstRank && + this.castleFlags['b'].includes(move.end.y) + ) { + const flagIdx = (move.end.y == this.castleFlags['b'][0] ? 0 : 1); + this.castleFlags['b'][flagIdx] = 8; + } + } + + getCurrentScore() { + // Turn has changed: + const color = V.GetOppCol(this.turn); + const lastRank = (color == 'w' ? 0 : 7); + if (this.kingPos[color][0] == lastRank) + // The opposing edge is reached! + return color == "w" ? "1-0" : "0-1"; + if (this.atLeastOneMove()) return "*"; + // Game over + const oppCol = this.turn; + return (oppCol == "w" ? "0-1" : "1-0"); + } + + static get VALUES() { + return Object.assign( + {}, + ChessRules.VALUES, + { + t: 7, + e: 7, + c: 4, + d: 4, + s: 2 + } + ); + } + + static get SEARCH_DEPTH() { + return 2; + } + +}; diff --git a/client/src/variants/Minixiangqi.js b/client/src/variants/Minixiangqi.js index 34e7d198..f467e2dc 100644 --- a/client/src/variants/Minixiangqi.js +++ b/client/src/variants/Minixiangqi.js @@ -74,6 +74,10 @@ export class MinixiangqiRules extends XiangqiRules { return evaluation; } + static get SEARCH_DEPTH() { + return 3; + } + // Also no randomization here static GenRandInitFen() { return "rcnkncr/p1ppp1p/7/7/7/P1PPP1P/RCNKNCR w 0"; diff --git a/client/src/variants/Orda.js b/client/src/variants/Orda.js index 308ae2f4..1fd5c1d3 100644 --- a/client/src/variants/Orda.js +++ b/client/src/variants/Orda.js @@ -37,58 +37,11 @@ export class OrdaRules extends ChessRules { 'k': 'k' }; - let pieces = { w: new Array(8), b: new Array(8) }; - let flags = ""; - // Shuffle pieces on first (and last rank if randomness == 2) - for (let c of ["w", "b"]) { - if (c == 'b' && randomness == 1) { - pieces['b'] = pieces['w'].map(p => piecesMap[p]); - break; - } - - // TODO: same code as in base_rules. Should extract and factorize? - - let positions = ArrayFun.range(8); - - let randIndex = 2 * randInt(4); - const bishop1Pos = positions[randIndex]; - let randIndex_tmp = 2 * randInt(4) + 1; - const bishop2Pos = positions[randIndex_tmp]; - positions.splice(Math.max(randIndex, randIndex_tmp), 1); - positions.splice(Math.min(randIndex, randIndex_tmp), 1); - - randIndex = randInt(6); - const knight1Pos = positions[randIndex]; - positions.splice(randIndex, 1); - randIndex = randInt(5); - const knight2Pos = positions[randIndex]; - positions.splice(randIndex, 1); - - randIndex = randInt(4); - const queenPos = positions[randIndex]; - positions.splice(randIndex, 1); - - const rook1Pos = positions[0]; - const kingPos = positions[1]; - const rook2Pos = positions[2]; - - 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"; - if (c == 'b') pieces[c] = pieces[c].map(p => piecesMap[p]); - else flags = V.CoordToColumn(rook1Pos) + V.CoordToColumn(rook2Pos); - } - // Add turn + flags + enpassant + const baseFen = ChessRules.GenRandInitFen(randomness); return ( - pieces["b"].join("") + - "/8/pppppppp/8/8/8/PPPPPPPP/" + - pieces["w"].join("").toUpperCase() + - " w 0 " + flags + " -" + baseFen.substr(0, 8).split('').map(p => piecesMap[p]).join('') + + // Skip 3 first rows + black castle flags + "/8/pppppppp" + baseFen.substr(19, 31) + " -" ); } @@ -186,24 +139,8 @@ export class OrdaRules extends ChessRules { return onlyMoves.concat(onlyTakes); } - getPotentialLancerMoves(sq) { - const onlyMoves = this.getSlideNJumpMoves( - sq, - V.steps[V.KNIGHT], - "oneStep", - { onlyMove: true } - ); - const onlyTakes = this.getSlideNJumpMoves( - sq, - V.steps[V.ROOK], - null, - { onlyTake: true } - ); - return onlyMoves.concat(onlyTakes); - } - getPotentialKheshigMoves(sq) { - return this.getSlideNJumpMoves( + return super.getSlideNJumpMoves( sq, V.steps[V.KNIGHT].concat(V.steps[V.ROOK]).concat(V.steps[V.BISHOP]), "oneStep" @@ -211,7 +148,7 @@ export class OrdaRules extends ChessRules { } getPotentialYurtMoves(sq) { - return this.getSlideNJumpMoves( + return super.getSlideNJumpMoves( sq, V.steps[V.BISHOP].concat([ [1, 0] ]), "oneStep" @@ -221,7 +158,7 @@ export class OrdaRules extends ChessRules { getPotentialKingMoves([x, y]) { if (this.getColor(x, y) == 'w') return super.getPotentialKingMoves([x, y]); // Horde doesn't castle: - return this.getSlideNJumpMoves( + return super.getSlideNJumpMoves( [x, y], V.steps[V.ROOK].concat(V.steps[V.BISHOP]), "oneStep" diff --git a/client/src/variants/Synochess.js b/client/src/variants/Synochess.js new file mode 100644 index 00000000..b8021100 --- /dev/null +++ b/client/src/variants/Synochess.js @@ -0,0 +1,7 @@ +import { ChessRules } from "@/base_rules"; + +export class SynochessRules extends ChessRules { + + // TODO + +}; diff --git a/client/src/variants/Xiangqi.js b/client/src/variants/Xiangqi.js index 1b49cedb..5deed643 100644 --- a/client/src/variants/Xiangqi.js +++ b/client/src/variants/Xiangqi.js @@ -259,17 +259,8 @@ export class XiangqiRules extends ChessRules { 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; + return super.isAttackedBySlideNJump( + [x, y], color, V.PAWN, [[shiftX, 0], [0, 1], [0, -1]], "oneStep"); } knightStepsFromBishopStep(step) { @@ -369,6 +360,10 @@ export class XiangqiRules extends ChessRules { return evaluation; } + static get SEARCH_DEPTH() { + return 2; + } + static GenRandInitFen() { // No randomization here (TODO?) return "rneakaenr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNEAKAENR w 0";