X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=base_rules.js;h=b98c7248d29a2bec84794f6cfd412e236cb30b92;hb=f3e90e30b6e7ff416afe288bc9dd865e5daf9860;hp=6a68f84dbef6f98b575f756f5b614c686ab79a2f;hpb=a5da62690c1314425a576302466b1d4ad406d80d;p=xogo.git diff --git a/base_rules.js b/base_rules.js index 6a68f84..b98c724 100644 --- a/base_rules.js +++ b/base_rules.js @@ -415,6 +415,8 @@ export default class ChessRules { // Graphical (can use variables defined above) this.containerId = o.element; + this.isDiagram = o.diagram; + this.marks = o.marks; this.graphicalInit(); } @@ -453,7 +455,7 @@ export default class ChessRules { this.setFlags(fenParsed.flags); if (this.hasEnpassant) this.epSquare = this.getEpSquare(fenParsed.enpassant); - if (this.hasReserve) + if (this.hasReserve && !this.isDiagram) this.initReserves(fenParsed.reserve); if (this.options["crazyhouse"]) this.initIspawn(fenParsed.ispawn); @@ -557,49 +559,52 @@ export default class ChessRules { // VISUAL METHODS graphicalInit() { - // NOTE: not window.onresize = this.re_drawBoardElts because scope (this) - window.onresize = () => this.re_drawBoardElements(); - this.re_drawBoardElements(); - this.initMouseEvents(); - const chessboard = - document.getElementById(this.containerId).querySelector(".chessboard"); + const g_init = () => { + this.re_drawBoardElements(); + if (!this.isDiagram && !this.mouseListeners && !this.touchListeners) + this.initMouseEvents(); + }; + let container = document.getElementById(this.containerId); + this.windowResizeObs = new ResizeObserver(g_init); + this.windowResizeObs.observe(container); } re_drawBoardElements() { const board = this.getSvgChessboard(); const oppCol = C.GetOppCol(this.playerColor); - let chessboard = - document.getElementById(this.containerId).querySelector(".chessboard"); + const container = document.getElementById(this.containerId); + const rc = container.getBoundingClientRect(); + let chessboard = container.querySelector(".chessboard"); chessboard.innerHTML = ""; chessboard.insertAdjacentHTML('beforeend', board); // Compare window ratio width / height to aspectRatio: - const windowRatio = window.innerWidth / window.innerHeight; + const windowRatio = rc.width / rc.height; let cbWidth, cbHeight; const vRatio = this.size.ratio || 1; if (windowRatio <= vRatio) { // Limiting dimension is width: - cbWidth = Math.min(window.innerWidth, 767); + cbWidth = Math.min(rc.width, 767); cbHeight = cbWidth / vRatio; } else { // Limiting dimension is height: - cbHeight = Math.min(window.innerHeight, 767); + cbHeight = Math.min(rc.height, 767); cbWidth = cbHeight * vRatio; } - if (this.hasReserve) { + if (this.hasReserve && !this.isDiagram) { const sqSize = cbWidth / this.size.y; // NOTE: allocate space for reserves (up/down) even if they are empty // Cannot use getReserveSquareSize() here, but sqSize is an upper bound. - if ((window.innerHeight - cbHeight) / 2 < sqSize + 5) { - cbHeight = window.innerHeight - 2 * (sqSize + 5); + if ((rc.height - cbHeight) / 2 < sqSize + 5) { + cbHeight = rc.height - 2 * (sqSize + 5); cbWidth = cbHeight * vRatio; } } chessboard.style.width = cbWidth + "px"; chessboard.style.height = cbHeight + "px"; // Center chessboard: - const spaceLeft = (window.innerWidth - cbWidth) / 2, - spaceTop = (window.innerHeight - cbHeight) / 2; + const spaceLeft = (rc.width - cbWidth) / 2, + spaceTop = (rc.height - cbHeight) / 2; chessboard.style.left = spaceLeft + "px"; chessboard.style.top = spaceTop + "px"; // Give sizes instead of recomputing them, @@ -643,46 +648,62 @@ export default class ChessRules { } setupPieces(r) { - if (this.g_pieces) { - // Refreshing: delete old pieces first - for (let i=0; i { + this[arrName][i][j] = document.createElement("piece"); + C.AddClass_es(this[arrName][i][j], classes); + this[arrName][i][j].style.width = pieceWidth + "px"; + this[arrName][i][j].style.height = pieceWidth + "px"; + let [ip, jp] = this.getPixelPosition(i, j, r); + // Translate coordinates to use chessboard as reference: + this[arrName][i][j].style.transform = + `translate(${ip - r.x}px,${jp - r.y}px)`; + chessboard.appendChild(this[arrName][i][j]); + }; + const conditionalReset = (arrName) => { + if (this[arrName]) { + // Refreshing: delete old pieces first. This isn't necessary, + // but simpler (this method isn't called many times) + for (let i=0; i addPiece(i, j, arrName, "mark")); + }; + if (this.marks) + conditionalReset("d_pieces"); + conditionalReset("g_pieces"); for (let i=0; i < this.size.x; i++) { for (let j=0; j < this.size.y; j++) { if (this.board[i][j] != "") { const color = this.getColor(i, j); const piece = this.getPiece(i, j); - this.g_pieces[i][j] = document.createElement("piece"); - C.AddClass_es(this.g_pieces[i][j], - this.pieces(color, i, j)[piece]["class"]); + addPiece(i, j, "g_pieces", this.pieces(color, i, j)[piece]["class"]); this.g_pieces[i][j].classList.add(C.GetColorClass(color)); - this.g_pieces[i][j].style.width = pieceWidth + "px"; - this.g_pieces[i][j].style.height = pieceWidth + "px"; - let [ip, jp] = this.getPixelPosition(i, j, r); - // Translate coordinates to use chessboard as reference: - this.g_pieces[i][j].style.transform = - `translate(${ip - r.x}px,${jp - r.y}px)`; if (this.enlightened && !this.enlightened[i][j]) this.g_pieces[i][j].classList.add("hidden"); - chessboard.appendChild(this.g_pieces[i][j]); + } + if (this.marks && this.d_pieces[i][j]) { + let classes = ["mark"]; + if (this.board[i][j] != "") + classes.push("transparent"); + addPiece(i, j, "d_pieces", classes); } } } - if (this.hasReserve) + if (this.hasReserve && !this.isDiagram) this.re_drawReserve(['w', 'b'], r); } @@ -771,26 +792,27 @@ export default class ChessRules { // Resize board: no need to destroy/recreate pieces rescale(mode) { - let chessboard = - document.getElementById(this.containerId).querySelector(".chessboard"); - const r = chessboard.getBoundingClientRect(); + const container = document.getElementById(this.containerId); + let chessboard = container.querySelector(".chessboard"); + const rc = container.getBoundingClientRect(), + r = chessboard.getBoundingClientRect(); const multFact = (mode == "up" ? 1.05 : 0.95); let [newWidth, newHeight] = [multFact * r.width, multFact * r.height]; // Stay in window: const vRatio = this.size.ratio || 1; - if (newWidth > window.innerWidth) { - newWidth = window.innerWidth; + if (newWidth > rc.width) { + newWidth = rc.width; newHeight = newWidth / vRatio; } - if (newHeight > window.innerHeight) { - newHeight = window.innerHeight; + if (newHeight > rc.height) { + newHeight = rc.height; newWidth = newHeight * vRatio; } chessboard.style.width = newWidth + "px"; chessboard.style.height = newHeight + "px"; - const newX = (window.innerWidth - newWidth) / 2; + const newX = (rc.width - newWidth) / 2; chessboard.style.left = newX + "px"; - const newY = (window.innerHeight - newHeight) / 2; + const newY = (rc.height - newHeight) / 2; chessboard.style.top = newY + "px"; const newR = {x: newX, y: newY, width: newWidth, height: newHeight}; const pieceWidth = this.getPieceWidth(newWidth); @@ -994,6 +1016,10 @@ export default class ChessRules { } 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); @@ -1485,7 +1511,7 @@ export default class ChessRules { if (piece == "p" && this.hasEnpassant && this.epSquare) Array.prototype.push.apply(moves, this.getEnpassantCaptures([x, y])); if ( - piece == "k" && this.hasCastle && + this.isKing(0, 0, piece) && this.hasCastle && this.castleFlags[color || this.turn].some(v => v < this.size.y) ) { Array.prototype.push.apply(moves, this.getCastleMoves([x, y])); @@ -2136,21 +2162,25 @@ export default class ChessRules { ); } - underCheck([x, y], oppCol) { + // Argument is (very generally) an array of squares (= arrays) + underCheck(square_s, oppCol) { if (this.options["taking"] || this.options["dark"]) return false; - return this.underAttack([x, y], oppCol); + if (!Array.isArray(square_s[0])) + square_s = [square_s]; + return square_s.some(sq => this.underAttack(sq, oppCol)); } - // Stop at first king found (TODO: multi-kings) + // Scan board for king(s) searchKingPos(color) { + let res = []; for (let i=0; i < this.size.x; i++) { for (let j=0; j < this.size.y; j++) { if (this.getColor(i, j) == color && this.isKing(i, j)) - return [i, j]; + res.push([i, j]); } } - return [-1, -1]; //king not found + return res; } // 'color' arg because some variants (e.g. Refusal) check opponent moves @@ -2158,24 +2188,32 @@ export default class ChessRules { if (!color) color = this.turn; const oppCol = C.GetOppCol(color); - const kingPos = this.searchKingPos(color); + let kingPos = this.searchKingPos(color); let filtered = {}; //avoid re-checking similar moves (promotions...) return moves.filter(m => { const key = m.start.x + m.start.y + '.' + m.end.x + m.end.y; if (!filtered[key]) { this.playOnBoard(m); - let square = kingPos, + let newKingPP = null, + sqIdx = 0, res = true; //a priori valid - if (m.vanish.some(v => this.isKing(0, 0, v.p) && v.c == color)) { + const oldKingPP = + m.vanish.find(v => this.isKing(0, 0, v.p) && v.c == color); + if (oldKingPP) { // Search king in appear array: - const newKingIdx = - m.appear.findIndex(a => this.isKing(0, 0, a.p) && a.c == color); - if (newKingIdx >= 0) - square = [m.appear[newKingIdx].x, m.appear[newKingIdx].y]; + newKingPP = + m.appear.find(a => this.isKing(0, 0, a.p) && a.c == color); + if (newKingPP) { + sqIdx = kingPos.findIndex(kp => + kp[0] == oldKingPP.x && kp[1] == oldKingPP.y); + kingPos[sqIdx] = [newKingPP.x, newKingPP.y]; + } else - res = false; + res = false; //king vanished } - res &&= !this.underCheck(square, oppCol); + res &&= !this.underCheck(kingPos, oppCol); + if (oldKingPP && newKingPP) + kingPos[sqIdx] = [oldKingPP.x, oldKingPP.y]; this.undoOnBoard(m); filtered[key] = res; return res; @@ -2322,7 +2360,7 @@ export default class ChessRules { return false; const color = this.turn; const oppKingPos = this.searchKingPos(C.GetOppCol(color)); - if (oppKingPos[0] < 0 || this.underCheck(oppKingPos, color)) + if (oppKingPos.length == 0 || this.underCheck(oppKingPos, color)) return true; return ( ( @@ -2371,17 +2409,20 @@ export default class ChessRules { getCurrentScore(move) { const color = this.turn; const oppCol = C.GetOppCol(color); - const kingPos = [this.searchKingPos(color), this.searchKingPos(oppCol)]; - if (kingPos[0][0] < 0 && kingPos[1][0] < 0) + const kingPos = { + [color]: this.searchKingPos(color), + [oppCol]: this.searchKingPos(oppCol) + }; + if (kingPos[color].length == 0 && kingPos[oppCol].length == 0) return "1/2"; - if (kingPos[0][0] < 0) + if (kingPos[color].length == 0) return (color == "w" ? "0-1" : "1-0"); - if (kingPos[1][0] < 0) + if (kingPos[oppCol].length == 0) return (color == "w" ? "1-0" : "0-1"); if (this.atLeastOneMove(color)) return "*"; // No valid move: stalemate or checkmate? - if (!this.underCheck(kingPos[0], oppCol)) + if (!this.underCheck(kingPos[color], oppCol)) return "1/2"; // OK, checkmate return (color == "w" ? "0-1" : "1-0");