X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=base_rules.js;h=7191c563b1a416903d4ccc1acefd61f06bd698ec;hb=adf7c6595b5e41e7ecbb3d40cb33b21996e6b16c;hp=4805f174b55773fc5e5a3e79dc9792d8a8b6dfaf;hpb=0c44c6768654f7127e36aa75beee58d61df4fc64;p=xogo.git diff --git a/base_rules.js b/base_rules.js index 4805f17..7191c56 100644 --- a/base_rules.js +++ b/base_rules.js @@ -455,7 +455,7 @@ export default class ChessRules { const steps = this.pieces(this.playerColor)["p"].attack[0].steps; for (let step of steps) { const x = this.epSquare.x - step[0], - y = this.computeY(this.epSquare.y - step[1]); + y = this.getY(this.epSquare.y - step[1]); if ( this.onBoard(x, y) && this.getColor(x, y) == this.playerColor && @@ -638,8 +638,10 @@ export default class ChessRules { 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"; - const [ip, jp] = this.getPixelPosition(i, j, r); - this.g_pieces[i][j].style.transform = `translate(${ip}px,${jp}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]); @@ -671,11 +673,10 @@ export default class ChessRules { } } else - this.r_pieces = { 'w': {}, 'b': {} }; - let chessboard = - document.getElementById(this.containerId).querySelector(".chessboard"); + this.r_pieces = { w: {}, b: {} }; + let container = document.getElementById(this.containerId); if (!r) - r = chessboard.getBoundingClientRect(); + r = container.querySelector(".chessboard").getBoundingClientRect(); for (let c of colors) { if (!this.reserve[c]) continue; @@ -694,7 +695,7 @@ export default class ChessRules { // NOTE: +1 fix display bug on Firefox at least rcontainer.style.width = (nbR * sqResSize + 1) + "px"; rcontainer.style.height = sqResSize + "px"; - chessboard.appendChild(rcontainer); + container.appendChild(rcontainer); for (let p of Object.keys(this.reserve[c])) { if (this.reserve[c][p] == 0) continue; @@ -748,13 +749,13 @@ export default class ChessRules { for (let x=0; x y). // We return here the CSS coordinates (more useful). getPixelPosition(i, j, r) { @@ -850,12 +853,12 @@ export default class ChessRules { x = (flipped ? this.size.y - 1 - j : j) * sqSize; y = (flipped ? this.size.x - 1 - i : i) * sqSize; } - return [x, y]; + return [r.x + x, r.y + y]; } initMouseEvents() { - let chessboard = - document.getElementById(this.containerId).querySelector(".chessboard"); + let container = document.getElementById(this.containerId); + let chessboard = container.querySelector(".chessboard"); const getOffset = e => { if (e.clientX) @@ -876,8 +879,8 @@ export default class ChessRules { const centerOnCursor = (piece, e) => { const centerShift = this.getPieceWidth(r.width) / 2; const offset = getOffset(e); - piece.style.left = (offset.x - r.x - centerShift) + "px"; - piece.style.top = (offset.y - r.y - centerShift) + "px"; + piece.style.left = (offset.x - centerShift) + "px"; + piece.style.top = (offset.y - centerShift) + "px"; } let start = null, @@ -910,7 +913,7 @@ export default class ChessRules { curPiece.style.width = pieceWidth + "px"; curPiece.style.height = pieceWidth + "px"; centerOnCursor(curPiece, e); - chessboard.appendChild(curPiece); + container.appendChild(curPiece); startPiece.style.opacity = "0.4"; chessboard.style.cursor = "none"; } @@ -1043,7 +1046,7 @@ export default class ChessRules { // Piece type on square (i,j) getPieceType(i, j) { - const p = (typeof i == "string" ? j : this.board[i][j].charAt(1)); + const p = this.getPiece(i, j); return C.CannibalKings[p] || p; //a cannibal king move as... } @@ -1073,7 +1076,8 @@ export default class ChessRules { pieces(color, x, y) { const pawnShift = (color == "w" ? -1 : 1); - const initRank = ((color == 'w' && x == 6) || (color == 'b' && x == 1)); + // NOTE: jump 2 squares from first rank (pawns can be here sometimes) + const initRank = ((color == 'w' && x >= 6) || (color == 'b' && x <= 1)); return { 'p': { "class": "pawn", @@ -1155,7 +1159,7 @@ export default class ChessRules { // MOVES GENERATION // For Cylinder: get Y coordinate - computeY(y) { + getY(y) { if (!this.options["cylinder"]) return y; let res = y % this.size.y; @@ -1176,13 +1180,13 @@ export default class ChessRules { const attacks = specs.attack || specs.moves; for (let a of attacks) { outerLoop: for (let step of a.steps) { - let [ii, jj] = [i + step[0], this.computeY(j + step[1])]; + let [ii, jj] = [i + step[0], this.getY(j + step[1])]; let stepCounter = 1; while (this.onBoard(ii, jj) && this.board[ii][jj] == "") { if (a.range <= stepCounter++) continue outerLoop; ii += step[0]; - jj = this.computeY(jj + step[1]); + jj = this.getY(jj + step[1]); } if ( this.onBoard(ii, jj) && @@ -1283,7 +1287,7 @@ export default class ChessRules { this.options["rifle"] ) { // In this case a rifle-capture from last rank may promote a pawn - this.riflePromotePostProcess(moves); + this.riflePromotePostProcess(moves, color); } return moves; @@ -1319,7 +1323,7 @@ export default class ChessRules { ]; for (let step of steps) { let x = m.end.x + step[0]; - let y = this.computeY(m.end.y + step[1]); + let y = this.getY(m.end.y + step[1]); if ( this.onBoard(x, y) && this.board[x][y] != "" && @@ -1384,7 +1388,7 @@ export default class ChessRules { Array.prototype.push.apply(moves, moreMoves); } - riflePromotePostProcess(moves) { + riflePromotePostProcess(moves, color) { const lastRank = (color == "w" ? 0 : this.size.x - 1); let newMoves = []; moves.forEach(m => { @@ -1413,7 +1417,8 @@ export default class ChessRules { "#": "r", "$": "n", "%": "b", - "*": "q" + "*": "q", + "k": "k" }; } @@ -1429,10 +1434,7 @@ export default class ChessRules { } isKing(symbol) { - return ( - symbol == 'k' || - (this.options["cannibal"] && C.CannibalKings[symbol]) - ); + return !!C.CannibalKings[symbol]; } // For Madrasi: @@ -1453,7 +1455,7 @@ export default class ChessRules { if (a.range <= stepCounter++) continue outerLoop; i += step[0]; - j = this.computeY(j + step[1]); + j = this.getY(j + step[1]); } if ( this.onBoard(i, j) && @@ -1472,23 +1474,49 @@ export default class ChessRules { const color = this.getColor(x, y); const stepSpec = this.pieces(color, x, y)[piece]; let moves = []; - let explored = {}; //for Cylinder mode + // Next 3 for Cylinder mode: + let explored = {}; + let segments = []; + let segStart = []; + + const addMove = (start, end) => { + let newMove = this.getBasicMove(start, end); + if (segments.length > 0) { + newMove.segments = JSON.parse(JSON.stringify(segments)); + newMove.segments.push([[segStart[0], segStart[1]], [end[0], end[1]]]); + } + moves.push(newMove); + }; const findAddMoves = (type, stepArray) => { for (let s of stepArray) { - // TODO: if jump in y (computeY, Cylinder), move.segments outerLoop: for (let step of s.steps) { - let [i, j] = [x + step[0], this.computeY(y + step[1])]; - let stepCounter = 1; - while (this.onBoard(i, j) && this.board[i][j] == "") { - if (type != "attack" && !explored[i + "." + j]) { + segments = []; + segStart = [x, y]; + let [i, j] = [x, y]; + let stepCounter = 0; + while ( + this.onBoard(i, j) && + (this.board[i][j] == "" || (i == x && j == y)) + ) { + if ( + type != "attack" && + !explored[i + "." + j] && + (i != x || j != y) + ) { explored[i + "." + j] = true; - moves.push(this.getBasicMove([x, y], [i, j])); + addMove([x, y], [i, j]); } if (s.range <= stepCounter++) continue outerLoop; + const oldIJ = [i, j]; i += step[0]; - j = this.computeY(j + step[1]); + j = this.getY(j + step[1]); + if (Math.abs(j - oldIJ[1]) > 1) { + // Boundary between segments (cylinder mode) + segments.push([[segStart[0], segStart[1]], oldIJ]); + segStart = [i, j]; + } } if (!this.onBoard(i, j)) continue; @@ -1509,7 +1537,7 @@ export default class ChessRules { ) ) { explored[i + "." + j] = true; - moves.push(this.getBasicMove([x, y], [i, j])); + addMove([x, y], [i, j]); } } } @@ -1519,28 +1547,29 @@ export default class ChessRules { if (specialAttack) findAddMoves("attack", stepSpec.attack); findAddMoves(specialAttack ? "moveonly" : "all", stepSpec.moves); - if (this.options["zen"]) - Array.prototype.push.apply(moves, this.findCapturesOn([x, y], true)); + if (this.options["zen"]) { + Array.prototype.push.apply(moves, + this.findCapturesOn([x, y], {zen: true})); + } return moves; } - findCapturesOn([x, y], zen) { + // Search for enemy (or not) pieces attacking [x, y] + findCapturesOn([x, y], args) { let moves = []; - // Find reverse captures (opponent takes) - const color = this.getColor(x, y); - const pieceType = this.getPieceType(x, y); - const oppCol = C.GetOppCol(color); + if (!args.oppCol) + args.oppCol = C.GetOppCol(this.getColor(x, y) || this.turn); for (let i=0; i= 1); + return ( + this.findCapturesOn([x, y], {oppCol: oppCol, one: true}).length >= 1 + ); } // Stop at first king found (TODO: multi-kings) @@ -1862,12 +1901,12 @@ export default class ChessRules { let square = kingPos, res = true; //a priori valid if (m.vanish.some(v => { - return (v.p == "k" || C.CannibalKings[v.p]) && v.c == color; + return C.CannibalKings[v.p] && v.c == color; })) { // Search king in appear array: const newKingIdx = m.appear.findIndex(a => { - return (a.p == "k" || C.CannibalKings[a.p]) && a.c == color; + return C.CannibalKings[a.p] && a.c == color; }); if (newKingIdx >= 0) square = [m.appear[newKingIdx].x, m.appear[newKingIdx].y]; @@ -1898,13 +1937,17 @@ export default class ChessRules { // Apply a move on board playOnBoard(move) { - for (let psq of move.vanish) this.board[psq.x][psq.y] = ""; - for (let psq of move.appear) this.board[psq.x][psq.y] = psq.c + psq.p; + for (let psq of move.vanish) + this.board[psq.x][psq.y] = ""; + for (let psq of move.appear) + this.board[psq.x][psq.y] = psq.c + psq.p; } // Un-apply the played move undoOnBoard(move) { - for (let psq of move.appear) this.board[psq.x][psq.y] = ""; - for (let psq of move.vanish) this.board[psq.x][psq.y] = psq.c + psq.p; + for (let psq of move.appear) + this.board[psq.x][psq.y] = ""; + for (let psq of move.vanish) + this.board[psq.x][psq.y] = psq.c + psq.p; } updateCastleFlags(move) { @@ -2093,7 +2136,6 @@ export default class ChessRules { return (color == "w" ? "0-1" : "1-0"); } - // NOTE: quite suboptimal for eg. Benedict (not a big deal I think) playVisual(move, r) { move.vanish.forEach(v => { // TODO: next "if" shouldn't be required @@ -2113,7 +2155,9 @@ export default class ChessRules { this.g_pieces[a.x][a.y].style.width = pieceWidth + "px"; this.g_pieces[a.x][a.y].style.height = pieceWidth + "px"; const [ip, jp] = this.getPixelPosition(a.x, a.y, r); - this.g_pieces[a.x][a.y].style.transform = `translate(${ip}px,${jp}px)`; + // Translate coordinates to use chessboard as reference: + this.g_pieces[a.x][a.y].style.transform = + `translate(${ip - r.x}px,${jp - r.y}px)`; if (this.enlightened && !this.enlightened[a.x][a.y]) this.g_pieces[a.x][a.y].classList.add("hidden"); chessboard.appendChild(this.g_pieces[a.x][a.y]); @@ -2142,22 +2186,26 @@ export default class ChessRules { callback(); return; } - let movingPiece = this.getDomPiece(move.start.x, move.start.y); - if (!movingPiece) { //TODO this shouldn't be required + let initPiece = this.getDomPiece(move.start.x, move.start.y); + if (!initPiece) { //TODO this shouldn't be required callback(); return; } - const initTransform = movingPiece.style.transform; - let chessboard = - document.getElementById(this.containerId).querySelector(".chessboard"); - const r = chessboard.getBoundingClientRect(); - const [ix, iy] = this.getPixelPosition(move.start.x, move.start.y, r); + // NOTE: cloning generally not required, but light enough, and simpler + let movingPiece = initPiece.cloneNode(); + initPiece.style.opacity = "0"; + let container = + document.getElementById(this.containerId) + const r = container.querySelector(".chessboard").getBoundingClientRect(); + if (typeof move.start.x == "string") { + // Need to bound width/height (was 100% for reserve pieces) + const pieceWidth = this.getPieceWidth(r.width); + movingPiece.style.width = pieceWidth + "px"; + movingPiece.style.height = pieceWidth + "px"; + } const maxDist = this.getMaxDistance(r.width); - // NOTE: move.drag could be generalized per-segment (usage?) + const pieces = this.pieces(); if (move.drag) { - // Drag something else: require cloning - movingPiece = movingPiece.cloneNode(); - const pieces = this.pieces(); const startCode = this.getPiece(move.start.x, move.start.y); movingPiece.classList.remove(pieces[startCode]["class"]); movingPiece.classList.add(pieces[move.drag.p]["class"]); @@ -2166,19 +2214,26 @@ export default class ChessRules { movingPiece.classList.remove(C.GetColorClass(apparentColor)); movingPiece.classList.add(C.GetColorClass(move.drag.c)); } - chessboard.appendChild(movingPiece); } + container.appendChild(movingPiece); const animateSegment = (index, cb) => { + // NOTE: move.drag could be generalized per-segment (usage?) const [i1, j1] = move.segments[index][0]; const [i2, j2] = move.segments[index][1]; const dep = this.getPixelPosition(i1, j1, r); const arr = this.getPixelPosition(i2, j2, r); + movingPiece.style.transitionDuration = "0s"; + movingPiece.style.transform = `translate(${dep[0]}px, ${dep[1]}px)`; const distance = Math.sqrt((arr[0] - dep[0]) ** 2 + (arr[1] - dep[1]) ** 2); const duration = 0.2 + (distance / maxDist) * 0.3; - movingPiece.style.transform = `translate(${arr[0]}px, ${arr[1]}px)`; - movingPiece.style.transitionDuration = duration + "s"; - setTimeout(cb, duration * 1000); + // TODO: unclear why we need this new delay below: + setTimeout(() => { + movingPiece.style.transitionDuration = duration + "s"; + // movingPiece is child of container: no need to adjust coordinates + movingPiece.style.transform = `translate(${arr[0]}px, ${arr[1]}px)`; + setTimeout(cb, duration * 1000); + }, 50); }; if (!move.segments) { move.segments = [ @@ -2190,12 +2245,8 @@ export default class ChessRules { if (index < move.segments.length) animateSegment(index++, animateSegmentCallback); else { - if (move.drag) - movingPiece.remove(); - else { - movingPiece.style.transform = initTransform; - movingPiece.style.transitionDuration = "0s"; - } + movingPiece.remove(); + initPiece.style.opacity = "1"; callback(); } };