X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=client%2Fsrc%2Fvariants%2FCircular.js;h=d1f8230b1de6d9197e0b387e52aabf8be026b8a3;hb=c3ff3a0c807d97c0311a06491318fe02440266db;hp=a5a5d6448148086a7f15d9471174e2918ed2050b;hpb=e3e2cc443054cfb273b28b3ba46f559117c5ceae;p=vchess.git diff --git a/client/src/variants/Circular.js b/client/src/variants/Circular.js index a5a5d644..d1f8230b 100644 --- a/client/src/variants/Circular.js +++ b/client/src/variants/Circular.js @@ -1,9 +1,9 @@ import { ChessRules, PiPo, Move } from "@/base_rules"; import { ArrayFun } from "@/utils/array"; -import { randInt, shuffle } from "@/utils/alea"; +import { shuffle } from "@/utils/alea"; -export const VariantRules = class CircularRules extends ChessRules { - static get HasFlags() { +export class CircularRules extends ChessRules { + static get HasCastle() { return false; } @@ -11,185 +11,214 @@ export const VariantRules = class CircularRules extends ChessRules { return false; } - // TODO: CanFlip --> also for racing kings (answer is false) + static get CanFlip() { + return false; + } + + setFlags(fenflags) { + this.pawnFlags = { + w: [...Array(8).fill(true)], //pawns can move 2 squares? + b: [...Array(8).fill(true)] + }; + for (let c of ["w", "b"]) { + for (let i = 0; i < 8; i++) + this.pawnFlags[c][i] = fenflags.charAt((c == "w" ? 0 : 8) + i) == "1"; + } + } + + aggregateFlags() { + return this.pawnFlags; + } + + disaggregateFlags(flags) { + this.pawnFlags = flags; + } + + static GenRandInitFen(randomness) { + if (randomness == 0) { + return "8/8/pppppppp/rnbqkbnr/8/8/PPPPPPPP/RNBQKBNR " + + "w 0 1111111111111111"; + } - // 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"; + if (c == 'b' && randomness == 1) { + pieces['b'] = pieces['w']; + break; + } + + // Get random squares for every piece, totally freely + let positions = shuffle(ArrayFun.range(8)); + const composition = ['b', 'b', 'r', 'r', 'n', 'n', 'k', 'q']; + const rem2 = positions[0] % 2; + if (rem2 == positions[1] % 2) { + // Fix bishops (on different colors) + for (let i=2; i<8; i++) { + if (positions[i] % 2 != rem2) + [positions[1], positions[i]] = [positions[i], positions[1]]; + } + } + for (let i = 0; i < 8; i++) pieces[c][positions[i]] = composition[i]; } return ( + "8/8/pppppppp/" + pieces["b"].join("") + - "/pppppppp/8/8/8/8/PPPPPPPP/" + + "/8/8/PPPPPPPP/" + pieces["w"].join("").toUpperCase() + - " w 0" + // 16 flags: can pawns advance 2 squares? + " w 0 1111111111111111" ); } - // TODO: adapt this for a circular board + // Output basically x % 8 (circular board) + static ComputeX(x) { + let res = x % V.size.x; + if (res < 0) + res += V.size.x; + return res; + } + getSlideNJumpMoves([x, y], steps, oneStep) { let moves = []; + // Don't add move twice when running on an infinite file: + let infiniteSteps = {}; outerLoop: for (let step of steps) { - let i = x + step[0]; + if (!!infiniteSteps[(-step[0]) + "." + (-step[1])]) continue; + let i = V.ComputeX(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]; + i = V.ComputeX(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])); + if (V.OnBoard(i, j)) { + if (i == x && j == y) + // Looped back onto initial square + infiniteSteps[step[0] + "." + step[1]] = true; + else if (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])); - } + // All pawns go in the same direction! + const shiftX = -1; + const startRank = color == "w" ? sizeX - 2 : 2; + + // One square forward + const nextRow = V.ComputeX(x + shiftX); + if (this.board[nextRow][y] == V.EMPTY) { + moves.push(this.getBasicMove([x, y], [nextRow, y])); + if ( + x == startRank && + this.pawnFlags[color][y] && + 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 - }) - ); - } - } + } + // Captures + for (let shiftY of [-1, 1]) { + if ( + y + shiftY >= 0 && + y + shiftY < sizeY && + this.board[nextRow][y + shiftY] != V.EMPTY && + this.canTake([x, y], [nextRow, y + shiftY]) + ) { + moves.push(this.getBasicMove([x, y], [nextRow, y + shiftY])); } } 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" - ); + filterValid(moves) { + const filteredMoves = super.filterValid(moves); + // If at least one full move made, everything is allowed: + if (this.movesCount >= 2) return filteredMoves; + // Else, forbid check: + const oppCol = V.GetOppCol(this.turn); + return filteredMoves.filter(m => { + this.play(m); + const res = !this.underCheck(oppCol); + this.undo(m); + return res; + }); } - // 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; - } - } + isAttackedByPawn([x, y], color) { + // pawn shift is always 1 (all pawns go the same way) + const attackerRow = V.ComputeX(x + 1); + for (let i of [-1, 1]) { + if ( + y + i >= 0 && + y + i < V.size.y && + this.getPiece(attackerRow, y + i) == V.PAWN && + this.getColor(attackerRow, y + i) == color + ) { + return true; } } return false; } - // TODO: adapt this function - isAttackedBySlideNJump([x, y], colors, piece, steps, oneStep) { + isAttackedBySlideNJump([x, y], color, piece, steps, oneStep) { for (let step of steps) { - let rx = x + step[0], + let rx = V.ComputeX(x + step[0]), ry = y + step[1]; while (V.OnBoard(rx, ry) && this.board[rx][ry] == V.EMPTY && !oneStep) { - rx += step[0]; + rx = V.ComputeX(rx + step[0]); ry += step[1]; } if ( V.OnBoard(rx, ry) && - this.getPiece(rx, ry) === piece && - colors.includes(this.getColor(rx, ry)) + this.getPiece(rx, ry) == piece && + this.getColor(rx, ry) == color ) { return true; } } return false; } + + getFlagsFen() { + // Return pawns flags + let flags = ""; + for (let c of ["w", "b"]) { + for (let i = 0; i < 8; i++) flags += this.pawnFlags[c][i] ? "1" : "0"; + } + return flags; + } + + postPlay(move) { + super.postPlay(move); + const c = move.vanish[0].c; + const secondRank = { "w": 6, "b": 2 }; + if (move.vanish[0].p == V.PAWN && secondRank[c] == move.start.x) + // This move turns off a 2-squares pawn flag + this.pawnFlags[c][move.start.y] = false; + } + + static get VALUES() { + return { + p: 1, + r: 5, + n: 3, + b: 4, + q: 10, + k: 1000 + }; + } + + static get SEARCH_DEPTH() { + return 2; + } };