X-Git-Url: https://git.auder.net/?p=xogo.git;a=blobdiff_plain;f=base_rules.js;h=88197ea0bed834c970659b95068aa5bec6f0b1aa;hp=98c6bd412c776a0275a1714715e9283c24764a1e;hb=HEAD;hpb=65203419968be4b63acf55172fe4b28658b96f38 diff --git a/base_rules.js b/base_rules.js index 98c6bd4..3d8e463 100644 --- a/base_rules.js +++ b/base_rules.js @@ -97,6 +97,14 @@ export default class ChessRules { return true; } + // Allow to take (moving: not disappearing) own pieces? + get hasSelfCaptures() { + return ( + this.options["recycle"] || + (this.options["teleport"] && this.subTurnTeleport == 1) + ); + } + get hasReserve() { return ( !!this.options["crazyhouse"] || @@ -167,6 +175,22 @@ export default class ChessRules { return Object.values(cd).map(c => c.toString(36)).join(""); } + // c10 --> 02 (assuming 10 rows) + static SquareFromUsual(sq) { + return ( + (this.size.x - parseInt(sq.substring(1), 10)).toString(36) + + (sq.charCodeAt(0) - 97).toString(36) + ); + } + + // 02 --> c10 + static UsualFromSquare(sq) { + return ( + String.fromCharCode(parseInt(sq.charAt(1), 36) + 97) + + (this.size.x - parseInt(sq.charAt(0), 36)).toString(10) + ); + } + coordsToId(cd) { if (typeof cd.x == "number") { return ( @@ -225,7 +249,7 @@ export default class ChessRules { ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'], { randomness: this.options["randomness"], - between: {p1: 'k', p2: 'r'}, + between: [{p1: 'k', p2: 'r'}], diffCol: ['b'], flags: ['r'] } @@ -316,7 +340,7 @@ export default class ChessRules { // Flags part of the FEN string getFlagsFen() { - return ["w", "b"].map(c => { + return ['w', 'b'].map(c => { return this.castleFlags[c].map(x => x.toString(36)).join(""); }).join(""); } @@ -330,9 +354,9 @@ export default class ChessRules { getReserveFen(o) { if (o.init) - return "000000000000"; + return Array(2 * V.ReserveArray.length).fill('0').join(""); return ( - ["w","b"].map(c => Object.values(this.reserve[c]).join("")).join("") + ['w', 'b'].map(c => Object.values(this.reserve[c]).join("")).join("") ); } @@ -433,14 +457,16 @@ export default class ChessRules { } // ordering as in pieces() p,r,n,b,q,k - initReserves(reserveStr, pieceArray) { - if (!pieceArray) - pieceArray = ['p', 'r', 'n', 'b', 'q', 'k']; + static get ReserveArray() { + return ['p', 'r', 'n', 'b', 'q', 'k']; + } + + initReserves(reserveStr) { const counts = reserveStr.split("").map(c => parseInt(c, 36)); - const L = pieceArray.length; + const L = V.ReserveArray.length; this.reserve = { - w: ArrayFun.toObject(pieceArray, counts.slice(0, L)), - b: ArrayFun.toObject(pieceArray, counts.slice(L, 2 * L)) + w: ArrayFun.toObject(V.ReserveArray, counts.slice(0, L)), + b: ArrayFun.toObject(V.ReserveArray, counts.slice(L, 2 * L)) }; } @@ -455,7 +481,7 @@ export default class ChessRules { // VISUAL UTILS getPieceWidth(rwidth) { - return (rwidth / this.size.y); + return (rwidth / Math.max(this.size.x, this.size.y)); } getReserveSquareSize(rwidth, nbR) { @@ -571,11 +597,18 @@ export default class ChessRules { // Get SVG board (background, no pieces) getSvgChessboard() { - const flipped = this.flippedBoard; let board = ` `; + board += this.getBaseSvgChessboard(); + board += ""; + return board; + } + + getBaseSvgChessboard() { + let board = ""; + const flipped = this.flippedBoard; for (let i=0; i < this.size.x; i++) { for (let j=0; j < this.size.y; j++) { if (!this.onBoard(i, j)) @@ -597,7 +630,6 @@ export default class ChessRules { />`; } } - board += ""; return board; } @@ -634,7 +666,11 @@ export default class ChessRules { else this[arrName] = ArrayFun.init(this.size.x, this.size.y, null); if (arrName == "d_pieces") - this.marks.forEach(([i, j]) => addPiece(i, j, arrName, "mark")); + this.marks.forEach((m) => { + const formattedSquare = C.SquareFromUsual(m); + const mCoords = C.SquareToCoords(formattedSquare); + addPiece(mCoords.x, mCoords.y, arrName, "mark"); + }); }; if (this.marks) conditionalReset("d_pieces"); @@ -834,9 +870,10 @@ export default class ChessRules { y = (this.playerColor == i ? y = r.height + 5 : - 5 - rsqSize); } else { - const sqSize = r.width / this.size.y; + const sqSize = r.width / Math.max(this.size.x, this.size.y); const flipped = this.flippedBoard; - x = (flipped ? this.size.y - 1 - j : j) * sqSize; + x = (flipped ? this.size.y - 1 - j : j) * sqSize + + Math.abs(this.size.x - this.size.y) * sqSize / 2; y = (flipped ? this.size.x - 1 - i : i) * sqSize; } return [r.x + x, r.y + y]; @@ -1188,7 +1225,7 @@ export default class ChessRules { } pieces(color, x, y) { - const pawnShift = this.getPawnShift(color); + const pawnShift = this.getPawnShift(color || 'w'); return { 'p': { "class": "pawn", @@ -1413,7 +1450,6 @@ export default class ChessRules { { attackOnly: true, one: true, - segments: this.options["cylinder"] }, allowed ) @@ -1424,10 +1460,7 @@ export default class ChessRules { this.options["zen"] && this.findCapturesOn( [i, j], - { - one: true, - segments: this.options["cylinder"] - }, + {one: true}, allowed ) ) @@ -1516,12 +1549,8 @@ export default class ChessRules { let moves = this.getPotentialMovesOf(piece, [x, y]); if (piece == "p" && this.hasEnpassant && this.epSquare) Array.prototype.push.apply(moves, this.getEnpassantCaptures([x, y])); - if ( - this.isKing(0, 0, piece) && this.hasCastle && - this.castleFlags[color || this.turn].some(v => v < this.size.y) - ) { + if (this.isKing(0, 0, piece) && this.hasCastle) Array.prototype.push.apply(moves, this.getCastleMoves([x, y])); - } return this.postProcessPotentialMoves(moves); } @@ -1692,7 +1721,6 @@ export default class ChessRules { [x, y], { attackOnly: true, - segments: this.options["cylinder"], stepSpec: stepSpec }, ([i1, j1], [i2, j2]) => { @@ -1707,7 +1735,6 @@ export default class ChessRules { [x, y], { moveOnly: !!stepSpec.attack || this.options["zen"], - segments: this.options["cylinder"], stepSpec: stepSpec } ); @@ -1720,22 +1747,17 @@ export default class ChessRules { !this.isKing(i1, j1) && this.canTake([i2, j2], [i1, j1]) ); // Technical step: segments (if any) are reversed - if (this.options["cylinder"]) { - zenCaptures.forEach(z => { + zenCaptures.forEach(z => { + if (!!z.segments) z.segments = z.segments.reverse().map(s => s.reverse()) - }); - } + }); Array.prototype.push.apply(squares, zenCaptures); } - if ( - this.options["recycle"] || - (this.options["teleport"] && this.subTurnTeleport == 1) - ) { + if (this.hasSelfCaptures) { const selfCaptures = this.findDestSquares( [x, y], { attackOnly: true, - segments: this.options["cylinder"], stepSpec: stepSpec }, ([i1, j1], [i2, j2]) => { @@ -1749,7 +1771,7 @@ export default class ChessRules { } return squares.map(s => { let mv = this.getBasicMove([x, y], s.sq); - if (this.options["cylinder"] && !!s.segments && s.segments.length >= 2) + if (!!s.segments) mv.segments = s.segments; return mv; }); @@ -1760,23 +1782,21 @@ export default class ChessRules { allowed = (sq1, sq2) => this.canTake(sq1, sq2); const apparentPiece = this.getPiece(x, y); //how it looks let res = []; - // Next 3 for Cylinder mode: (unused if !o.segments) + // Next 3 for Cylinder mode or circular (useless otherwise) let explored = {}; let segments = []; let segStart = []; const addSquare = ([i, j]) => { let elt = {sq: [i, j]}; - if (o.segments) + if (segments.length >= 1) elt.segments = this.getSegments(segments, segStart, [i, j]); res.push(elt); }; const exploreSteps = (stepArray, mode) => { for (let s of stepArray) { outerLoop: for (let step of s.steps) { - if (o.segments) { - segments = []; - segStart = [x, y]; - } + segments = []; + segStart = [x, y]; let [i, j] = [x, y]; let stepCounter = 0; while ( @@ -1801,8 +1821,11 @@ export default class ChessRules { continue outerLoop; const oldIJ = [i, j]; [i, j] = this.increment([i, j], step); - if (o.segments && Math.abs(j - oldIJ[1]) > 1) { - // Boundary between segments (cylinder mode) + if ( + Math.abs(i - oldIJ[0]) != Math.abs(step[0]) || + Math.abs(j - oldIJ[1]) != Math.abs(step[1]) + ) { + // Boundary between segments (cylinder or circular mode) segments.push([[segStart[0], segStart[1]], oldIJ]); segStart = [i, j]; } @@ -1873,7 +1896,6 @@ export default class ChessRules { { captureTarget: [x, y], captureSteps: [{steps: [s], range: a.range}], - segments: o.segments }, allowed ); @@ -2021,8 +2043,9 @@ export default class ChessRules { return []; } - getCastleMoves([x, y], finalSquares, castleWith) { + getCastleMoves([x, y], finalSquares, castleWith, castleFlags) { const c = this.getColor(x, y); + castleFlags = castleFlags || this.castleFlags[c]; // Castling ? const oppCols = this.getOppCols(c); @@ -2036,12 +2059,12 @@ export default class ChessRules { castleSide < 2; castleSide++ //large, then small ) { - if (this.castleFlags[c][castleSide] >= this.size.y) + if (castleFlags[castleSide] >= this.size.y) continue; // If this code is reached, rook and king are on initial position // NOTE: in some variants this is not a rook - const rookPos = this.castleFlags[c][castleSide]; + const rookPos = castleFlags[castleSide]; const castlingPiece = this.getPiece(x, rookPos); if ( this.board[x][rookPos] == "" || @@ -2143,7 +2166,6 @@ export default class ChessRules { [x, y], { byCol: oppCols, - segments: this.options["cylinder"], one: true } ) @@ -2155,7 +2177,6 @@ export default class ChessRules { [x, y], { attackOnly: true, - segments: this.options["cylinder"], one: true }, ([i1, j1], [i2, j2]) => oppCols.includes(this.getColor(i2, j2)) @@ -2246,11 +2267,20 @@ export default class ChessRules { this.board[psq.x][psq.y] = psq.c + psq.p; } - updateCastleFlags(move) { + // NOTE: arg "castleFlags" for Coregal or Twokings + updateCastleFlags(move, castleFlags, king) { + castleFlags = castleFlags || this.castleFlags; + // If flags already off, no need to re-check: + if ( + Object.values(castleFlags).every(cvals => + cvals.every(val => val >= this.size.y)) + ) { + return; + } // Update castling flags if start or arrive from/at rook/king locations move.appear.concat(move.vanish).forEach(psq => { - if (this.isKing(0, 0, psq.p)) - this.castleFlags[psq.c] = [this.size.y, this.size.y]; + if ((king && psq.p == king) || (!king && this.isKing(0, 0, psq.p))) + castleFlags[psq.c] = [this.size.y, this.size.y]; // NOTE: not "else if" because king can capture enemy rook... let c = ""; if (psq.x == 0) @@ -2258,28 +2288,17 @@ export default class ChessRules { else if (psq.x == this.size.x - 1) c = "w"; if (c != "") { - const fidx = this.castleFlags[c].findIndex(f => f == psq.y); + const fidx = castleFlags[c].findIndex(f => f == psq.y); if (fidx >= 0) - this.castleFlags[c][fidx] = this.size.y; + castleFlags[c][fidx] = this.size.y; } }); } prePlay(move) { - if ( - this.hasCastle && - // If flags already off, no need to re-check: - Object.values(this.castleFlags).some(cvals => - cvals.some(val => val < this.size.y)) - ) { + if (this.hasCastle) this.updateCastleFlags(move); - } if (this.options["crazyhouse"]) { - move.vanish.forEach(v => { - const square = C.CoordsToSquare({x: v.x, y: v.y}); - if (this.ispawn[square]) - delete this.ispawn[square]; - }); if (move.appear.length > 0 && move.vanish.length > 0) { // Assumption: something is moving const initSquare = C.CoordsToSquare(move.start); @@ -2298,6 +2317,11 @@ export default class ChessRules { delete this.ispawn[destSquare]; } } + move.vanish.forEach(v => { + const square = C.CoordsToSquare({x: v.x, y: v.y}); + if (this.ispawn[square]) + delete this.ispawn[square]; + }); } const minSize = Math.min(move.appear.length, move.vanish.length); if ( @@ -2340,10 +2364,11 @@ export default class ChessRules { if (this.options["teleport"]) { if ( this.subTurnTeleport == 1 && - move.vanish.length > move.appear.length && + move.vanish.length == 2 && + move.appear.length == 1 && move.vanish[1].c == this.turn ) { - const v = move.vanish[move.vanish.length - 1]; + const v = move.vanish[1]; this.captured = {x: v.x, y: v.y, c: v.c, p: v.p}; this.subTurnTeleport = 2; return;