UNPKG

chessgroundx

Version:

Extended lichess.org Chess UI

1,159 lines (1,156 loc) 152 kB
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