chessground12
Version:
Extended lishuuro.org Chess UI
341 lines • 12.2 kB
JavaScript
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