UNPKG

chessground12

Version:
341 lines 12.2 kB
import { pos2key, key2pos, opposite, distanceSq, allPos, computeSquareCenter, dropOrigOf, kingRoles, } from './util'; import { premove, queen, knight } from './premove'; import { predrop } from './predrop'; import { cancelDropMode } from './drop'; import { dimensions, } from './types'; // eslint-disable-next-line export function callUserFunction(f, ...args) { if (f) setTimeout(() => f(...args), 1); } export function toggleOrientation(state) { state.orientation = opposite(state.orientation); state.animation.current = state.draggable.current = state.selected = undefined; } export function reset(state) { state.lastMove = undefined; unselect(state); unsetPremove(state); unsetPredrop(state); } export function setPieces(state, pieces) { state.pieces.clear(); for (const [key, piece] of pieces) { if (piece) state.pieces.set(key, piece); else state.pieces.delete(key); } } export function setPlinths(state, plinths) { for (const [key, piece] of plinths) { if (piece) state.plinths?.set(key, piece); else state.plinths?.delete(key); } } export function setLastMove(state, from, to) { state.lastMove = [from, to]; // } export function setCheck(state, color) { const kings = kingRoles(state.variant); state.check = undefined; if (color === true) color = state.turnColor; if (color) for (const [k, p] of state.pieces) { if (kings.includes(p.role) && p.color === color) { state.check = k; break; } } } function setPremove(state, orig, dest, meta) { unsetPredrop(state); state.premovable.current = [orig, dest]; callUserFunction(state.premovable.events.set, orig, dest, meta); } export function unsetPremove(state) { if (state.premovable.current) { state.premovable.current = undefined; callUserFunction(state.premovable.events.unset); } } function setPredrop(state, role, key) { unsetPremove(state); state.predroppable.current = { role, key }; callUserFunction(state.predroppable.events.set, role, key); } export function unsetPredrop(state) { const pd = state.predroppable; if (pd.current) { pd.current = undefined; callUserFunction(pd.events.unset); } } export function baseMove(state, orig, dest) { const origPiece = state.pieces.get(orig), destPiece = state.pieces.get(dest); if (orig === dest || !origPiece) return false; const captured = destPiece && destPiece.color !== origPiece.color ? destPiece : undefined; if (dest === state.selected) unselect(state); callUserFunction(state.events.move, orig, dest, captured); if (!state.autoCastle) { state.pieces.set(dest, origPiece); state.pieces.delete(orig); } state.lastMove = [orig, dest]; state.check = undefined; callUserFunction(state.events.change); return captured || true; } export function baseNewPiece(state, piece, key, force) { if (state.pieces.has(key)) { if (force) state.pieces.delete(key); else return false; } callUserFunction(state.events.dropNewPiece, piece, key); state.pieces.set(key, piece); state.lastMove = [key]; state.check = undefined; callUserFunction(state.events.change); state.movable.dests = undefined; state.dropmode.dropDests = undefined; state.turnColor = opposite(state.turnColor); return true; } function baseUserMove(state, orig, dest) { const result = baseMove(state, orig, dest); if (result) { state.movable.dests = undefined; state.dropmode.dropDests = undefined; state.turnColor = opposite(state.turnColor); state.animation.current = undefined; } return result; } export function userMove(state, orig, dest) { if (canMove(state, orig, dest)) { const result = baseUserMove(state, orig, dest); if (result) { const holdTime = state.hold.stop(); unselect(state); const metadata = { premove: false, ctrlKey: state.stats.ctrlKey, holdTime, }; if (result !== true) metadata.captured = result; callUserFunction(state.movable.events.after, orig, dest, metadata); return true; } } else if (canPremove(state, orig, dest)) { setPremove(state, orig, dest, { ctrlKey: state.stats.ctrlKey, }); unselect(state); return true; } unselect(state); return false; } /** * TODO: I believe this function is always called with orig=='a0'. Maybe consider changing that parameter to piece/role instead. * I think we currently artificially assign state.pieces[a0] to the current pocket piece being dragged/selected, but it is imho hackish * and we might little by little make existing code agnostic of that hack instead of tightly coupling it to it and making it depend * on having that there, with the eventual goal of making pocket dynamics more of a first class citizens rather than hacks on top of * regular chess movements dynamics * */ export function dropNewPiece(state, orig, dest, force) { const piece = state.pieces.get(orig); if (piece && (canDrop(state, piece.role, dest) || force)) { state.pieces.delete(orig); baseNewPiece(state, piece, dest, force); state.dropmode.active = false; callUserFunction(state.movable.events.afterNewPiece, piece.role, dest, { premove: false, predrop: false, }); } else if (piece && canPredrop(state, orig, dest)) { setPredrop(state, piece.role, dest); } else { unsetPremove(state); unsetPredrop(state); cancelDropMode(state); } state.pieces.delete(orig); unselect(state); } export function selectSquare(state, key, force) { callUserFunction(state.events.select, key); if (state.selected) { if (state.selected === key && !state.draggable.enabled) { unselect(state); state.hold.cancel(); return; } else if ((state.selectable.enabled || force) && state.selected !== key) { if (userMove(state, state.selected, key)) { state.stats.dragged = false; return; } } } if (isMovable(state, key) || isPremovable(state, key)) { setSelected(state, key); state.hold.start(); } } export function setSelected(state, key) { state.selected = key; if (isPremovable(state, key)) { state.premovable.dests = premove(state.pieces, key, state.premovable.castle, state.geometry, state.variant); } else { state.premovable.dests = undefined; state.predroppable.dropDests = undefined; } } export function unselect(state) { state.selected = undefined; state.premovable.dests = undefined; state.predroppable.dropDests = undefined; state.hold.cancel(); } function isMovable(state, orig) { const piece = state.pieces.get(orig); return (!!piece && (state.movable.color === 'both' || (state.movable.color === piece.color && state.turnColor === piece.color))); } export function canMove(state, orig, dest) { return (orig !== dest && isMovable(state, orig) && (state.movable.free || !!state.movable.dests?.get(orig)?.includes(dest))); } function canDrop(state, role, dest) { if (state.movable.free) return true; return !!state.movable.dests?.get(dropOrigOf(role))?.includes(dest); } function isPremovable(state, orig) { const piece = state.pieces.get(orig); return (!!piece && state.premovable.enabled && state.movable.color === piece.color && state.turnColor !== piece.color); } export function isPredroppable(state) { const piece = state.dropmode.active ? state.dropmode.piece : state.draggable.current?.piece; return (!!piece && (state.dropmode.active || state.draggable.current?.orig === 'a0') && state.predroppable.enabled && state.movable.color === piece.color && state.turnColor !== piece.color); } function canPremove(state, orig, dest) { return (orig !== dest && isPremovable(state, orig) && premove(state.pieces, orig, state.premovable.castle, state.geometry, state.variant).includes(dest)); } // TODO: orig is probably always equal to a0 and only used for getting the piece - consider replacing that param with "piece" (see also dropNewPiece(...) ) function canPredrop(state, orig, dest) { const piece = state.pieces.get(orig); const destPiece = state.pieces.get(dest); return (!!piece && (!destPiece || destPiece.color !== state.movable.color) && state.predroppable.enabled && state.movable.color === piece.color && state.turnColor !== piece.color && predrop(state.pieces, piece, state.geometry, state.variant).includes(dest)); } export function isDraggable(state, orig) { const piece = state.pieces.get(orig); return (!!piece && state.draggable.enabled && (state.movable.color === 'both' || (state.movable.color === piece.color && (state.turnColor === piece.color || state.premovable.enabled)))); } export function playPremove(state) { const move = state.premovable.current; if (!move) return false; const orig = move[0], dest = move[1]; let success = false; if (canMove(state, orig, dest)) { const result = baseUserMove(state, orig, dest); if (result) { const metadata = { premove: true }; if (result !== true) metadata.captured = result; callUserFunction(state.movable.events.after, orig, dest, metadata); success = true; } } unsetPremove(state); return success; } export function playPredrop(state) { const drop = state.predroppable.current; let success = false; if (!drop) return false; if (canDrop(state, drop.role, drop.key)) { const piece = { role: drop.role, color: state.movable.color, }; if (baseNewPiece(state, piece, drop.key)) { callUserFunction(state.movable.events.afterNewPiece, drop.role, drop.key, { premove: false, predrop: true, }); success = true; } } unsetPredrop(state); return success; } export function cancelMove(state) { unsetPremove(state); unsetPredrop(state); unselect(state); } export function stop(state) { state.movable.color = state.movable.dests = state.dropmode.dropDests = state.animation.current = undefined; cancelMove(state); } export function getKeyAtDomPos(pos, asWhite, bounds, geom) { const bd = dimensions[geom]; let file = Math.floor((bd.width * (pos[0] - bounds.left)) / bounds.width); if (!asWhite) file = bd.width - 1 - file; let rank = bd.height - 1 - Math.floor((bd.height * (pos[1] - bounds.top)) / bounds.height); if (!asWhite) rank = bd.height - 1 - rank; return file >= 0 && file < bd.width && rank >= 0 && rank < bd.height ? pos2key([file, rank]) : undefined; } export function getSnappedKeyAtDomPos(orig, pos, asWhite, bounds, geom) { const origPos = key2pos(orig); const validSnapPos = allPos(geom).filter(pos2 => { return (queen(origPos[0], origPos[1], pos2[0], pos2[1]) || knight(origPos[0], origPos[1], pos2[0], pos2[1])); }); const bd = dimensions[geom]; const validSnapCenters = validSnapPos.map(pos2 => computeSquareCenter(pos2key(pos2), asWhite, bounds, bd)); const validSnapDistances = validSnapCenters.map(pos2 => distanceSq(pos, pos2)); const [, closestSnapIndex] = validSnapDistances.reduce((a, b, index) => (a[0] < b ? a : [b, index]), [validSnapDistances[0], 0]); return pos2key(validSnapPos[closestSnapIndex]); } export function whitePov(s) { return s.orientation === 'white'; } //# sourceMappingURL=board.js.map