X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FRoyalrace.js;fp=client%2Fsrc%2Fvariants%2FRoyalrace.js;h=1ae531d5083fdb312861c265be0607c88a84f8ce;hb=0ba6420d3515e278b34c29e5afa1e58f6e08e9eb;hp=0000000000000000000000000000000000000000;hpb=71ef1664983cd58db3c3bbfdf6cb7c362474e9a5;p=vchess.git diff --git a/client/src/variants/Royalrace.js b/client/src/variants/Royalrace.js new file mode 100644 index 00000000..1ae531d5 --- /dev/null +++ b/client/src/variants/Royalrace.js @@ -0,0 +1,211 @@ +import { ChessRules } from "@/base_rules"; +import { ArrayFun } from "@/utils/array"; +import { randInt, shuffle } from "@/utils/alea"; + +export const VariantRules = class RoyalraceRules extends ChessRules { + static get HasFlags() { + return false; + } + + static get HasEnpassant() { + return false; + } + + static get CanFlip() { + return false; + } + + static get size() { + return { x: 11, y: 11 }; + } + + static GenRandInitFen() { + let pieces = { w: new Array(10), b: new Array(10) }; + // Shuffle pieces on first and second rank + for (let c of ["w", "b"]) { + // Reserve 4 and 5 which are pawns positions + let positions = ArrayFun.range(10).filter(i => i != 4 && i != 5); + + // 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); + + // Place the king at random on (remaining squares of) first row + let maxIndex = 4; + if (positions[maxIndex-1] >= 4) + maxIndex--; + if (positions[maxIndex-1] >= 4) + maxIndex--; + randIndex = randInt(maxIndex); + const kingPos = positions[randIndex]; + positions.splice(randIndex, 1); + + // Get random squares for knights + randIndex = randInt(5); + const knight1Pos = positions[randIndex]; + positions.splice(randIndex, 1); + randIndex = randInt(4); + const knight2Pos = positions[randIndex]; + positions.splice(randIndex, 1); + + // Get random squares for rooks + randIndex = randInt(3); + const rook1Pos = positions[randIndex]; + positions.splice(randIndex, 1); + randIndex = randInt(2); + const rook2Pos = positions[randIndex]; + positions.splice(randIndex, 1); + + // Queen position is now determined, + // because pawns are not placed at random + const queenPos = positions[0]; + + // 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"; + pieces[c][4] = "p"; + pieces[c][5] = "p"; + } + const whiteFen = pieces["w"].join("").toUpperCase(); + const blackFen = pieces["b"].join(""); + return ( + "11/11/11/11/11/11/11/11/11/" + + whiteFen.substr(5).split("").reverse().join("") + + "1" + + blackFen.substr(5).split("").reverse().join("") + + "/" + + whiteFen.substr(0,5) + "1" + blackFen.substr(0,5) + + " w 0" + ); + } + + getPotentialPawnMoves([x, y]) { + // Normal moves (as a rook) + let moves = + this.getSlideNJumpMoves([x, y], V.steps[V.ROOK]).filter(m => { + // Remove captures. Alt: redefine canTake + return m.vanish.length == 1; + }); + + // Captures + const shiftX = -1; + for (let shiftY of [-1, 1]) { + if ( + V.OnBoard(x + shiftX, y + shiftY) && + this.board[x + shiftX][y + shiftY] != V.EMPTY && + this.canTake([x, y], [x + shiftX, y + shiftY]) + ) { + moves.push(this.getBasicMove([x, y], [x + shiftX, y + shiftY])); + } + } + + return moves; + } + + getPotentialKnightMoves(sq) { + // Knight becomes knightrider: + return this.getSlideNJumpMoves(sq, V.steps[V.KNIGHT]); + } + + // 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" + ); + } + + filterValid(moves) { + if (moves.length == 0) return []; + const color = this.turn; + const oppCol = V.GetOppCol(color); + return moves.filter(m => { + this.play(m); + // Giving check is forbidden as well: + const res = !this.underCheck(color) && !this.underCheck(oppCol); + this.undo(m); + return res; + }); + } + + isAttackedByPawn([x, y], colors) { + const pawnShift = 1; + if (x + pawnShift < V.size.x) { + for (let c of colors) { + 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; + } + + isAttackedByKnight(sq, colors) { + return this.isAttackedBySlideNJump( + sq, + colors, + V.KNIGHT, + V.steps[V.KNIGHT] + ); + } + + getCurrentScore() { + // Turn has changed: + const color = V.GetOppCol(this.turn); + if (this.kingPos[color][0] == 0) + // The opposing edge is reached! + return color == "w" ? "1-0" : "0-1"; + return "*"; + } + + static get SEARCH_DEPTH() { + return 2; + } + + static get VALUES() { + return { + p: 2, + r: 5, + n: 3, + b: 3, + q: 9, + k: 1000 + }; + } + + evalPosition() { + // Count material: + let evaluation = super.evalPosition(); + // Ponder with king position: + return evaluation/5 + this.kingPos["b"][0] - this.kingPos["w"][0]; + } + + getNotation(move) { + // Since pawns are much more mobile, treat them as other pieces: + return ( + move.vanish[0].p.toUpperCase() + + (move.vanish.length > move.appear.length ? "x" : "") + + V.CoordsToSquare(move.end) + ); + } +};