X-Git-Url: https://git.auder.net/assets/rpsls.css?a=blobdiff_plain;f=base_rules.js;h=f9913c44bea0be04c6ba049fed65c97a18053492;hb=55d3b31f34f6b29f089db5723caf552ebf091905;hp=8eed3d35250e521b0a6cb78d4a85d67222c8341d;hpb=d6d0a46e5c8c1d9176f4a9e9c44a4b5f2ed791e7;p=xogo.git diff --git a/base_rules.js b/base_rules.js index 8eed3d3..f9913c4 100644 --- a/base_rules.js +++ b/base_rules.js @@ -1,5 +1,6 @@ import {Random} from "/utils/alea.js"; import {ArrayFun} from "/utils/array.js"; +import {FenUtil} from "/utils/setupPieces.js"; import PiPo from "/utils/PiPo.js"; import Move from "/utils/Move.js"; @@ -208,74 +209,27 @@ export default class ChessRules { baseFen.o = Object.assign({init: true}, baseFen.o); const parts = this.getPartFen(baseFen.o); return ( - baseFen.fen + + baseFen.fen + " w 0" + (Object.keys(parts).length > 0 ? (" " + JSON.stringify(parts)) : "") ); } // Setup the initial random-or-not (asymmetric-or-not) position genRandInitBaseFen() { - let fen, flags = "0707"; - if (!this.options.randomness) - // Deterministic: - fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w 0"; - - else { - // Randomize - let pieces = {w: new Array(8), b: new Array(8)}; - flags = ""; - // Shuffle pieces on first (and last rank if randomness == 2) - for (let c of ["w", "b"]) { - if (c == 'b' && this.options.randomness == 1) { - pieces['b'] = pieces['w']; - flags += flags; - break; - } - let positions = ArrayFun.range(8); - // Get random squares for bishops - let randIndex = 2 * Random.randInt(4); - const bishop1Pos = positions[randIndex]; - // The second bishop must be on a square of different color - let randIndex_tmp = 2 * Random.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 = Random.randInt(6); - const knight1Pos = positions[randIndex]; - positions.splice(randIndex, 1); - randIndex = Random.randInt(5); - const knight2Pos = positions[randIndex]; - positions.splice(randIndex, 1); - // Get random square for queen - randIndex = Random.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"; - flags += rook1Pos.toString() + rook2Pos.toString(); + const s = FenUtil.setupPieces( + ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'], + { + randomness: this.options["randomness"], + between: {p1: 'k', p2: 'r'}, + diffCol: ['b'], + flags: ['r'] } - fen = ( - pieces["b"].join("") + - "/pppppppp/8/8/8/8/PPPPPPPP/" + - pieces["w"].join("").toUpperCase() + - " w 0" - ); - } - return { fen: fen, o: {flags: flags} }; + ); + return { + fen: s.b.join("") + "/pppppppp/8/8/8/8/PPPPPPPP/" + + s.w.join("").toUpperCase(), + o: {flags: s.flags} + }; } // "Parse" FEN: just return untransformed string data @@ -332,9 +286,9 @@ export default class ChessRules { // Position part of the FEN string getPosition() { let position = ""; - for (let i = 0; i < this.size.y; i++) { + for (let i = 0; i < this.size.x; i++) { let emptyCount = 0; - for (let j = 0; j < this.size.x; j++) { + for (let j = 0; j < this.size.y; j++) { if (this.board[i][j] == "") emptyCount++; else { @@ -349,7 +303,7 @@ export default class ChessRules { if (emptyCount > 0) // "Flush remainder" position += C.FenEmptySquares(emptyCount); - if (i < this.size.y - 1) + if (i < this.size.x - 1) position += "/"; //separate rows } return position; @@ -405,9 +359,6 @@ export default class ChessRules { if (this.options[opt.variable] === undefined) this.options[opt.variable] = opt.defaut; }); - if (o.genFenOnly) - // This object will be used only for initial FEN generation - return; // Some variables this.playerColor = o.color; @@ -477,17 +428,15 @@ export default class ChessRules { } // ordering as in pieces() p,r,n,b,q,k - initReserves(reserveStr) { + initReserves(reserveStr, pieceArray) { + if (!pieceArray) + pieceArray = ['p', 'r', 'n', 'b', 'q', 'k']; const counts = reserveStr.split("").map(c => parseInt(c, 36)); - this.reserve = { w: {}, b: {} }; - const pieceName = ['p', 'r', 'n', 'b', 'q', 'k']; - const L = pieceName.length; - for (let i of ArrayFun.range(2 * L)) { - if (i < L) - this.reserve['w'][pieceName[i]] = counts[i]; - else - this.reserve['b'][pieceName[i-L]] = counts[i]; - } + const L = pieceArray.length; + this.reserve = { + w: ArrayFun.toObject(pieceArray, counts.slice(0, L)), + b: ArrayFun.toObject(pieceArray, counts.slice(L, 2 * L)) + }; } initIspawn(ispawnStr) { @@ -608,7 +557,7 @@ export default class ChessRules { chessboard.style.top = spaceTop + "px"; // Give sizes instead of recomputing them, // because chessboard might not be drawn yet. - this.setupPieces({ + this.setupVisualPieces({ width: cbWidth, height: cbHeight, x: spaceLeft, @@ -648,7 +597,7 @@ export default class ChessRules { return board; } - setupPieces(r) { + setupVisualPieces(r) { let chessboard = document.getElementById(this.containerId).querySelector(".chessboard"); if (!r) @@ -782,8 +731,8 @@ export default class ChessRules { piece = "k"; //capturing cannibal king: back to king form const oldCount = this.reserve[color][piece]; this.reserve[color][piece] = count; - // Redrawing is much easier if count==0 - if ([oldCount, count].includes(0)) + // Redrawing is much easier if count==0 (or undefined) + if ([oldCount, count].some(item => !item)) this.re_drawReserve([color]); else { const numId = this.getReserveNumId(color, piece); @@ -1016,11 +965,10 @@ export default class ChessRules { // TODO: onpointerdown/move/up ? See reveal.js /controllers/touch.js } + // NOTE: not called if isDiagram removeListeners() { let container = document.getElementById(this.containerId); this.windowResizeObs.unobserve(container); - if (this.isDiagram) - return; //no listeners in this case if ('onmousedown' in window) { this.mouseListeners.forEach(ml => { document.removeEventListener(ml.type, ml.listener); @@ -1245,13 +1193,13 @@ export default class ChessRules { }, 'r': { "class": "rook", - moves: [ + both: [ {steps: [[0, 1], [0, -1], [1, 0], [-1, 0]]} ] }, 'n': { "class": "knight", - moves: [ + both: [ { steps: [ [1, 2], [1, -2], [-1, 2], [-1, -2], @@ -1263,13 +1211,13 @@ export default class ChessRules { }, 'b': { "class": "bishop", - moves: [ + both: [ {steps: [[1, 1], [1, -1], [-1, 1], [-1, -1]]} ] }, 'q': { "class": "queen", - moves: [ + both: [ { steps: [ [0, 1], [0, -1], [1, 0], [-1, 0], @@ -1280,7 +1228,7 @@ export default class ChessRules { }, 'k': { "class": "king", - moves: [ + both: [ { steps: [ [0, 1], [0, -1], [1, 0], [-1, 0], @@ -1344,7 +1292,20 @@ export default class ChessRules { } getStepSpec(color, x, y, piece) { - return this.pieces(color, x, y)[piece || this.getPieceType(x, y)]; + let pieceType = piece; + let allSpecs = this.pieces(color, x, y); + if (!piece) + pieceType = this.getPieceType(x, y); + else if (allSpecs[piece].moveas) + pieceType = allSpecs[piece].moveas; + let res = allSpecs[pieceType]; + if (!res["both"]) + res.both = []; + if (!res["moves"]) + res.moves = []; + if (!res["attack"]) + res.attack = []; + return res; } // Can thing on square1 capture thing on square2? @@ -1378,7 +1339,7 @@ export default class ChessRules { const oppCol = C.GetOppCol(color); const piece = this.getPieceType(x, y); const stepSpec = this.getStepSpec(color, x, y, piece); - const attacks = stepSpec.attack || stepSpec.moves; + const attacks = stepSpec.both.concat(stepSpec.attack); for (let a of attacks) { outerLoop: for (let step of a.steps) { let [i, j] = [x + step[0], y + step[1]]; @@ -1777,7 +1738,7 @@ export default class ChessRules { elt.segments = this.getSegments(segments, segStart, end); res.push(elt); }; - const exploreSteps = (stepArray) => { + const exploreSteps = (stepArray, mode) => { for (let s of stepArray) { outerLoop: for (let step of s.steps) { if (o.segments) { @@ -1796,9 +1757,9 @@ export default class ChessRules { !o.captureTarget || (o.captureTarget[0] == i && o.captureTarget[1] == j) ) { - if (o.one && !o.attackOnly) + if (o.one && mode != "attack") return true; - if (!o.attackOnly) + if (mode != "attack") addSquare(!o.captureTarget ? [i, j] : [x, y]); if (o.captureTarget) return res[0]; @@ -1821,9 +1782,9 @@ export default class ChessRules { if (!explored[i + "." + j]) { explored[i + "." + j] = true; if (allowed([x, y], [i, j])) { - if (o.one && !o.moveOnly) + if (o.one && mode != "moves") return true; - if (!o.moveOnly) + if (mode != "moves") addSquare(!o.captureTarget ? [i, j] : [x, y]); if ( o.captureTarget && @@ -1838,17 +1799,15 @@ export default class ChessRules { return undefined; //default, but let's explicit it }; if (o.captureTarget) - return exploreSteps(o.captureSteps) + return exploreSteps(o.captureSteps, "attack"); else { const stepSpec = o.stepSpec || this.getStepSpec(this.getColor(x, y), x, y); let outOne = false; - if (!o.attackOnly || !stepSpec.attack) - outOne = exploreSteps(stepSpec.moves); - if (!outOne && !o.moveOnly && !!stepSpec.attack) { - o.attackOnly = true; //ok because o is always a temporary object - outOne = exploreSteps(stepSpec.attack); - } + if (!o.attackOnly) + outOne = exploreSteps(stepSpec.both.concat(stepSpec.moves), "moves"); + if (!outOne && !o.moveOnly) + outOne = exploreSteps(stepSpec.both.concat(stepSpec.attack), "attack"); return (o.one ? outOne : res); } } @@ -1871,7 +1830,7 @@ export default class ChessRules { if (this.canStepOver(x, y, apparentPiece)) continue; const stepSpec = this.getStepSpec(colIJ, i, j); - const attacks = stepSpec.attack || stepSpec.moves; + const attacks = stepSpec.attack.concat(stepSpec.both); for (let a of attacks) { for (let s of a.steps) { // Quick check: if step isn't compatible, don't even try @@ -2073,7 +2032,7 @@ export default class ChessRules { // will be executed in filterValid() later. ( i != finalSquares[castleSide][0] && - this.underCheck([x, i], oppCol) + this.underCheck([[x, i]], oppCol) ) || ( @@ -2180,8 +2139,6 @@ export default class ChessRules { underCheck(square_s, oppCol) { if (this.options["taking"] || this.options["dark"]) return false; - if (!Array.isArray(square_s[0])) - square_s = [square_s]; return square_s.some(sq => this.underAttack(sq, oppCol)); } @@ -2343,14 +2300,13 @@ export default class ChessRules { } postPlay(move) { - const color = this.turn; if (this.options["dark"]) this.updateEnlightened(); if (this.options["teleport"]) { if ( this.subTurnTeleport == 1 && move.vanish.length > move.appear.length && - move.vanish[1].c == color + move.vanish[1].c == this.turn ) { const v = move.vanish[move.vanish.length - 1]; this.captured = {x: v.x, y: v.y, c: v.c, p: v.p}; @@ -2360,8 +2316,12 @@ export default class ChessRules { this.subTurnTeleport = 1; this.captured = null; } + this.tryChangeTurn(move); + } + + tryChangeTurn(move) { if (this.isLastMove(move)) { - this.turn = C.GetOppCol(color); + this.turn = C.GetOppCol(this.turn); this.movesCount++; this.subTurn = 1; } @@ -2479,32 +2439,37 @@ export default class ChessRules { buildMoveStack(move, r) { this.moveStack.push(move); this.computeNextMove(move); - this.play(move); - const newTurn = this.turn; - if (this.moveStack.length == 1 && !this.hideMoves) - this.playVisual(move, r); - if (move.next) { - this.gameState = { - fen: this.getFen(), - board: JSON.parse(JSON.stringify(this.board)) //easier - }; - this.buildMoveStack(move.next, r); - } - else { - if (this.moveStack.length == 1) { - // Usual case (one normal move) - this.afterPlay(this.moveStack, newTurn, {send: true, res: true}); - this.moveStack = [] + const then = () => { + const newTurn = this.turn; + if (this.moveStack.length == 1 && !this.hideMoves) + this.playVisual(move, r); + if (move.next) { + this.gameState = { + fen: this.getFen(), + board: JSON.parse(JSON.stringify(this.board)) //easier + }; + this.buildMoveStack(move.next, r); } else { - this.afterPlay(this.moveStack, newTurn, {send: true, res: false}); - this.re_initFromFen(this.gameState.fen, this.gameState.board); - this.playReceivedMove(this.moveStack.slice(1), () => { - this.afterPlay(this.moveStack, newTurn, {send: false, res: true}); - this.moveStack = [] - }); + if (this.moveStack.length == 1) { + // Usual case (one normal move) + this.afterPlay(this.moveStack, newTurn, {send: true, res: true}); + this.moveStack = []; + } + else { + this.afterPlay(this.moveStack, newTurn, {send: true, res: false}); + this.re_initFromFen(this.gameState.fen, this.gameState.board); + this.playReceivedMove(this.moveStack.slice(1), () => { + this.afterPlay(this.moveStack, newTurn, {send: false, res: true}); + this.moveStack = []; + }); + } } - } + }; + // If hiding moves, then they are revealed in play() with callback + this.play(move, this.hideMoves ? then : null); + if (!this.hideMoves) + then(); } // Implemented in variants using (automatic) moveStack @@ -2635,8 +2600,9 @@ export default class ChessRules { launchAnimation(moves, container, callback) { if (this.hideMoves) { - moves.forEach(m => this.play(m)); - callback(); + for (let i=0; i {}); return; } const r = container.querySelector(".chessboard").getBoundingClientRect(); @@ -2675,6 +2641,7 @@ export default class ChessRules { let container = document.getElementById(this.containerId); if (document.hidden) { document.onvisibilitychange = () => { + // TODO here: page reload ?! (some issues if tab changed...) document.onvisibilitychange = undefined; checkDisplayThenAnimate(700); };