From: Benjamin Auder Date: Thu, 19 Mar 2020 23:05:34 +0000 (+0100) Subject: Draft Coregal variant - still getCastleMoves() and updateCastleFlags() TODO X-Git-Url: https://git.auder.net/?p=vchess.git;a=commitdiff_plain;h=bb688df52df0713aba7b2c1c068614544f5ae96d Draft Coregal variant - still getCastleMoves() and updateCastleFlags() TODO --- diff --git a/client/src/base_rules.js b/client/src/base_rules.js index 267b2335..71fa13cf 100644 --- a/client/src/base_rules.js +++ b/client/src/base_rules.js @@ -132,8 +132,7 @@ export const ChessRules = class ChessRules { for (let row of rows) { let sumElts = 0; for (let i = 0; i < row.length; i++) { - if (['K','k'].includes(row[i])) - kings[row[i]] = true; + if (['K','k'].includes(row[i])) kings[row[i]] = true; if (V.PIECES.includes(row[i].toLowerCase())) sumElts++; else { const num = parseInt(row[i]); @@ -144,8 +143,7 @@ export const ChessRules = class ChessRules { if (sumElts != V.size.y) return false; } // Both kings should be on board: - if (Object.keys(kings).length != 2) - return false; + if (Object.keys(kings).length != 2) return false; return true; } @@ -433,7 +431,7 @@ export const ChessRules = class ChessRules { // Extract (relevant) flags from fen setFlags(fenflags) { // white a-castle, h-castle, black a-castle, h-castle - this.castleFlags = { w: [true, true], b: [true, true] }; + this.castleFlags = { w: [-1, -1], b: [-1, -1] }; for (let i = 0; i < 4; i++) { this.castleFlags[i < 2 ? "w" : "b"][i % 2] = V.ColumnToCoord(fenflags.charAt(i)); @@ -1081,13 +1079,15 @@ export const ChessRules = class ChessRules { this.postPlay(move); } - updateCastleFlags(move) { + updateCastleFlags(move, piece) { const c = V.GetOppCol(this.turn); const firstRank = (c == "w" ? V.size.x - 1 : 0); // Update castling flags if rooks are moved const oppCol = V.GetOppCol(c); const oppFirstRank = V.size.x - 1 - firstRank; - if ( + if (piece == V.KING && move.appear.length > 0) + this.castleFlags[c] = [V.size.y, V.size.y]; + else if ( move.start.x == firstRank && //our rook moves? this.castleFlags[c].includes(move.start.y) ) { @@ -1117,10 +1117,9 @@ export const ChessRules = class ChessRules { if (piece == V.KING && move.appear.length > 0) { this.kingPos[c][0] = move.appear[0].x; this.kingPos[c][1] = move.appear[0].y; - if (V.HasCastle) this.castleFlags[c] = [V.size.y, V.size.y]; return; } - if (V.HasCastle) this.updateCastleFlags(move); + if (V.HasCastle) this.updateCastleFlags(move, piece); } preUndo() {} @@ -1154,14 +1153,11 @@ export const ChessRules = class ChessRules { // What is the score ? (Interesting if game is over) getCurrentScore() { - if (this.atLeastOneMove()) - return "*"; - + if (this.atLeastOneMove()) return "*"; // Game over const color = this.turn; // No valid move: stalemate or checkmate? - if (!this.isAttacked(this.kingPos[color], V.GetOppCol(color))) - return "1/2"; + if (!this.underCheck(color)) return "1/2"; // OK, checkmate return (color == "w" ? "0-1" : "1-0"); } diff --git a/client/src/translations/rules/Coregal/en.pug b/client/src/translations/rules/Coregal/en.pug new file mode 100644 index 00000000..3a33838b --- /dev/null +++ b/client/src/translations/rules/Coregal/en.pug @@ -0,0 +1,2 @@ +p.boxed + | TODO diff --git a/client/src/translations/rules/Coregal/es.pug b/client/src/translations/rules/Coregal/es.pug new file mode 100644 index 00000000..3a33838b --- /dev/null +++ b/client/src/translations/rules/Coregal/es.pug @@ -0,0 +1,2 @@ +p.boxed + | TODO diff --git a/client/src/translations/rules/Coregal/fr.pug b/client/src/translations/rules/Coregal/fr.pug new file mode 100644 index 00000000..3a33838b --- /dev/null +++ b/client/src/translations/rules/Coregal/fr.pug @@ -0,0 +1,2 @@ +p.boxed + | TODO diff --git a/client/src/variants/Alice.js b/client/src/variants/Alice.js index d30acf05..9eb0e878 100644 --- a/client/src/variants/Alice.js +++ b/client/src/variants/Alice.js @@ -263,10 +263,7 @@ export class AliceRules extends ChessRules { } getCurrentScore() { - if (this.atLeastOneMove()) - // game not over - return "*"; - + if (this.atLeastOneMove()) return "*"; const pieces = Object.keys(V.ALICE_CODES); const color = this.turn; const kp = this.kingPos[color]; diff --git a/client/src/variants/Allmate1.js b/client/src/variants/Allmate1.js index 3d843005..d07474b3 100644 --- a/client/src/variants/Allmate1.js +++ b/client/src/variants/Allmate1.js @@ -203,8 +203,7 @@ export class Allmate1Rules extends ChessRules { if (kp[0] < 0) // King disappeared return color == "w" ? "0-1" : "1-0"; - if (this.atLeastOneMove()) - return "*"; + if (this.atLeastOneMove()) return "*"; // Kings still there, no moves: return "1/2"; } diff --git a/client/src/variants/Allmate2.js b/client/src/variants/Allmate2.js index 424ff61b..31d41bef 100644 --- a/client/src/variants/Allmate2.js +++ b/client/src/variants/Allmate2.js @@ -207,8 +207,7 @@ export class Allmate2Rules extends ChessRules { if (kp[0] < 0) // King disappeared return color == "w" ? "0-1" : "1-0"; - if (this.atLeastOneMove()) - return "*"; + if (this.atLeastOneMove()) return "*"; // Kings still there, no moves: return "1/2"; } diff --git a/client/src/variants/Antiking1.js b/client/src/variants/Antiking1.js index 75cc14cb..3aa02d35 100644 --- a/client/src/variants/Antiking1.js +++ b/client/src/variants/Antiking1.js @@ -192,21 +192,6 @@ export class Antiking1Rules extends BerolinaRules { this.antikingPos[c] = [move.start.x, move.start.y]; } - getCurrentScore() { - if (this.atLeastOneMove()) - return "*"; - - const color = this.turn; - const oppCol = V.GetOppCol(color); - if ( - !this.isAttacked(this.kingPos[color], oppCol) && - this.isAttacked(this.antikingPos[color], oppCol) - ) { - return "1/2"; - } - return color == "w" ? "0-1" : "1-0"; - } - static get VALUES() { return Object.assign( { a: 1000 }, diff --git a/client/src/variants/Antiking2.js b/client/src/variants/Antiking2.js index 0a427430..087dce35 100644 --- a/client/src/variants/Antiking2.js +++ b/client/src/variants/Antiking2.js @@ -132,21 +132,6 @@ export class Antiking2Rules extends ChessRules { this.antikingPos[c] = [move.start.x, move.start.y]; } - getCurrentScore() { - if (this.atLeastOneMove()) - return "*"; - - const color = this.turn; - const oppCol = V.GetOppCol(color); - if ( - !this.isAttacked(this.kingPos[color], oppCol) && - this.isAttacked(this.antikingPos[color], oppCol) - ) { - return "1/2"; - } - return color == "w" ? "0-1" : "1-0"; - } - static get VALUES() { return Object.assign( { a: 1000 }, diff --git a/client/src/variants/Atomic.js b/client/src/variants/Atomic.js index a6a5625b..889f2dff 100644 --- a/client/src/variants/Atomic.js +++ b/client/src/variants/Atomic.js @@ -151,8 +151,7 @@ export class AtomicRules extends ChessRules { if (kp[0] < 0) // King disappeared return color == "w" ? "0-1" : "1-0"; - if (this.atLeastOneMove()) - return "*"; + if (this.atLeastOneMove()) return "*"; if (!this.isAttacked(kp, V.GetOppCol(color))) return "1/2"; return color == "w" ? "0-1" : "1-0"; //checkmate } diff --git a/client/src/variants/Benedict.js b/client/src/variants/Benedict.js index fbc4b488..4ca5292d 100644 --- a/client/src/variants/Benedict.js +++ b/client/src/variants/Benedict.js @@ -154,8 +154,7 @@ export class BenedictRules extends ChessRules { const kp = this.kingPos[color]; if (this.getColor(kp[0], kp[1]) != color) return color == "w" ? "0-1" : "1-0"; - if (this.atLeastOneMove()) - return "*"; + if (this.atLeastOneMove()) return "*"; // Stalemate: return "1/2"; } diff --git a/client/src/variants/Cannibal.js b/client/src/variants/Cannibal.js index 6166860e..7860ab04 100644 --- a/client/src/variants/Cannibal.js +++ b/client/src/variants/Cannibal.js @@ -181,7 +181,7 @@ export class CannibalRules extends ChessRules { this.castleFlags[c] = [V.size.y, V.size.y]; return; } - super.updateCastleFlags(move); + super.updateCastleFlags(move, piece); } postUndo(move) { diff --git a/client/src/variants/Checkered.js b/client/src/variants/Checkered.js index d601d14a..d48d6322 100644 --- a/client/src/variants/Checkered.js +++ b/client/src/variants/Checkered.js @@ -357,10 +357,7 @@ export class CheckeredRules extends ChessRules { } getCurrentScore() { - if (this.atLeastOneMove()) - // game not over - return "*"; - + if (this.atLeastOneMove()) return "*"; const color = this.turn; // Artifically change turn, for checkered pawns this.turn = V.GetOppCol(this.turn); diff --git a/client/src/variants/Coregal.js b/client/src/variants/Coregal.js new file mode 100644 index 00000000..ff4f1dfa --- /dev/null +++ b/client/src/variants/Coregal.js @@ -0,0 +1,268 @@ +import { ChessRules } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { randInt, sample } from "@/utils/alea"; + +export class CoregalRules extends ChessRules { + static IsGoodPosition(position) { + if (!super.IsGoodPosition(position)) return false; + // Check that at least one queen of each color is there: + let queens = {}; + for (let row of rows) { + for (let i = 0; i < row.length; i++) + if (['Q','q'].includes(row[i])) queens[row[i]] = true; + } + if (Object.keys(queens).length != 2) return false; + return true; + } + + static IsGoodFlags(flags) { + return !!flags.match(/^[a-z]{8,8}$/); + } + + getCheckSquares(color) { + let squares = []; + const oppCol = V.GetOppCol(color); + if (this.isAttacked(this.kingPos[color], oppCol)) + squares.push(this.kingPos[color]); + for (let i=0; i= kingPos) randIndex++; + let queenPos = randIndex + 1; + + // Get random squares for rooks to the left and right of the queen + // and king: not all squares of the same colors (for bishops). + const minQR = Math.min(kingPos, queenPos); + const maxQR = Math.max(kingPos, queenPos); + let rook1Pos = randInt(minQR); + let rook2Pos = 7 - randInt(7 - maxQR); + + // Now, if we are unlucky all these 4 pieces may be on the same color. + const rem2 = [kingPos, queenPos, rook1Pos, rook2Pos].map(pos => pos % 2); + if (rem2.every(r => r == 0) || rem2.every(r => r == 1)) { + // Shift a random of these pieces to the left or right + switch (randInt(4)) { + case 0: + if (rook1Pos == 0) rook1Pos++; + else rook1Pos--; + break; + case 1: + if (Math.random() < 0.5) kingPos++; + else kingPos--; + break; + case 2: + if (Math.random() < 0.5) queenPos++; + else queenPos--; + break; + case 3: + if (rook2Pos == 7) rook2Pos--; + else rook2Pos++; + break; + } + } + let bishop1Options = { 0: true, 2: true, 4: true, 6: true }; + let bishop2Options = { 1: true, 3: true, 5: true, 7: true }; + [kingPos, queenPos, rook1Pos, rook2Pos].forEach(pos => { + if (!!bishop1Options[pos]) delete bishop1Options[pos]; + else if (!!bishop2Options[pos]) delete bishop2Options[pos]; + }); + const bishop1Pos = parseInt(sample(Object.keys(bishop1Options), 1)[0]); + const bishop2Pos = parseInt(sample(Object.keys(bishop2Options), 1)[0]); + + // Knights' positions are now determined + const forbidden = [ + kingPos, queenPos, rook1Pos, rook2Pos, bishop1Pos, bishop2Pos + ]; + const [knight1Pos, knight2Pos] = + ArrayFun.range(8).filter(pos => !forbidden.includes(pos)); + + 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"; + flags += V.CoordToColumn(rook1Pos) + V.CoordToColumn(queenPos) + + V.CoordToColumn(kingPos) + V.CoordToColumn(rook2Pos); + } + // Add turn + flags + enpassant + return ( + pieces["b"].join("") + + "/pppppppp/8/8/8/8/PPPPPPPP/" + + pieces["w"].join("").toUpperCase() + + " w 0 " + flags + " -" + ); + } + + setFlags(fenflags) { + // white a-castle, h-castle, black a-castle, h-castle + this.castleFlags = { w: [...Array(4)], b: [...Array(4)] }; + for (let i = 0; i < 8; i++) { + this.castleFlags[i < 4 ? "w" : "b"][i % 4] = + V.ColumnToCoord(fenflags.charAt(i)); + } + } + + getPotentialQueenMoves(sq) { + return super.getPotentialQueenMoves(sq).concat(this.getCastleMoves(sq)); + } + + getCastleMoves([x, y], castleInCheck) { + return []; +// const c = this.getColor(x, y); +// if (x != (c == "w" ? V.size.x - 1 : 0) || y != this.INIT_COL_KING[c]) +// return []; //x isn't first rank, or king has moved (shortcut) +// +// // Castling ? +// const oppCol = V.GetOppCol(c); +// let moves = []; +// let i = 0; +// // King, then rook: +// const finalSquares = [ +// [2, 3], +// [V.size.y - 2, V.size.y - 3] +// ]; +// castlingCheck: for ( +// let castleSide = 0; +// castleSide < 2; +// castleSide++ //large, then small +// ) { +// if (this.castleFlags[c][castleSide] >= V.size.y) continue; +// // If this code is reached, rooks and king are on initial position +// +// // NOTE: in some variants this is not a rook, but let's keep variable name +// const rookPos = this.castleFlags[c][castleSide]; +// const castlingPiece = this.getPiece(x, rookPos); +// if (this.getColor(x, rookPos) != c) +// // Rook is here but changed color (see Benedict) +// continue; +// +// // Nothing on the path of the king ? (and no checks) +// const finDist = finalSquares[castleSide][0] - y; +// let step = finDist / Math.max(1, Math.abs(finDist)); +// i = y; +// do { +// if ( +// (!castleInCheck && this.isAttacked([x, i], oppCol)) || +// (this.board[x][i] != V.EMPTY && +// // NOTE: next check is enough, because of chessboard constraints +// (this.getColor(x, i) != c || +// ![V.KING, castlingPiece].includes(this.getPiece(x, i)))) +// ) { +// continue castlingCheck; +// } +// i += step; +// } while (i != finalSquares[castleSide][0]); +// +// // Nothing on the path to the rook? +// step = castleSide == 0 ? -1 : 1; +// for (i = y + step; i != rookPos; i += step) { +// if (this.board[x][i] != V.EMPTY) continue castlingCheck; +// } +// +// // Nothing on final squares, except maybe king and castling rook? +// for (i = 0; i < 2; i++) { +// if ( +// this.board[x][finalSquares[castleSide][i]] != V.EMPTY && +// this.getPiece(x, finalSquares[castleSide][i]) != V.KING && +// finalSquares[castleSide][i] != rookPos +// ) { +// continue castlingCheck; +// } +// } +// +// // If this code is reached, castle is valid +// moves.push( +// new Move({ +// appear: [ +// new PiPo({ x: x, y: finalSquares[castleSide][0], p: V.KING, c: c }), +// new PiPo({ x: x, y: finalSquares[castleSide][1], p: castlingPiece, c: c }) +// ], +// vanish: [ +// new PiPo({ x: x, y: y, p: V.KING, c: c }), +// new PiPo({ x: x, y: rookPos, p: castlingPiece, c: c }) +// ], +// end: +// Math.abs(y - rookPos) <= 2 +// ? { x: x, y: rookPos } +// : { x: x, y: y + 2 * (castleSide == 0 ? -1 : 1) } +// }) +// ); +// } +// +// return moves; + } + + underCheck(color) { + const oppCol = V.GetOppCol(color); + if (this.isAttacked(this.kingPos[color], oppCol)) return true; + for (let i=0; i 0) +// this.castleFlags[c] = [V.size.y, V.size.y]; +// else if ( +// move.start.x == firstRank && //our rook moves? +// this.castleFlags[c].includes(move.start.y) +// ) { +// const flagIdx = (move.start.y == this.castleFlags[c][0] ? 0 : 1); +// this.castleFlags[c][flagIdx] = V.size.y; +// } else if ( +// move.end.x == oppFirstRank && //we took opponent rook? +// this.castleFlags[oppCol].includes(move.end.y) +// ) { +// const flagIdx = (move.end.y == this.castleFlags[oppCol][0] ? 0 : 1); +// this.castleFlags[oppCol][flagIdx] = V.size.y; +// } + } + + // NOTE: do not set queen value to 1000 or so, because there may be several. +}; diff --git a/client/src/variants/Extinction.js b/client/src/variants/Extinction.js index e2571378..746809f6 100644 --- a/client/src/variants/Extinction.js +++ b/client/src/variants/Extinction.js @@ -99,7 +99,6 @@ export class ExtinctionRules extends ChessRules { } return "*"; } - return this.turn == "w" ? "0-1" : "1-0"; //NOTE: currently unreachable... } diff --git a/client/src/variants/Magnetic.js b/client/src/variants/Magnetic.js index a6150cb5..ee9fe28a 100644 --- a/client/src/variants/Magnetic.js +++ b/client/src/variants/Magnetic.js @@ -196,7 +196,7 @@ export class MagneticRules extends ChessRules { // King disappeared return color == "w" ? "0-1" : "1-0"; if (this.atLeastOneMove()) - // game not over + // Game not over return "*"; return "1/2"; //no moves but kings still there } diff --git a/client/src/variants/Suction.js b/client/src/variants/Suction.js index d15a7c84..ac3aeb02 100644 --- a/client/src/variants/Suction.js +++ b/client/src/variants/Suction.js @@ -182,10 +182,8 @@ export class SuctionRules extends ChessRules { getCurrentScore() { const color = this.turn; const kp = this.kingPos[color]; - if (color == "w" && kp[0] == 0) - return "0-1"; - if (color == "b" && kp[0] == V.size.x - 1) - return "1-0"; + if (color == "w" && kp[0] == 0) return "0-1"; + if (color == "b" && kp[0] == V.size.x - 1) return "1-0"; // King is not on the opposite edge: game not over return "*"; } diff --git a/client/src/variants/Wildebeest.js b/client/src/variants/Wildebeest.js index 8192fa04..5bb50e81 100644 --- a/client/src/variants/Wildebeest.js +++ b/client/src/variants/Wildebeest.js @@ -225,10 +225,7 @@ export class WildebeestRules extends ChessRules { } getCurrentScore() { - if (this.atLeastOneMove()) - // game not over - return "*"; - + if (this.atLeastOneMove()) return "*"; // No valid move: game is lost (stalemate is a win) return this.turn == "w" ? "0-1" : "1-0"; } diff --git a/client/src/variants/Wormhole.js b/client/src/variants/Wormhole.js index f47fea39..20c9d119 100644 --- a/client/src/variants/Wormhole.js +++ b/client/src/variants/Wormhole.js @@ -270,8 +270,7 @@ export class WormholeRules extends ChessRules { } getCurrentScore() { - if (this.atLeastOneMove()) - return "*"; + if (this.atLeastOneMove()) return "*"; // No valid move: I lose return this.turn == "w" ? "0-1" : "1-0"; }