UNPKG

@zoendev/react-chessboard-v2

Version:

The React Chessboard Library used at ChessOpenings.co.uk. Inspired and adapted from the unmaintained Chessboard.jsx.

1,257 lines (1,106 loc) 305 kB
import { jsx, jsxs, Fragment } from 'react/jsx-runtime'; import { useState, useEffect, createContext, forwardRef, useRef, useImperativeHandle, useContext, memo, useLayoutEffect, useCallback, useMemo, isValidElement, cloneElement, Fragment as Fragment$1 } from 'react'; /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol */ function __rest(s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; const COLUMNS = "abcdefgh".split(""); const START_POSITION_OBJECT = { a8: "bR", b8: "bN", c8: "bB", d8: "bQ", e8: "bK", f8: "bB", g8: "bN", h8: "bR", a7: "bP", b7: "bP", c7: "bP", d7: "bP", e7: "bP", f7: "bP", g7: "bP", h7: "bP", a2: "wP", b2: "wP", c2: "wP", d2: "wP", e2: "wP", f2: "wP", g2: "wP", h2: "wP", a1: "wR", b1: "wN", c1: "wB", d1: "wQ", e1: "wK", f1: "wB", g1: "wN", h1: "wR", }; // https://commons.wikimedia.org/wiki/Category:SVG_chess_pieces // By en:User:Cburnett - Own work // This W3C - unspecified vector image was created with Inkscape., CC BY - SA 3.0, https://commons.wikimedia.org/w/index.php?curid=1499810 const defaultPieces = { wP: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsx("path", { d: "m 22.5,9 c -2.21,0 -4,1.79 -4,4 0,0.89 0.29,1.71 0.78,2.38 C 17.33,16.5 16,18.59 16,21 c 0,2.03 0.94,3.84 2.41,5.03 C 15.41,27.09 11,31.58 11,39.5 H 34 C 34,31.58 29.59,27.09 26.59,26.03 28.06,24.84 29,23.03 29,21 29,18.59 27.67,16.5 25.72,15.38 26.21,14.71 26.5,13.89 26.5,13 c 0,-2.21 -1.79,-4 -4,-4 z", style: { opacity: "1", fill: "#ffffff", fillOpacity: "1", fillRule: "nonzero", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "miter", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }) }))), wR: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { opacity: "1", fill: "#ffffff", fillOpacity: "1", fillRule: "evenodd", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }, { children: [jsx("path", { d: "M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z ", style: { strokeLinecap: "butt" } }), jsx("path", { d: "M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z ", style: { strokeLinecap: "butt" } }), jsx("path", { d: "M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14", style: { strokeLinecap: "butt" } }), jsx("path", { d: "M 34,14 L 31,17 L 14,17 L 11,14" }), jsx("path", { d: "M 31,17 L 31,29.5 L 14,29.5 L 14,17", style: { strokeLinecap: "butt", strokeLinejoin: "miter" } }), jsx("path", { d: "M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" }), jsx("path", { d: "M 11,14 L 34,14", style: { fill: "none", stroke: "#000000", strokeLinejoin: "miter" } })] })) }))), wN: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { opacity: "1", fill: "none", fillOpacity: "1", fillRule: "evenodd", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }, { children: [jsx("path", { d: "M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18", style: { fill: "#ffffff", stroke: "#000000" } }), jsx("path", { d: "M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10", style: { fill: "#ffffff", stroke: "#000000" } }), jsx("path", { d: "M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z", style: { fill: "#000000", stroke: "#000000" } }), jsx("path", { d: "M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z", transform: "matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)", style: { fill: "#000000", stroke: "#000000" } })] })) }))), wB: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { opacity: "1", fill: "none", fillRule: "evenodd", fillOpacity: "1", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }, { children: [jsxs("g", Object.assign({ style: { fill: "#ffffff", stroke: "#000000", strokeLinecap: "butt" } }, { children: [jsx("path", { d: "M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.65,38.99 6.68,38.97 6,38 C 7.35,36.54 9,36 9,36 z" }), jsx("path", { d: "M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" }), jsx("path", { d: "M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" })] })), jsx("path", { d: "M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18", style: { fill: "none", stroke: "#000000", strokeLinejoin: "miter" } })] })) }))), wQ: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { fill: "#ffffff", stroke: "#000000", strokeWidth: "1.5", strokeLinejoin: "round", } }, { children: [jsx("path", { d: "M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z" }), jsx("path", { d: "M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 11,36 11,36 C 9.5,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z" }), jsx("path", { d: "M 11.5,30 C 15,29 30,29 33.5,30", style: { fill: "none" } }), jsx("path", { d: "M 12,33.5 C 18,32.5 27,32.5 33,33.5", style: { fill: "none" } }), jsx("circle", { cx: "6", cy: "12", r: "2" }), jsx("circle", { cx: "14", cy: "9", r: "2" }), jsx("circle", { cx: "22.5", cy: "8", r: "2" }), jsx("circle", { cx: "31", cy: "9", r: "2" }), jsx("circle", { cx: "39", cy: "12", r: "2" })] })) }))), wK: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { fill: "none", fillOpacity: "1", fillRule: "evenodd", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }, { children: [jsx("path", { d: "M 22.5,11.63 L 22.5,6", style: { fill: "none", stroke: "#000000", strokeLinejoin: "miter" } }), jsx("path", { d: "M 20,8 L 25,8", style: { fill: "none", stroke: "#000000", strokeLinejoin: "miter" } }), jsx("path", { d: "M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25", style: { fill: "#ffffff", stroke: "#000000", strokeLinecap: "butt", strokeLinejoin: "miter", } }), jsx("path", { d: "M 12.5,37 C 18,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 20,16 10.5,13 6.5,19.5 C 3.5,25.5 12.5,30 12.5,30 L 12.5,37", style: { fill: "#ffffff", stroke: "#000000" } }), jsx("path", { d: "M 12.5,30 C 18,27 27,27 32.5,30", style: { fill: "none", stroke: "#000000" } }), jsx("path", { d: "M 12.5,33.5 C 18,30.5 27,30.5 32.5,33.5", style: { fill: "none", stroke: "#000000" } }), jsx("path", { d: "M 12.5,37 C 18,34 27,34 32.5,37", style: { fill: "none", stroke: "#000000" } })] })) }))), bP: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsx("path", { d: "m 22.5,9 c -2.21,0 -4,1.79 -4,4 0,0.89 0.29,1.71 0.78,2.38 C 17.33,16.5 16,18.59 16,21 c 0,2.03 0.94,3.84 2.41,5.03 C 15.41,27.09 11,31.58 11,39.5 H 34 C 34,31.58 29.59,27.09 26.59,26.03 28.06,24.84 29,23.03 29,21 29,18.59 27.67,16.5 25.72,15.38 26.21,14.71 26.5,13.89 26.5,13 c 0,-2.21 -1.79,-4 -4,-4 z", style: { opacity: "1", fill: "#000000", fillOpacity: "1", fillRule: "nonzero", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "miter", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }) }))), bR: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { opacity: "1", fill: "#000000", fillOpacity: "1", fillRule: "evenodd", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }, { children: [jsx("path", { d: "M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z ", style: { strokeLinecap: "butt" } }), jsx("path", { d: "M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z ", style: { strokeLinecap: "butt" } }), jsx("path", { d: "M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z ", style: { strokeLinecap: "butt" } }), jsx("path", { d: "M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z ", style: { strokeLinecap: "butt", strokeLinejoin: "miter" } }), jsx("path", { d: "M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z ", style: { strokeLinecap: "butt" } }), jsx("path", { d: "M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z ", style: { strokeLinecap: "butt" } }), jsx("path", { d: "M 12,35.5 L 33,35.5 L 33,35.5", style: { fill: "none", stroke: "#ffffff", strokeWidth: "1", strokeLinejoin: "miter", } }), jsx("path", { d: "M 13,31.5 L 32,31.5", style: { fill: "none", stroke: "#ffffff", strokeWidth: "1", strokeLinejoin: "miter", } }), jsx("path", { d: "M 14,29.5 L 31,29.5", style: { fill: "none", stroke: "#ffffff", strokeWidth: "1", strokeLinejoin: "miter", } }), jsx("path", { d: "M 14,16.5 L 31,16.5", style: { fill: "none", stroke: "#ffffff", strokeWidth: "1", strokeLinejoin: "miter", } }), jsx("path", { d: "M 11,14 L 34,14", style: { fill: "none", stroke: "#ffffff", strokeWidth: "1", strokeLinejoin: "miter", } })] })) }))), bN: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { opacity: "1", fill: "none", fillOpacity: "1", fillRule: "evenodd", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }, { children: [jsx("path", { d: "M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18", style: { fill: "#000000", stroke: "#000000" } }), jsx("path", { d: "M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10", style: { fill: "#000000", stroke: "#000000" } }), jsx("path", { d: "M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z", style: { fill: "#ffffff", stroke: "#ffffff" } }), jsx("path", { d: "M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z", transform: "matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)", style: { fill: "#ffffff", stroke: "#ffffff" } }), jsx("path", { d: "M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z ", style: { fill: "#ffffff", stroke: "none" } })] })) }))), bB: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { opacity: "1", fill: "none", fillRule: "evenodd", fillOpacity: "1", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }, { children: [jsxs("g", Object.assign({ style: { fill: "#000000", stroke: "#000000", strokeLinecap: "butt" } }, { children: [jsx("path", { d: "M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.65,38.99 6.68,38.97 6,38 C 7.35,36.54 9,36 9,36 z" }), jsx("path", { d: "M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z" }), jsx("path", { d: "M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z" })] })), jsx("path", { d: "M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18", style: { fill: "none", stroke: "#ffffff", strokeLinejoin: "miter" } })] })) }))), bQ: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { fill: "#000000", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", } }, { children: [jsx("path", { d: "M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z", style: { strokeLinecap: "butt", fill: "#000000" } }), jsx("path", { d: "m 9,26 c 0,2 1.5,2 2.5,4 1,1.5 1,1 0.5,3.5 -1.5,1 -1,2.5 -1,2.5 -1.5,1.5 0,2.5 0,2.5 6.5,1 16.5,1 23,0 0,0 1.5,-1 0,-2.5 0,0 0.5,-1.5 -1,-2.5 -0.5,-2.5 -0.5,-2 0.5,-3.5 1,-2 2.5,-2 2.5,-4 -8.5,-1.5 -18.5,-1.5 -27,0 z" }), jsx("path", { d: "M 11.5,30 C 15,29 30,29 33.5,30" }), jsx("path", { d: "m 12,33.5 c 6,-1 15,-1 21,0" }), jsx("circle", { cx: "6", cy: "12", r: "2" }), jsx("circle", { cx: "14", cy: "9", r: "2" }), jsx("circle", { cx: "22.5", cy: "8", r: "2" }), jsx("circle", { cx: "31", cy: "9", r: "2" }), jsx("circle", { cx: "39", cy: "12", r: "2" }), jsx("path", { d: "M 11,38.5 A 35,35 1 0 0 34,38.5", style: { fill: "none", stroke: "#000000", strokeLinecap: "butt" } }), jsxs("g", Object.assign({ style: { fill: "none", stroke: "#ffffff" } }, { children: [jsx("path", { d: "M 11,29 A 35,35 1 0 1 34,29" }), jsx("path", { d: "M 12.5,31.5 L 32.5,31.5" }), jsx("path", { d: "M 11.5,34.5 A 35,35 1 0 0 33.5,34.5" }), jsx("path", { d: "M 10.5,37.5 A 35,35 1 0 0 34.5,37.5" })] }))] })) }))), bK: (jsx("svg", Object.assign({ xmlns: "http://www.w3.org/2000/svg", version: "1.1", width: "45", height: "45" }, { children: jsxs("g", Object.assign({ style: { fill: "none", fillOpacity: "1", fillRule: "evenodd", stroke: "#000000", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", strokeMiterlimit: "4", strokeDasharray: "none", strokeOpacity: "1", } }, { children: [jsx("path", { d: "M 22.5,11.63 L 22.5,6", style: { fill: "none", stroke: "#000000", strokeLinejoin: "miter" }, id: "path6570" }), jsx("path", { d: "M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25", style: { fill: "#000000", fillOpacity: "1", strokeLinecap: "butt", strokeLinejoin: "miter", } }), jsx("path", { d: "M 12.5,37 C 18,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 20,16 10.5,13 6.5,19.5 C 3.5,25.5 12.5,30 12.5,30 L 12.5,37", style: { fill: "#000000", stroke: "#000000" } }), jsx("path", { d: "M 20,8 L 25,8", style: { fill: "none", stroke: "#000000", strokeLinejoin: "miter" } }), jsx("path", { d: "M 32,29.5 C 32,29.5 40.5,25.5 38.03,19.85 C 34.15,14 25,18 22.5,24.5 L 22.5,26.6 L 22.5,24.5 C 20,18 10.85,14 6.97,19.85 C 4.5,25.5 13,29.5 13,29.5", style: { fill: "none", stroke: "#ffffff" } }), jsx("path", { d: "M 12.5,30 C 18,27 27,27 32.5,30 M 12.5,33.5 C 18,30.5 27,30.5 32.5,33.5 M 12.5,37 C 18,34 27,34 32.5,37", style: { fill: "none", stroke: "#ffffff" } })] })) }))), }; function getDefaultExportFromCjs (x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; } var chess = {}; /* * Copyright (c) 2021, Jeff Hlywa (jhlywa@gmail.com) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * *----------------------------------------------------------------------------*/ (function (exports) { var Chess = function (fen) { var BLACK = 'b'; var WHITE = 'w'; var EMPTY = -1; var PAWN = 'p'; var KNIGHT = 'n'; var BISHOP = 'b'; var ROOK = 'r'; var QUEEN = 'q'; var KING = 'k'; var SYMBOLS = 'pnbrqkPNBRQK'; var DEFAULT_POSITION = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'; var TERMINATION_MARKERS = ['1-0', '0-1', '1/2-1/2', '*']; var PAWN_OFFSETS = { b: [16, 32, 17, 15], w: [-16, -32, -17, -15], }; var PIECE_OFFSETS = { n: [-18, -33, -31, -14, 18, 33, 31, 14], b: [-17, -15, 17, 15], r: [-16, 1, 16, -1], q: [-17, -16, -15, 1, 17, 16, 15, -1], k: [-17, -16, -15, 1, 17, 16, 15, -1], }; // prettier-ignore var ATTACKS = [ 20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0,20, 0, 0,20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0,20, 0, 0, 0, 0,20, 0, 0, 0, 0, 24, 0, 0, 0, 0,20, 0, 0, 0, 0, 0, 0,20, 0, 0, 0, 24, 0, 0, 0,20, 0, 0, 0, 0, 0, 0, 0, 0,20, 0, 0, 24, 0, 0,20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20, 2, 24, 2,20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0, 24,24,24,24,24,24,56, 0, 56,24,24,24,24,24,24, 0, 0, 0, 0, 0, 0, 2,53, 56, 53, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20, 2, 24, 2,20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,20, 0, 0, 24, 0, 0,20, 0, 0, 0, 0, 0, 0, 0, 0,20, 0, 0, 0, 24, 0, 0, 0,20, 0, 0, 0, 0, 0, 0,20, 0, 0, 0, 0, 24, 0, 0, 0, 0,20, 0, 0, 0, 0,20, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0,20, 0, 0, 20, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0,20 ]; // prettier-ignore var RAYS = [ 17, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 15, 0, 0, 17, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 17, 0, 0, 0, 0, 16, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 16, 0, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 16, 0, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 16, 0, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 16, 15, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, -1, -1, -1,-1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0,-15,-16,-17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-15, 0,-16, 0,-17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-15, 0, 0,-16, 0, 0,-17, 0, 0, 0, 0, 0, 0, 0, 0,-15, 0, 0, 0,-16, 0, 0, 0,-17, 0, 0, 0, 0, 0, 0,-15, 0, 0, 0, 0,-16, 0, 0, 0, 0,-17, 0, 0, 0, 0,-15, 0, 0, 0, 0, 0,-16, 0, 0, 0, 0, 0,-17, 0, 0, -15, 0, 0, 0, 0, 0, 0,-16, 0, 0, 0, 0, 0, 0,-17 ]; var SHIFTS = { p: 0, n: 1, b: 2, r: 3, q: 4, k: 5 }; var FLAGS = { NORMAL: 'n', CAPTURE: 'c', BIG_PAWN: 'b', EP_CAPTURE: 'e', PROMOTION: 'p', KSIDE_CASTLE: 'k', QSIDE_CASTLE: 'q', }; var BITS = { NORMAL: 1, CAPTURE: 2, BIG_PAWN: 4, EP_CAPTURE: 8, PROMOTION: 16, KSIDE_CASTLE: 32, QSIDE_CASTLE: 64, }; var RANK_1 = 7; var RANK_2 = 6; var RANK_7 = 1; var RANK_8 = 0; // prettier-ignore var SQUARES = { a8: 0, b8: 1, c8: 2, d8: 3, e8: 4, f8: 5, g8: 6, h8: 7, a7: 16, b7: 17, c7: 18, d7: 19, e7: 20, f7: 21, g7: 22, h7: 23, a6: 32, b6: 33, c6: 34, d6: 35, e6: 36, f6: 37, g6: 38, h6: 39, a5: 48, b5: 49, c5: 50, d5: 51, e5: 52, f5: 53, g5: 54, h5: 55, a4: 64, b4: 65, c4: 66, d4: 67, e4: 68, f4: 69, g4: 70, h4: 71, a3: 80, b3: 81, c3: 82, d3: 83, e3: 84, f3: 85, g3: 86, h3: 87, a2: 96, b2: 97, c2: 98, d2: 99, e2: 100, f2: 101, g2: 102, h2: 103, a1: 112, b1: 113, c1: 114, d1: 115, e1: 116, f1: 117, g1: 118, h1: 119 }; var ROOKS = { w: [ { square: SQUARES.a1, flag: BITS.QSIDE_CASTLE }, { square: SQUARES.h1, flag: BITS.KSIDE_CASTLE }, ], b: [ { square: SQUARES.a8, flag: BITS.QSIDE_CASTLE }, { square: SQUARES.h8, flag: BITS.KSIDE_CASTLE }, ], }; var board = new Array(128); var kings = { w: EMPTY, b: EMPTY }; var turn = WHITE; var castling = { w: 0, b: 0 }; var ep_square = EMPTY; var half_moves = 0; var move_number = 1; var history = []; var header = {}; var comments = {}; /* if the user passes in a fen string, load it, else default to * starting position */ if (typeof fen === 'undefined') { load(DEFAULT_POSITION); } else { load(fen); } function clear(keep_headers) { if (typeof keep_headers === 'undefined') { keep_headers = false; } board = new Array(128); kings = { w: EMPTY, b: EMPTY }; turn = WHITE; castling = { w: 0, b: 0 }; ep_square = EMPTY; half_moves = 0; move_number = 1; history = []; if (!keep_headers) header = {}; comments = {}; update_setup(generate_fen()); } function prune_comments() { var reversed_history = []; var current_comments = {}; var copy_comment = function (fen) { if (fen in comments) { current_comments[fen] = comments[fen]; } }; while (history.length > 0) { reversed_history.push(undo_move()); } copy_comment(generate_fen()); while (reversed_history.length > 0) { make_move(reversed_history.pop()); copy_comment(generate_fen()); } comments = current_comments; } function reset() { load(DEFAULT_POSITION); } function load(fen, keep_headers) { if (typeof keep_headers === 'undefined') { keep_headers = false; } var tokens = fen.split(/\s+/); var position = tokens[0]; var square = 0; if (!validate_fen(fen).valid) { return false } clear(keep_headers); for (var i = 0; i < position.length; i++) { var piece = position.charAt(i); if (piece === '/') { square += 8; } else if (is_digit(piece)) { square += parseInt(piece, 10); } else { var color = piece < 'a' ? WHITE : BLACK; put({ type: piece.toLowerCase(), color: color }, algebraic(square)); square++; } } turn = tokens[1]; if (tokens[2].indexOf('K') > -1) { castling.w |= BITS.KSIDE_CASTLE; } if (tokens[2].indexOf('Q') > -1) { castling.w |= BITS.QSIDE_CASTLE; } if (tokens[2].indexOf('k') > -1) { castling.b |= BITS.KSIDE_CASTLE; } if (tokens[2].indexOf('q') > -1) { castling.b |= BITS.QSIDE_CASTLE; } ep_square = tokens[3] === '-' ? EMPTY : SQUARES[tokens[3]]; half_moves = parseInt(tokens[4], 10); move_number = parseInt(tokens[5], 10); update_setup(generate_fen()); return true } /* TODO: this function is pretty much crap - it validates structure but * completely ignores content (e.g. doesn't verify that each side has a king) * ... we should rewrite this, and ditch the silly error_number field while * we're at it */ function validate_fen(fen) { var errors = { 0: 'No errors.', 1: 'FEN string must contain six space-delimited fields.', 2: '6th field (move number) must be a positive integer.', 3: '5th field (half move counter) must be a non-negative integer.', 4: '4th field (en-passant square) is invalid.', 5: '3rd field (castling availability) is invalid.', 6: '2nd field (side to move) is invalid.', 7: "1st field (piece positions) does not contain 8 '/'-delimited rows.", 8: '1st field (piece positions) is invalid [consecutive numbers].', 9: '1st field (piece positions) is invalid [invalid piece].', 10: '1st field (piece positions) is invalid [row too large].', 11: 'Illegal en-passant square', }; /* 1st criterion: 6 space-seperated fields? */ var tokens = fen.split(/\s+/); if (tokens.length !== 6) { return { valid: false, error_number: 1, error: errors[1] } } /* 2nd criterion: move number field is a integer value > 0? */ if (isNaN(tokens[5]) || parseInt(tokens[5], 10) <= 0) { return { valid: false, error_number: 2, error: errors[2] } } /* 3rd criterion: half move counter is an integer >= 0? */ if (isNaN(tokens[4]) || parseInt(tokens[4], 10) < 0) { return { valid: false, error_number: 3, error: errors[3] } } /* 4th criterion: 4th field is a valid e.p.-string? */ if (!/^(-|[abcdefgh][36])$/.test(tokens[3])) { return { valid: false, error_number: 4, error: errors[4] } } /* 5th criterion: 3th field is a valid castle-string? */ if (!/^(KQ?k?q?|Qk?q?|kq?|q|-)$/.test(tokens[2])) { return { valid: false, error_number: 5, error: errors[5] } } /* 6th criterion: 2nd field is "w" (white) or "b" (black)? */ if (!/^(w|b)$/.test(tokens[1])) { return { valid: false, error_number: 6, error: errors[6] } } /* 7th criterion: 1st field contains 8 rows? */ var rows = tokens[0].split('/'); if (rows.length !== 8) { return { valid: false, error_number: 7, error: errors[7] } } /* 8th criterion: every row is valid? */ for (var i = 0; i < rows.length; i++) { /* check for right sum of fields AND not two numbers in succession */ var sum_fields = 0; var previous_was_number = false; for (var k = 0; k < rows[i].length; k++) { if (!isNaN(rows[i][k])) { if (previous_was_number) { return { valid: false, error_number: 8, error: errors[8] } } sum_fields += parseInt(rows[i][k], 10); previous_was_number = true; } else { if (!/^[prnbqkPRNBQK]$/.test(rows[i][k])) { return { valid: false, error_number: 9, error: errors[9] } } sum_fields += 1; previous_was_number = false; } } if (sum_fields !== 8) { return { valid: false, error_number: 10, error: errors[10] } } } if ( (tokens[3][1] == '3' && tokens[1] == 'w') || (tokens[3][1] == '6' && tokens[1] == 'b') ) { return { valid: false, error_number: 11, error: errors[11] } } /* everything's okay! */ return { valid: true, error_number: 0, error: errors[0] } } function generate_fen() { var empty = 0; var fen = ''; for (var i = SQUARES.a8; i <= SQUARES.h1; i++) { if (board[i] == null) { empty++; } else { if (empty > 0) { fen += empty; empty = 0; } var color = board[i].color; var piece = board[i].type; fen += color === WHITE ? piece.toUpperCase() : piece.toLowerCase(); } if ((i + 1) & 0x88) { if (empty > 0) { fen += empty; } if (i !== SQUARES.h1) { fen += '/'; } empty = 0; i += 8; } } var cflags = ''; if (castling[WHITE] & BITS.KSIDE_CASTLE) { cflags += 'K'; } if (castling[WHITE] & BITS.QSIDE_CASTLE) { cflags += 'Q'; } if (castling[BLACK] & BITS.KSIDE_CASTLE) { cflags += 'k'; } if (castling[BLACK] & BITS.QSIDE_CASTLE) { cflags += 'q'; } /* do we have an empty castling flag? */ cflags = cflags || '-'; var epflags = ep_square === EMPTY ? '-' : algebraic(ep_square); return [fen, turn, cflags, epflags, half_moves, move_number].join(' ') } function set_header(args) { for (var i = 0; i < args.length; i += 2) { if (typeof args[i] === 'string' && typeof args[i + 1] === 'string') { header[args[i]] = args[i + 1]; } } return header } /* called when the initial board setup is changed with put() or remove(). * modifies the SetUp and FEN properties of the header object. if the FEN is * equal to the default position, the SetUp and FEN are deleted * the setup is only updated if history.length is zero, ie moves haven't been * made. */ function update_setup(fen) { if (history.length > 0) return if (fen !== DEFAULT_POSITION) { header['SetUp'] = '1'; header['FEN'] = fen; } else { delete header['SetUp']; delete header['FEN']; } } function get(square) { var piece = board[SQUARES[square]]; return piece ? { type: piece.type, color: piece.color } : null } function put(piece, square) { /* check for valid piece object */ if (!('type' in piece && 'color' in piece)) { return false } /* check for piece */ if (SYMBOLS.indexOf(piece.type.toLowerCase()) === -1) { return false } /* check for valid square */ if (!(square in SQUARES)) { return false } var sq = SQUARES[square]; /* don't let the user place more than one king */ if ( piece.type == KING && !(kings[piece.color] == EMPTY || kings[piece.color] == sq) ) { return false } board[sq] = { type: piece.type, color: piece.color }; if (piece.type === KING) { kings[piece.color] = sq; } update_setup(generate_fen()); return true } function remove(square) { var piece = get(square); board[SQUARES[square]] = null; if (piece && piece.type === KING) { kings[piece.color] = EMPTY; } update_setup(generate_fen()); return piece } function build_move(board, from, to, flags, promotion) { var move = { color: turn, from: from, to: to, flags: flags, piece: board[from].type, }; if (promotion) { move.flags |= BITS.PROMOTION; move.promotion = promotion; } if (board[to]) { move.captured = board[to].type; } else if (flags & BITS.EP_CAPTURE) { move.captured = PAWN; } return move } function generate_moves(options) { function add_move(board, moves, from, to, flags) { /* if pawn promotion */ if ( board[from].type === PAWN && (rank(to) === RANK_8 || rank(to) === RANK_1) ) { var pieces = [QUEEN, ROOK, BISHOP, KNIGHT]; for (var i = 0, len = pieces.length; i < len; i++) { moves.push(build_move(board, from, to, flags, pieces[i])); } } else { moves.push(build_move(board, from, to, flags)); } } var moves = []; var us = turn; var them = swap_color(us); var second_rank = { b: RANK_7, w: RANK_2 }; var first_sq = SQUARES.a8; var last_sq = SQUARES.h1; var single_square = false; /* do we want legal moves? */ var legal = typeof options !== 'undefined' && 'legal' in options ? options.legal : true; var piece_type = typeof options !== 'undefined' && 'piece' in options && typeof options.piece === 'string' ? options.piece.toLowerCase() : true; /* are we generating moves for a single square? */ if (typeof options !== 'undefined' && 'square' in options) { if (options.square in SQUARES) { first_sq = last_sq = SQUARES[options.square]; single_square = true; } else { /* invalid square */ return [] } } for (var i = first_sq; i <= last_sq; i++) { /* did we run off the end of the board */ if (i & 0x88) { i += 7; continue } var piece = board[i]; if (piece == null || piece.color !== us) { continue } if (piece.type === PAWN && (piece_type === true || piece_type === PAWN)) { /* single square, non-capturing */ var square = i + PAWN_OFFSETS[us][0]; if (board[square] == null) { add_move(board, moves, i, square, BITS.NORMAL); /* double square */ var square = i + PAWN_OFFSETS[us][1]; if (second_rank[us] === rank(i) && board[square] == null) { add_move(board, moves, i, square, BITS.BIG_PAWN); } } /* pawn captures */ for (j = 2; j < 4; j++) { var square = i + PAWN_OFFSETS[us][j]; if (square & 0x88) continue if (board[square] != null && board[square].color === them) { add_move(board, moves, i, square, BITS.CAPTURE); } else if (square === ep_square) { add_move(board, moves, i, ep_square, BITS.EP_CAPTURE); } } } else if (piece_type === true || piece_type === piece.type) { for (var j = 0, len = PIECE_OFFSETS[piece.type].length; j < len; j++) { var offset = PIECE_OFFSETS[piece.type][j]; var square = i; while (true) { square += offset; if (square & 0x88) break if (board[square] == null) { add_move(board, moves, i, square, BITS.NORMAL); } else { if (board[square].color === us) break add_move(board, moves, i, square, BITS.CAPTURE); break } /* break, if knight or king */ if (piece.type === 'n' || piece.type === 'k') break } } } } /* check for castling if: a) we're generating all moves, or b) we're doing * single square move generation on the king's square */ if (piece_type === true || piece_type === KING) { if (!single_square || last_sq === kings[us]) { /* king-side castling */ if (castling[us] & BITS.KSIDE_CASTLE) { var castling_from = kings[us]; var castling_to = castling_from + 2; if ( board[castling_from + 1] == null && board[castling_to] == null && !attacked(them, kings[us]) && !attacked(them, castling_from + 1) && !attacked(them, castling_to) ) { add_move(board, moves, kings[us], castling_to, BITS.KSIDE_CASTLE); } } /* queen-side castling */ if (castling[us] & BITS.QSIDE_CASTLE) { var castling_from = kings[us]; var castling_to = castling_from - 2; if ( board[castling_from - 1] == null && board[castling_from - 2] == null && board[castling_from - 3] == null && !attacked(them, kings[us]) && !attacked(them, castling_from - 1) && !attacked(them, castling_to) ) { add_move(board, moves, kings[us], castling_to, BITS.QSIDE_CASTLE); } } } } /* return all pseudo-legal moves (this includes moves that allow the king * to be captured) */ if (!legal) { return moves } /* filter out illegal moves */ var legal_moves = []; for (var i = 0, len = moves.length; i < len; i++) { make_move(moves[i]); if (!king_attacked(us)) { legal_moves.push(moves[i]); } undo_move(); } return legal_moves } /* convert a move from 0x88 coordinates to Standard Algebraic Notation * (SAN) * * @param {boolean} sloppy Use the sloppy SAN generator to work around over * disambiguation bugs in Fritz and Chessbase. See below: * * r1bqkbnr/ppp2ppp/2n5/1B1pP3/4P3/8/PPPP2PP/RNBQK1NR b KQkq - 2 4 * 4. ... Nge7 is overly disambiguated because the knight on c6 is pinned * 4. ... Ne7 is technically the valid SAN */ function move_to_san(move, moves) { var output = ''; if (move.flags & BITS.KSIDE_CASTLE) { output = 'O-O'; } else if (move.flags & BITS.QSIDE_CASTLE) { output = 'O-O-O'; } else { if (move.piece !== PAWN) { var disambiguator = get_disambiguator(move, moves); output += move.piece.toUpperCase() + disambiguator; } if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) { if (move.piece === PAWN) { output += algebraic(move.from)[0]; } output += 'x'; } output += algebraic(move.to); if (move.flags & BITS.PROMOTION) { output += '=' + move.promotion.toUpperCase(); } } make_move(move); if (in_check()) { if (in_checkmate()) { output += '#'; } else { output += '+'; } } undo_move(); return output } // parses all of the decorators out of a SAN string function stripped_san(move) { return move.replace(/=/, '').replace(/[+#]?[?!]*$/, '') } function attacked(color, square) { for (var i = SQUARES.a8; i <= SQUARES.h1; i++) { /* did we run off the end of the board */ if (i & 0x88) { i += 7; continue } /* if empty square or wrong color */ if (board[i] == null || board[i].color !== color) continue var piece = board[i]; var difference = i - square; var index = difference + 119; if (ATTACKS[index] & (1 << SHIFTS[piece.type])) { if (piece.type === PAWN) { if (difference > 0) { if (piece.color === WHITE) return true } else { if (piece.color === BLACK) return true } continue } /* if the piece is a knight or a king */ if (piece.type === 'n' || piece.type === 'k') return true var offset = RAYS[index]; var j = i + offset; var blocked = false; while (j !== square) { if (board[j] != null) { blocked = true; break } j += offset; } if (!blocked) return true } } return false } function king_attacked(color) { return attacked(swap_color(color), kings[color]) } function in_check() { return king_attacked(turn) } function in_checkmate() { return in_check() && generate_moves().length === 0 } function in_stalemate() { return !in_check() && generate_moves().length === 0 } function insufficient_material() { var pieces = {}; var bishops = []; var num_pieces = 0; var sq_color = 0; for (var i = SQUARES.a8; i <= SQUARES.h1; i++) { sq_color = (sq_color + 1) % 2; if (i & 0x88) { i += 7; continue } var piece = board[i]; if (piece) { pieces[piece.type] = piece.type in pieces ? pieces[piece.type] + 1 : 1; if (piece.type === BISHOP) { bishops.push(sq_color); } num_pieces++; } } /* k vs. k */ if (num_pieces === 2) { return true } else if ( /* k vs. kn .... or .... k vs. kb */ num_pieces === 3 && (pieces[BISHOP] === 1 || pieces[KNIGHT] === 1) ) { return true } else if (num_pieces === pieces[BISHOP] + 2) { /* kb vs. kb where any number of bishops are all on the same color */ var sum = 0; var len = bishops.length; for (var i = 0; i < len; i++) { sum += bishops[i]; } if (sum === 0 || sum === len) { return true } } return false } function in_threefold_repetition() { /* TODO: while this function is fine for casual use, a better * implementation would use a Zobrist key (instead of FEN). the * Zobrist key would be maintained in the make_move/undo_move functions, * avoiding the costly that we do below. */ var moves = []; var positions = {}; var repetition = false; while (true) { var move = undo_move(); if (!move) break moves.push(move); } while (true) { /* remove the last two fields in the FEN string, they're not needed * when checking for draw by rep */ var fen = generate_fen().split(' ').slice(0, 4).join(' '); /* has the position occurred three or move times */ positions[fen] = fen in positions ? positions[fen] + 1 : 1; if (positions[fen] >= 3) { repetition = true; } if (!moves.length) { break } make_move(moves.pop()); } return repetition } function push(move) { history.push({ move: move, kings: { b: kings.b, w: kings.w }, turn: turn, castling: { b: castling.b, w: castling.w }, ep_square: ep_square, half_moves: half_moves, move_number: move_number, }); } function make_move(move) { var us = turn; var them = swap_color(us); push(move); board[move.to] = board[move.from]; board[move.from] = null; /* if ep capture, remove the captured pawn */ if (move.flags & BITS.EP_CAPTURE) { if (turn === BLACK) { board[move.to - 16] = null; } else { board[move.to + 16] = null; } } /* if pawn promotion, replace with new piece */ if (move.flags & BITS.PROMOTION) { board[move.to] = { type: move.promotion, color: us }; } /* if we moved the king */ if (board[move.to].type === KING) { kings[board[move.to].color] = move.to; /* if we castled, move the rook next to the king */ if (move.flags & BITS.KSIDE_CASTLE) { var castling_to = move.to - 1; var castling_from = move.to + 1; board[castling_to] = board[castling_from]; board[castling_from] = null; } else if (move.flags & BITS.QSIDE_CASTLE) { var castling_to = move.to + 1; var castling_from = move.to - 2; board[castling_to] = board[castling_from]; board[castling_from] = null; } /* turn off castling */ castling[us] = ''; } /* turn off castling if we move a rook */ if (castling[us]) { for (var i = 0, len = ROOKS[us].length; i < len; i++) { if ( move.from === ROOKS[us][i].square && castling[us] & ROOKS[us][i].flag ) { castling[us] ^= ROOKS[us][i].flag; break } } } /* turn off castling if we capture a rook */ if (castling[them]) { for (var i = 0, len = ROOKS[them].length; i < len; i++) { if ( move.to === ROOKS[them][i].square && castling[them] & ROOKS[them][i].flag ) { castling[them] ^= ROOKS[them][i].flag; break } } } /* if big pawn move, update the en passant square */ if (move.flags & BITS.BIG_PAWN) { if (turn === 'b') { ep_square = move.to - 16; } else { ep_square = move.to + 16; } } else { ep_square = EMPTY; } /* reset the 50 move counter if a pawn is moved or a piece is captured */ if (move.piece === PAWN) { half_moves = 0; } else if (move.flags & (BITS.CAPTURE | BITS.EP_CAPTURE)) { half_moves = 0; } else { half_moves++; } if (turn === BLACK) { move_number++; } turn = swap_color(turn); } function undo_move() { var old = history.pop(); if (old == null) { return null } var move = old.m