chessgroundx
Version:
Extended lichess.org Chess UI
1,159 lines (1,156 loc) • 152 kB
JavaScript
var Chessground = (function () {
'use strict';
var Notation;
(function (Notation) {
Notation[Notation["ALGEBRAIC"] = 0] = "ALGEBRAIC";
Notation[Notation["SHOGI_ENGLET"] = 1] = "SHOGI_ENGLET";
Notation[Notation["SHOGI_ARBNUM"] = 2] = "SHOGI_ARBNUM";
Notation[Notation["SHOGI_HANNUM"] = 3] = "SHOGI_HANNUM";
Notation[Notation["JANGGI"] = 4] = "JANGGI";
Notation[Notation["XIANGQI_ARBNUM"] = 5] = "XIANGQI_ARBNUM";
Notation[Notation["XIANGQI_HANNUM"] = 6] = "XIANGQI_HANNUM";
Notation[Notation["THAI_ALGEBRAIC"] = 7] = "THAI_ALGEBRAIC";
})(Notation || (Notation = {}));
const colors = ['white', 'black'];
const files$1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'];
const ranks$1 = ['1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@'];
const letters = [
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z',
];
const invRanks = [...ranks$1].reverse();
function files(n) {
return files$1.slice(0, n);
}
function ranks(n) {
return ranks$1.slice(0, n);
}
function allKeys(bd) {
return Array.prototype.concat(...files(bd.width).map(c => ranks(bd.height).map(r => c + r)));
}
function allPos(bd) {
return allKeys(bd).map(key2pos);
}
const pos2key = (pos) => (files$1[pos[0]] + ranks$1[pos[1]]);
const key2pos = (k) => [k.charCodeAt(0) - 97, k.charCodeAt(1) - 49];
function roleOf(letter) {
return (letter.replace('+', 'p').replace('*', '_').replace('@', '').toLowerCase() + '-piece');
}
function letterOf(role, uppercase = false) {
const letterPart = role.slice(0, role.indexOf('-'));
const letter = (letterPart.length > 1 ? letterPart.replace('p', '+') : letterPart).replace('_', '*');
return (uppercase ? letter.toUpperCase() : letter);
}
function dropOrigOf(role) {
return (letterOf(role, true) + '@');
}
function isDropOrig(orig) {
return orig[0] === orig[0].toUpperCase();
}
function isKey(selectable) {
return typeof selectable === 'string' && selectable[0] === selectable[0].toLowerCase();
}
function isPiece(selectable) {
return typeof selectable !== 'string';
}
function isSame(lhs, rhs) {
if (isPiece(lhs) && isPiece(rhs))
return samePiece(lhs, rhs);
else
return lhs === rhs;
}
function changeNumber(map, key, num) {
var _a;
map.set(key, ((_a = map.get(key)) !== null && _a !== void 0 ? _a : 0) + num);
}
function memo(f) {
let v;
const ret = () => {
if (v === undefined)
v = f();
return v;
};
ret.clear = () => {
v = undefined;
};
return ret;
}
const timer = () => {
let startAt;
return {
start() {
startAt = performance.now();
},
cancel() {
startAt = undefined;
},
stop() {
if (!startAt)
return 0;
const time = performance.now() - startAt;
startAt = undefined;
return time;
},
};
};
const opposite = (c) => (c === 'white' ? 'black' : 'white');
const samePiece = (p1, p2) => p1.role === p2.role && p1.color === p2.color && !!p1.promoted === !!p2.promoted;
const pieceSide = (p, o) => (p.color === o ? 'ally' : 'enemy');
const pieceClasses = (p, o) => `${p.color} ${pieceSide(p, o)} ${p.mirror ? 'mirror' : ''} ${p.promoted ? 'promoted ' : ''}${p.role}`;
const distanceSq = (pos1, pos2) => {
const dx = pos1[0] - pos2[0], dy = pos1[1] - pos2[1];
return dx * dx + dy * dy;
};
const posToTranslate = (bounds, bd) => (pos, asWhite) => [
((asWhite ? pos[0] : bd.width - 1 - pos[0]) * bounds.width) / bd.width,
((asWhite ? bd.height - 1 - pos[1] : pos[1]) * bounds.height) / bd.height,
];
const translate = (el, pos) => {
el.style.transform = `translate(${pos[0]}px,${pos[1]}px)`;
};
const translateAndScale = (el, pos, scale = 1) => {
el.style.transform = `translate(${pos[0]}px,${pos[1]}px) scale(${scale})`;
};
const setVisible = (el, v) => {
el.style.visibility = v ? 'visible' : 'hidden';
};
const eventPosition = (e) => {
var _a;
if (e.clientX || e.clientX === 0)
return [e.clientX, e.clientY];
if ((_a = e.targetTouches) === null || _a === void 0 ? void 0 : _a[0])
return [e.targetTouches[0].clientX, e.targetTouches[0].clientY];
return; // touchend has no position!
};
const isRightButton = (e) => e.buttons === 2 || e.button === 2;
const createEl = (tagName, className) => {
const el = document.createElement(tagName);
if (className)
el.className = className;
return el;
};
const isMiniBoard = (el) => {
return Array.from(el.classList).includes('mini');
};
function computeSquareCenter(key, asWhite, bounds, bd) {
const pos = key2pos(key);
if (!asWhite) {
pos[0] = bd.width - 1 - pos[0];
pos[1] = bd.height - 1 - pos[1];
}
return [
bounds.left + (bounds.width * (pos[0] + 0.5)) / bd.width,
bounds.top + (bounds.height * (bd.height - pos[1] - 0.5)) / bd.height,
];
}
const diff = (a, b) => Math.abs(a - b);
const pawn = (color) => (x1, y1, x2, y2) => diff(x1, x2) < 2 &&
(color === 'white'
? // allow 2 squares from first two ranks, for horde
y2 === y1 + 1 || (y1 <= 1 && y2 === y1 + 2 && x1 === x2)
: y2 === y1 - 1 || (y1 >= 6 && y2 === y1 - 2 && x1 === x2));
const knight = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return (xd === 1 && yd === 2) || (xd === 2 && yd === 1);
};
const bishop = (x1, y1, x2, y2) => {
return diff(x1, x2) === diff(y1, y2);
};
const rook = (x1, y1, x2, y2) => {
return x1 === x2 || y1 === y2;
};
const queen = (x1, y1, x2, y2) => {
return bishop(x1, y1, x2, y2) || rook(x1, y1, x2, y2);
};
const king = (color, rookFiles, canCastle) => (x1, y1, x2, y2) => (diff(x1, x2) < 2 && diff(y1, y2) < 2) ||
(canCastle &&
y1 === y2 &&
y1 === (color === 'white' ? 0 : 7) &&
((x1 === 4 && ((x2 === 2 && rookFiles.includes(0)) || (x2 === 6 && rookFiles.includes(7)))) ||
rookFiles.includes(x2)));
function rookFilesOf(pieces, color) {
const backrank = color === 'white' ? '1' : '8';
const files = [];
for (const [key, piece] of pieces) {
if (key[1] === backrank && piece.color === color && piece.role === 'r-piece') {
files.push(key2pos(key)[0]);
}
}
return files;
}
function and(...ms) {
return (x1, y1, x2, y2) => ms.map(m => m(x1, y1, x2, y2)).reduce((a, b) => a && b);
}
function or(...ms) {
return (x1, y1, x2, y2) => ms.map(m => m(x1, y1, x2, y2)).reduce((a, b) => a || b);
}
/* TODO make use of this
function not(m: Mobility): Mobility {
return (x1, y1, x2, y2) => !m(x1, y1, x2, y2);
}
*/
function _distance(dist) {
return (x1, y1, x2, y2) => Math.max(diff(x1, x2), diff(y1, y2)) <= dist;
}
function memoizeDistance() {
const cache = {};
return (dist) => {
const key = `${dist}`;
if (!(key in cache))
cache[key] = _distance(dist);
return cache[key];
};
}
const distance = memoizeDistance();
function backrank(color) {
return color === 'white' ? 0 : 7;
}
// king without castling
const kingNoCastling = (x1, y1, x2, y2) => {
return diff(x1, x2) < 2 && diff(y1, y2) < 2;
};
// 960 king (can only castle with king takes rook)
function king960(color, rookFiles, canCastle) {
return (x1, y1, x2, y2) => kingNoCastling(x1, y1, x2, y2) || (canCastle && y1 === y2 && y1 === backrank(color) && rookFiles.includes(x2));
}
// capablanca king (different castling files from standard chess king)
function kingCapa(color, rookFiles, canCastle) {
return (x1, y1, x2, y2) => kingNoCastling(x1, y1, x2, y2) ||
(canCastle &&
y1 === y2 &&
y1 === backrank(color) &&
x1 === 5 &&
((x2 === 8 && rookFiles.includes(9)) || (x2 === 2 && rookFiles.includes(0))));
}
// shako king (different castling files and ranks from standard chess king)
function kingShako(color, rookFiles, canCastle) {
return (x1, y1, x2, y2) => kingNoCastling(x1, y1, x2, y2) ||
(canCastle &&
y1 === y2 &&
y1 === (color === 'white' ? 1 : 8) &&
x1 === 5 &&
((x2 === 7 && rookFiles.includes(8)) || (x2 === 3 && rookFiles.includes(1))));
}
function rookFilesOfShako(pieces, color) {
const backrank = color === 'white' ? '2' : '9';
const files = [];
for (const [key, piece] of pieces) {
if (key[1] === backrank && piece.color === color && piece.role === 'r-piece') {
files.push(key2pos(key)[0]);
}
}
return files;
}
// ouk king (can jump like a knight to the second row on its first move)
function kingOuk(color, canCastle) {
return (x1, y1, x2, y2) => kingNoCastling(x1, y1, x2, y2) ||
(canCastle &&
(color === 'white'
? x1 === 3 && y1 === 0 && (x2 === 1 || x2 === 5) && y2 === 1
: x1 === 4 && y1 === 7 && (x2 === 6 || x2 === 2) && y2 === 6));
}
function pawnNoDoubleStep(color) {
return (x1, y1, x2, y2) => diff(x1, x2) < 2 && (color === 'white' ? y2 === y1 + 1 : y2 === y1 - 1);
}
// grand pawn (10x10 board, can move two squares on third row)
function pawnGrand(color) {
return (x1, y1, x2, y2) => diff(x1, x2) < 2 &&
(color === 'white'
? y2 === y1 + 1 || (y1 <= 2 && y2 === y1 + 2 && x1 === x2)
: y2 === y1 - 1 || (y1 >= 7 && y2 === y1 - 2 && x1 === x2));
}
// sittuyin pawn (8x8 board, can move diagonally backward to promote on some squares)
function pawnSittuyin(pieces, color) {
return (x1, y1, x2, y2) => {
let canPromote = (color === 'white' ? y1 >= 4 : y1 <= 3) && (x1 === y1 || 7 - x1 === y1);
if (!canPromote) {
let pawnCount = 0;
for (const p of pieces.values())
if (p.role === 'p-piece' && p.color === color)
pawnCount += 1;
canPromote || (canPromote = pawnCount === 1);
}
return pawnNoDoubleStep(color)(x1, y1, x2, y2) || (canPromote && ferz(x1, y1, x2, y2));
};
}
function pawnBerolina(color) {
return (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
return (color === 'white'
? // allow 2 squares from first two ranks, for horde
(y2 === y1 + 1 && xd <= 1) || (y1 <= 1 && y2 === y1 + 2 && xd === 2)
: (y2 === y1 - 1 && xd <= 1) || (y1 >= 6 && y2 === y1 - 2 && xd === 2));
};
}
const pawnAtaxx = (x1, y1, x2, y2) => {
return diff(x1, x2) <= 2 && diff(y1, y2) <= 2;
};
const sideways = (x1, y1, x2, y2) => {
return y1 === y2 && diff(x1, x2) <= 1;
};
// wazir
const wazir = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return (xd === 1 && yd === 0) || (xd === 0 && yd === 1);
};
// ferz, met
const ferz = (x1, y1, x2, y2) => diff(x1, x2) === diff(y1, y2) && diff(x1, x2) === 1;
const fersAlfil = (x1, y1, x2, y2) => {
return diff(x1, x2) === diff(y1, y2) && diff(x1, x2) < 3;
};
const wazirDabbaba = (x1, y1, x2, y2) => {
return (x1 === x2 && diff(y1, y2) < 3) || (y1 === y2 && diff(x1, x2) < 3);
};
// ouk ferz (can jump two squares forward on its first move)
function ferzOuk(color) {
return (x1, y1, x2, y2) => ferz(x1, y1, x2, y2) ||
(color === 'white' ? x1 === 4 && y1 === 0 && x2 === 4 && y2 === 2 : x1 === 3 && y1 === 7 && x2 === 3 && y2 === 5);
}
// alfil (shatranj elephant)
const elephant = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return xd === yd && xd === 2;
};
// archbishop (knight + bishop)
const archbishop = (x1, y1, x2, y2) => {
return bishop(x1, y1, x2, y2) || knight(x1, y1, x2, y2);
};
// chancellor (knight + rook)
const chancellor = (x1, y1, x2, y2) => {
return rook(x1, y1, x2, y2) || knight(x1, y1, x2, y2);
};
// amazon (knight + queen)
const amazon = (x1, y1, x2, y2) => {
return bishop(x1, y1, x2, y2) || rook(x1, y1, x2, y2) || knight(x1, y1, x2, y2);
};
// mansindam rhino (archbishop + wazir)
const crownPrincess = (x1, y1, x2, y2) => {
return bishop(x1, y1, x2, y2) || knight(x1, y1, x2, y2) || wazir(x1, y1, x2, y2);
};
// mansindam ship (chancellor + ferz)
const archChancellor = (x1, y1, x2, y2) => {
return rook(x1, y1, x2, y2) || knight(x1, y1, x2, y2) || ferz(x1, y1, x2, y2);
};
// shogun general (knight + king)
const centaur = (x1, y1, x2, y2) => {
return kingNoCastling(x1, y1, x2, y2) || knight(x1, y1, x2, y2);
};
// shogi lance
function shogiLance(color) {
return (x1, y1, x2, y2) => x2 === x1 && (color === 'white' ? y2 > y1 : y2 < y1);
}
// shogi silver, makruk khon, sittuyin elephant
function shogiSilver(color) {
return (x1, y1, x2, y2) => ferz(x1, y1, x2, y2) || (x1 === x2 && (color === 'white' ? y2 === y1 + 1 : y2 === y1 - 1));
}
// shogi gold, promoted pawn/knight/lance/silver
function shogiGold(color) {
return (x1, y1, x2, y2) => wazir(x1, y1, x2, y2) || (diff(x1, x2) < 2 && (color === 'white' ? y2 === y1 + 1 : y2 === y1 - 1));
}
// shogi pawn
function shogiPawn(color) {
return (x1, y1, x2, y2) => x2 === x1 && (color === 'white' ? y2 === y1 + 1 : y2 === y1 - 1);
}
// shogi knight
function shogiKnight(color) {
return (x1, y1, x2, y2) => (x2 === x1 - 1 || x2 === x1 + 1) && (color === 'white' ? y2 === y1 + 2 : y2 === y1 - 2);
}
// shogi promoted rook (dragon king)
const shogiDragon = (x1, y1, x2, y2) => {
return rook(x1, y1, x2, y2) || ferz(x1, y1, x2, y2);
};
// shogi promoted bishop (dragon horse)
const shogiHorse = (x1, y1, x2, y2) => {
return bishop(x1, y1, x2, y2) || wazir(x1, y1, x2, y2);
};
// cannon shogi promoted orthogonal (silver/gold) cannons
const flyingOrthogonalCannon = (x1, y1, x2, y2) => {
return rook(x1, y1, x2, y2) || fersAlfil(x1, y1, x2, y2);
};
// cannon shogi promoted diagonal (iron/copper) cannons
const flyingDiagonalCannon = (x1, y1, x2, y2) => {
return bishop(x1, y1, x2, y2) || wazirDabbaba(x1, y1, x2, y2);
};
function scout(color) {
return (x1, y1, x2, y2) => (x2 === x1 && (color === 'white' ? y2 === y1 + 1 : y2 === y1 - 1)) ||
((x2 === x1 - 1 || x2 === x1 + 1) && (color === 'white' ? y2 === y1 + 2 : y2 === y1 - 2)) ||
((x2 === x1 - 2 || x2 === x1 + 2) && (color === 'white' ? y2 === y1 + 1 : y2 === y1 - 1));
}
function _palace(bd, color) {
const middleFile = Math.floor(bd.width / 2);
const startingRank = color === 'white' ? 0 : bd.height - 3;
return [
[middleFile - 1, startingRank + 2],
[middleFile, startingRank + 2],
[middleFile + 1, startingRank + 2],
[middleFile - 1, startingRank + 1],
[middleFile, startingRank + 1],
[middleFile + 1, startingRank + 1],
[middleFile - 1, startingRank],
[middleFile, startingRank],
[middleFile + 1, startingRank],
];
}
function memoizePalace() {
const cache = {};
return (bd, color) => {
const key = `${bd.width}x${bd.height}${color.slice(0, 1)}`;
if (!(key in cache))
cache[key] = _palace(bd, color);
return cache[key];
};
}
const palace = memoizePalace();
// xiangqi pawn
function xiangqiPawn(color) {
return (x1, y1, x2, y2) => (x2 === x1 && (color === 'white' ? y2 === y1 + 1 : y2 === y1 - 1)) ||
(y2 === y1 && diff(x1, x2) < 2 && (color === 'white' ? y1 > 4 : y1 < 5));
}
// minixiangqi pawn
function minixiangqiPawn(color) {
return (x1, y1, x2, y2) => (x2 === x1 && (color === 'white' ? y2 === y1 + 1 : y2 === y1 - 1)) || (y2 === y1 && diff(x1, x2) < 2);
}
// xiangqi elephant
function xiangqiElephant(color) {
return (x1, y1, x2, y2) => elephant(x1, y1, x2, y2) && (color === 'white' ? y2 < 5 : y2 > 4);
}
// xiangqi advisor
function xiangqiAdvisor(color, bd) {
const p = palace(bd, color);
return (x1, y1, x2, y2) => ferz(x1, y1, x2, y2) && p.some(point => point[0] === x2 && point[1] === y2);
}
// xiangqi general (king)
function xiangqiKing(color, bd) {
const p = palace(bd, color);
return (x1, y1, x2, y2) => wazir(x1, y1, x2, y2) && p.some(point => point[0] === x2 && point[1] === y2);
}
// janggi elephant
const janggiElephant = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return (xd === 2 && yd === 3) || (xd === 3 && yd === 2);
};
// janggi pawn
function janggiPawn(color, bd) {
const oppPalace = palace(bd, opposite(color));
return (x1, y1, x2, y2) => {
const palacePos = oppPalace.findIndex(point => point[0] === x1 && point[1] === y1);
let additionalMobility;
switch (palacePos) {
case 0:
additionalMobility = (x1, y1, x2, y2) => x2 === x1 + 1 && color === 'black' && y2 === y1 - 1;
break;
case 2:
additionalMobility = (x1, y1, x2, y2) => x2 === x1 - 1 && color === 'black' && y2 === y1 - 1;
break;
case 4:
additionalMobility = (x1, y1, x2, y2) => diff(x1, x2) === 1 && (color === 'white' ? y2 === y1 + 1 : y2 === y1 - 1);
break;
case 6:
additionalMobility = (x1, y1, x2, y2) => x2 === x1 + 1 && color === 'white' && y2 === y1 + 1;
break;
case 8:
additionalMobility = (x1, y1, x2, y2) => x2 === x1 - 1 && color === 'white' && y2 === y1 + 1;
break;
default:
additionalMobility = () => false;
}
return minixiangqiPawn(color)(x1, y1, x2, y2) || additionalMobility(x1, y1, x2, y2);
};
}
// janggi rook
function janggiRook(bd) {
const wPalace = palace(bd, 'white');
const bPalace = palace(bd, 'black');
return (x1, y1, x2, y2) => {
let additionalMobility;
const wPalacePos = wPalace.findIndex(point => point[0] === x1 && point[1] === y1);
const bPalacePos = bPalace.findIndex(point => point[0] === x1 && point[1] === y1);
const palacePos = wPalacePos !== -1 ? wPalacePos : bPalacePos;
const xd = diff(x1, x2);
const yd = diff(y1, y2);
switch (palacePos) {
case 0:
additionalMobility = (x1, y1, x2, y2) => xd === yd && x2 > x1 && x2 <= x1 + 2 && y2 < y1 && y2 >= y1 - 2;
break;
case 2:
additionalMobility = (x1, y1, x2, y2) => xd === yd && x2 < x1 && x2 >= x1 - 2 && y2 < y1 && y2 >= y1 - 2;
break;
case 4:
additionalMobility = ferz;
break;
case 6:
additionalMobility = (x1, y1, x2, y2) => xd === yd && x2 > x1 && x2 <= x1 + 2 && y2 > y1 && y2 <= y1 + 2;
break;
case 8:
additionalMobility = (x1, y1, x2, y2) => xd === yd && x2 < x1 && x2 >= x1 - 2 && y2 > y1 && y2 <= y1 + 2;
break;
default:
additionalMobility = () => false;
}
return rook(x1, y1, x2, y2) || additionalMobility(x1, y1, x2, y2);
};
}
// janggi general (king)
function janggiKing(color, bd) {
const ownPalace = palace(bd, color);
return (x1, y1, x2, y2) => {
const palacePos = ownPalace.findIndex(point => point[0] === x1 && point[1] === y1);
let additionalMobility;
switch (palacePos) {
case 0:
additionalMobility = (x1, y1, x2, y2) => x2 === x1 + 1 && y2 === y1 - 1;
break;
case 2:
additionalMobility = (x1, y1, x2, y2) => x2 === x1 - 1 && y2 === y1 - 1;
break;
case 4:
additionalMobility = ferz;
break;
case 6:
additionalMobility = (x1, y1, x2, y2) => x2 === x1 + 1 && y2 === y1 + 1;
break;
case 8:
additionalMobility = (x1, y1, x2, y2) => x2 === x1 - 1 && y2 === y1 + 1;
break;
default:
additionalMobility = () => false;
}
return ((wazir(x1, y1, x2, y2) || additionalMobility(x1, y1, x2, y2)) &&
ownPalace.some(point => point[0] === x2 && point[1] === y2));
};
}
// musketeer leopard
const musketeerLeopard = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return (xd === 1 || xd === 2) && (yd === 1 || yd === 2);
};
// musketeer hawk
const musketeerHawk = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return ((xd === 0 && (yd === 2 || yd === 3)) ||
(yd === 0 && (xd === 2 || xd === 3)) ||
(xd === yd && (xd === 2 || xd === 3)));
};
// musketeer elephant
const musketeerElephant = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return xd === 1 || yd === 1 || (xd === 2 && (yd === 0 || yd === 2)) || (xd === 0 && yd === 2);
};
// musketeer cannon
const musketeerCannon = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return xd < 3 && (yd < 2 || (yd === 2 && xd === 0));
};
// musketeer unicorn
const musketeerUnicorn = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return knight(x1, y1, x2, y2) || (xd === 1 && yd === 3) || (xd === 3 && yd === 1);
};
// musketeer dragon
const musketeerDragon = (x1, y1, x2, y2) => {
return knight(x1, y1, x2, y2) || queen(x1, y1, x2, y2);
};
// musketeer fortress
const musketeerFortress = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return (xd === yd && xd < 4) || (yd === 0 && xd === 2) || (yd === 2 && xd < 2);
};
// musketeer spider
const musketeerSpider = (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return xd < 3 && yd < 3 && !(xd === 1 && yd === 0) && !(xd === 0 && yd === 1);
};
// tori shogi goose (promoted swallow)
function toriGoose(color) {
return (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
return color === 'white'
? (xd === 2 && y2 === y1 + 2) || (xd === 0 && y2 === y1 - 2)
: (xd === 2 && y2 === y1 - 2) || (xd === 0 && y2 === y1 + 2);
};
}
// tori shogi left quail
function toriLeftQuail(color) {
return (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return color === 'white'
? (x2 === x1 && y2 > y1) || (xd === yd && x2 > x1 && y2 < y1) || (x2 === x1 - 1 && y2 === y1 - 1)
: (x2 === x1 && y2 < y1) || (xd === yd && x2 < x1 && y2 > y1) || (x2 === x1 + 1 && y2 === y1 + 1);
};
}
// tori shogi right quail
function toriRightQuail(color) {
return (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return color === 'white'
? (x2 === x1 && y2 > y1) || (xd === yd && x2 < x1 && y2 < y1) || (x2 === x1 + 1 && y2 === y1 - 1)
: (x2 === x1 && y2 < y1) || (xd === yd && x2 > x1 && y2 > y1) || (x2 === x1 - 1 && y2 === y1 + 1);
};
}
// tori shogi pheasant
function toriPheasant(color) {
return (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
return color === 'white'
? (x2 === x1 && y2 === y1 + 2) || (xd === 1 && y2 === y1 - 1)
: (x2 === x1 && y2 === y1 - 2) || (xd === 1 && y2 === y1 + 1);
};
}
// tori shogi crane
const toriCrane = (x1, y1, x2, y2) => {
return kingNoCastling(x1, y1, x2, y2) && y2 !== y1;
};
// tori shogi falcon
function toriFalcon(color) {
return (x1, y1, x2, y2) => {
return color === 'white'
? kingNoCastling(x1, y1, x2, y2) && !(x2 === x1 && y2 === y1 - 1)
: kingNoCastling(x1, y1, x2, y2) && !(x2 === x1 && y2 === y1 + 1);
};
}
// tori shogi eagle (promoted falcon)
function toriEagle(color) {
return (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return color === 'white'
? kingNoCastling(x1, y1, x2, y2) || (xd === yd && (y2 > y1 || (y2 < y1 && yd <= 2))) || (x2 === x1 && y2 < y1)
: kingNoCastling(x1, y1, x2, y2) || (xd === yd && (y2 < y1 || (y2 > y1 && yd <= 2))) || (x2 === x1 && y2 > y1);
};
}
// chak pawn
function pawnChak(color) {
return (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
return color === 'white' ? y2 >= y1 && y2 - y1 <= 1 && xd <= 1 : y1 >= y2 && y1 - y2 <= 1 && xd <= 1;
};
}
// chak warrior
function chakWarrior(color) {
return (x1, y1, x2, y2) => toriCrane(x1, y1, x2, y2) && (color === 'white' ? y2 >= 4 : y2 <= 4);
}
// chak divine king
function chakDivineKing(color) {
return (x1, y1, x2, y2) => {
const xd = diff(x1, x2);
const yd = diff(y1, y2);
return queen(x1, y1, x2, y2) && xd <= 2 && yd <= 2 && (color === 'white' ? y2 >= 4 : y2 <= 4);
};
}
// chennis king
function kingChennis(color) {
return (x1, y1, x2, y2) => kingNoCastling(x1, y1, x2, y2) && x2 >= 1 && x2 <= 5 && (color === 'white' ? y2 <= 3 : y2 >= 3);
}
// cannot premove
const noMove = () => false;
function premove(variant, chess960, bd) {
const mobility = builtinMobility$1(variant, chess960, bd);
return (boardState, key, canCastle) => {
const pos = key2pos(key);
return allPos(bd)
.filter(pos2 => (pos[0] !== pos2[0] || pos[1] !== pos2[1]) &&
mobility(boardState, key, canCastle)(pos[0], pos[1], pos2[0], pos2[1]))
.map(pos2key);
};
}
function builtinMobility$1(variant, chess960, bd) {
switch (variant) {
case 'ataxx':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
switch (role) {
case 'p-piece': // pawn
return pawnAtaxx;
default:
return noMove;
}
};
case 'xiangqi':
case 'manchu':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn (soldier)
return xiangqiPawn(color);
case 'c-piece': // cannon
case 'r-piece': // chariot
return rook;
case 'n-piece': // horse
return knight;
case 'b-piece': // elephant
return xiangqiElephant(color);
case 'a-piece': // advisor
return xiangqiAdvisor(color, bd);
case 'k-piece': // king
return xiangqiKing(color, bd);
case 'm-piece': // banner
return chancellor;
default:
return noMove;
}
};
case 'janggi':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn (soldier)
return janggiPawn(color, bd);
case 'c-piece': // cannon
case 'r-piece': // chariot
return janggiRook(bd);
case 'n-piece': // horse
return knight;
case 'b-piece': // elephant
return janggiElephant;
case 'a-piece': // advisor
case 'k-piece': // king
return janggiKing(color, bd);
default:
return noMove;
}
};
case 'minixiangqi':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn (soldier
return minixiangqiPawn(color);
case 'c-piece': // cannon
case 'r-piece': // chariot
return rook;
case 'n-piece': // horse
return knight;
case 'k-piece': // king
return xiangqiKing(color, bd);
default:
return noMove;
}
};
case 'shogi':
case 'minishogi':
case 'gorogoro':
case 'gorogoroplus':
case 'cannonshogi':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return variant === 'cannonshogi' ? minixiangqiPawn(color) : shogiPawn(color);
case 'l-piece': // lance
return shogiLance(color);
case 'n-piece': // knight
return shogiKnight(color);
case 'k-piece': // king
return kingNoCastling;
case 's-piece': // silver
return shogiSilver(color);
case 'g-piece': // gold
case 'pp-piece': // tokin
case 'pl-piece': // promoted lance
case 'pn-piece': // promoted knight
case 'ps-piece': // promoted silver
return shogiGold(color);
case 'b-piece': // bishop
case 'i-piece': // iron cannon
case 'c-piece': // copper cannon
return bishop;
case 'r-piece': // rook
case 'u-piece': // gold cannon
case 'a-piece': // silver cannon
return rook;
case 'pr-piece': // dragon (promoted rook)
return shogiDragon;
case 'pb-piece': // horse (promoted bishop), not to be confused with the knight
return shogiHorse;
case 'pu-piece': // promoted gold cannon
case 'pa-piece': // promoted silver cannon
return flyingOrthogonalCannon;
case 'pi-piece': // promoted iron cannon
case 'pc-piece': // promoted copper cannon
return flyingDiagonalCannon;
default:
return noMove;
}
};
case 'kyotoshogi':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'l-piece': // kyoto - lance-tokin
return shogiLance(color);
case 'pl-piece':
return shogiGold(color);
case 's-piece': // ginkaku - silver-bishop
return shogiSilver(color);
case 'ps-piece':
return bishop;
case 'n-piece': // kinkei gold-knight
return shogiKnight(color);
case 'pn-piece':
return shogiGold(color);
case 'p-piece': // hifu - rook-pawn
return shogiPawn(color);
case 'pp-piece':
return rook;
case 'k-piece': // king
return kingNoCastling;
default:
return noMove;
}
};
case 'dobutsu':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'c-piece': // chick
return shogiPawn(color);
case 'e-piece': // elephant
return ferz;
case 'g-piece': // giraffe
return wazir;
case 'l-piece': // lion
return kingNoCastling;
case 'pc-piece': // hen (promoted chick)
return shogiGold(color);
default:
return noMove;
}
};
case 'torishogi':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 's-piece': // swallow
return shogiPawn(color);
case 'ps-piece': // goose (promoted swallow)
return toriGoose(color);
case 'l-piece': // left quail
return toriLeftQuail(color);
case 'r-piece': // right quail
return toriRightQuail(color);
case 'p-piece': // pheasant (NOT pawn)
return toriPheasant(color);
case 'c-piece': // crane
return toriCrane;
case 'f-piece': // falcon
return toriFalcon(color);
case 'pf-piece': // eagle (promoted falcon)
return toriEagle(color);
case 'k-piece': // phoenix
return kingNoCastling;
default:
return noMove;
}
};
case 'makruk':
case 'makpong':
case 'sittuyin':
case 'cambodian':
case 'asean':
return (boardState, key, canCastle) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return variant === 'sittuyin' ? pawnSittuyin(boardState.pieces, color) : pawnNoDoubleStep(color);
case 'r-piece': // rook
return rook;
case 'n-piece': // knight
return knight;
case 'b-piece': // ASEAN khon
case 's-piece': // khon
return shogiSilver(color);
case 'q-piece': // ASEAN met
case 'f-piece': // Sittuyin ferz
case 'm-piece': // met
return variant === 'cambodian' ? ferzOuk(color) : ferz;
case 'k-piece': // king
return variant === 'cambodian' ? kingOuk(color, canCastle) : kingNoCastling;
default:
return noMove;
}
};
case 'shatranj':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return pawnNoDoubleStep(color);
case 'r-piece': // rook
return rook;
case 'n-piece': // knight
return knight;
case 'b-piece': // bishop
return elephant;
case 'q-piece': // queen
return ferz;
case 'k-piece': // king
return kingNoCastling;
default:
return noMove;
}
};
case 'grand':
case 'grandhouse':
return (boardState, key) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return pawnGrand(color);
case 'r-piece': // rook
return rook;
case 'n-piece': // knight
return knight;
case 'b-piece': // bishop
return bishop;
case 'q-piece': // queen
return queen;
case 'c-piece': // chancellor
return chancellor;
case 'a-piece': // archbishop
return archbishop;
case 'k-piece': // king
return kingNoCastling;
default:
return noMove;
}
};
case 'shako':
return (boardState, key, canCastle) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return pawnGrand(color);
case 'c-piece': // cannon
case 'r-piece': // rook
return rook;
case 'n-piece': // knight
return knight;
case 'b-piece': // bishop
return bishop;
case 'q-piece': // queen
return queen;
case 'e-piece': // elephant
return fersAlfil;
case 'k-piece': // king
return kingShako(color, rookFilesOfShako(boardState.pieces, color), canCastle);
default:
return noMove;
}
};
case 'shogun':
return (boardState, key, canCastle) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return pawn(color);
case 'pp-piece': // captain
return kingNoCastling;
case 'r-piece': // rook
return rook;
case 'pr-piece': // mortar
return chancellor;
case 'n-piece': // knight
return knight;
case 'pn-piece': // general
return centaur;
case 'b-piece': // bishop
return bishop;
case 'pb-piece': // archbishop
return archbishop;
case 'f-piece': // duchess
return ferz;
case 'pf-piece': // queen
return queen;
case 'k-piece': // king
return king(color, rookFilesOf(boardState.pieces, color), canCastle);
default:
return noMove;
}
};
case 'orda':
case 'khans':
case 'ordamirror':
return (boardState, key, canCastle) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return pawn(color);
case 'r-piece': // rook
return rook;
case 'n-piece': // knight
return knight;
case 'b-piece': // bishop
return bishop;
case 'q-piece': // queen
return queen;
case 'l-piece': // lancer
return chancellor;
case 'h-piece': // kheshig
case 't-piece': // khatun
return centaur;
case 'a-piece': // archer
return archbishop;
case 'y-piece': // yurt
return shogiSilver(color);
case 'f-piece': // falcon
return amazon;
case 's-piece': // scout
return scout(color);
case 'k-piece': // king
return king(color, rookFilesOf(boardState.pieces, color), canCastle);
default:
return noMove;
}
};
case 'synochess':
return (boardState, key, canCastle) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return pawn(color);
case 'c-piece': // cannon
case 'r-piece': // rook
return rook;
case 'n-piece': // knight
return knight;
case 'b-piece': // bishop
return bishop;
case 'q-piece': // queen
return queen;
case 's-piece': // soldier
return minixiangqiPawn(color);
case 'e-piece': // elephant
return fersAlfil;
case 'a-piece': // advisor
return kingNoCastling;
case 'k-piece': // king
return king(color, rookFilesOf(boardState.pieces, color), canCastle && color === 'white');
default:
return noMove;
}
};
case 'musketeer':
return (boardState, key, canCastle) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return pawn(color);
case 'r-piece': // rook
return rook;
case 'n-piece': // knight
return knight;
case 'b-piece': // bishop
return bishop;
case 'q-piece': // queen
return queen;
case 'l-piece': // leopard
return musketeerLeopard;
case 'o-piece': // cannon
return musketeerCannon;
case 'u-piece': // unicorn
return musketeerUnicorn;
case 'd-piece': // dragon
return musketeerDragon;
case 'c-piece': // chancellor
return chancellor;
case 'a-piece': // archbishop
return archbishop;
case 'e-piece': // elephant
return musketeerElephant;
case 'h-piece': // hawk
return musketeerHawk; // hawk
case 'f-piece': // fortress
return musketeerFortress;
case 's-piece': // spider
return musketeerSpider;
case 'k-piece':
return king(color, rookFilesOf(boardState.pieces, color), canCastle);
default:
return noMove;
}
};
case 'hoppelpoppel':
return (boardState, key, canCastle) => {
const piece = boardState.pieces.get(key);
const role = piece.role;
const color = piece.color;
switch (role) {
case 'p-piece': // pawn
return pawn(color);
case 'r-piece': // rook
return rook;
case 'n-piece': // knight (takes like bishop)
case 'b-piece': // bishop (takes like knight)
return archbishop;
case 'q-piece': // queen
return queen;
case 'k-piece': // king
return king(co