[{"data":1,"prerenderedAt":186},["ShallowReactive",2],{"profile":3,"source-chess":169},{"name":4,"meta":5,"statement":6,"lede":9,"description":10,"readout":11,"current":21,"work":67,"trajectory":111,"notes":140,"about":143,"contact":153},"Chris Wijnia","Engineer · Founder",{"text":7,"accent":8},"I build software and companies.","companies.","I work out what’s worth building next.","Dutch engineer and founder. I work out what’s worth building next.",[12,15,18],{"label":13,"value":14},"Origin","Netherlands",{"label":16,"value":17},"Work","Product \u002F R&D",{"label":19,"value":20},"Status","Online",{"title":22,"accent":23,"records":24},"What I’m working on.","working on.",[25,40,54],{"id":26,"label":27,"headline":28,"accent":29,"paragraphs":30,"facets":33,"period":39},"agio","01 \u002F Agio Digital","Building financial software.","financial software.",[31,32],"I co-founded Agio Digital in 2021 and have been its Chief Innovation Officer since 2026.","I work across product architecture, compliance and AI: fund operations and reporting, regulated onboarding, payments, and the agents that increasingly help build it all. I still write code.",[34,35,36,37,38],"Financial software","Compliance","Product","Engineering","R&D","2021–Now",{"id":41,"label":42,"headline":43,"accent":44,"paragraphs":45,"facets":48,"period":53},"ai","02 \u002F AI & software","Finding useful things to do with new models.","new models.",[46,47],"I spend a lot of time on agents, developer tools, automation and new ways of building software.","I’m less interested in demos than in what holds up once real users, existing systems and messy constraints get involved.",[49,50,51,52],"Agents","Developer tools","Automation","Applied AI","2022–Now",{"id":55,"label":56,"headline":57,"accent":58,"paragraphs":59,"facets":62,"period":66},"independent","03 \u002F Independent work","Building things to see where they go.","where they go.",[60,61],"Small software projects, open-source work, strange interfaces and ideas that start as a repository before they have a business case.","Some become useful. Some answer a question. That’s enough.",[63,64,65],"Software","Open source","Experiments","Ongoing",{"title":68,"accent":69,"outro":70,"projects":71},"A few things I’ve worked on.","worked on.","The work has changed a lot over the years. The habit of making things hasn’t.",[72,77,82,88,94,100,105],{"no":73,"name":74,"area":75,"period":39,"slug":76},"01","Agio Digital","Financial software \u002F Compliance","agio-digital",{"no":78,"name":79,"area":80,"period":53,"slug":81},"02","AI systems","Agents \u002F Automation \u002F Developer tools","ai-systems",{"no":83,"name":84,"area":85,"period":86,"slug":87},"03","1GRAM","Wallet software","2020–2021","1gram",{"no":89,"name":90,"area":91,"period":92,"slug":93},"04","MVRDV","Digital platform \u002F Architecture","2015–2019","mvrdv",{"no":95,"name":96,"area":97,"period":98,"slug":99},"05","JIJ & Overijssel","Public participation platform","2014–2018","jij-overijssel",{"no":101,"name":102,"area":103,"period":104,"slug":102},"06","100-100-100","Sustainability campaign platform","2014–2019",{"no":106,"name":107,"area":108,"period":109,"slug":110},"07","Archive","Design \u002F Web \u002F Software","2007–2022","archive",{"title":112,"accent":113,"eras":114},"Moving down the stack as the web evolved.","evolved.",[115,119,122,124,128,132,136],{"year":116,"name":117,"line":118},"2005","Design","Learning the web by making things.",{"year":120,"name":36,"line":121},"2013","Design became code.",{"name":37,"line":123},"Code became systems.",{"year":125,"name":126,"line":127},"2018","Wallets","Software for a new kind of money.",{"year":129,"name":130,"line":131},"2021","Financial infra","Building systems where software meets regulation.",{"year":133,"name":134,"line":135},"2022","AI","From using models to orchestrating them.",{"year":137,"name":138,"line":139},"Now","Building","All of it, at once.",{"title":141,"intro":142},"Notes","Things I’m working on, learning or trying to understand.",{"title":144,"accent":145,"paragraphs":146},"I’ve been making things on computers for most of my life.","life.",[147,148,149,150,151,152],"I grew up in the Netherlands and built my first websites at thirteen.","I trained in interaction design, and design pulled me into frontend development, then engineering, product architecture and eventually company-building.","Over the years I’ve worked on websites, consumer products, distributed systems, financial systems and now AI. I don’t draw hard lines between design, engineering and product. They’re different ways of looking at the same problem.","Lately that includes regulated work: onboarding, compliance and fund reporting, where the rules shape the product as much as the users do.","I like small teams, ambitious projects, good engineering and people who can disagree without making it tedious. I still enjoy opening an empty repository and figuring something out.","I work internationally. Outside work I train Muay Thai and like [chess](\u002Fexperiments\u002Fchess), which I played competitively in Zwolle and taught to primary-school children there. I also build things that occasionally have no sensible commercial purpose.",{"title":154,"accent":155,"intro":156,"links":157},"Making something interesting?","interesting?","For companies, collaborations, technical discussions, speaking, new ventures, or a good reason I haven’t thought of.",[158,160,163,166],{"label":159},"Email",{"label":161,"href":162},"LinkedIn","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fchris-wijnia-1364a29b",{"label":164,"href":165},"GitHub","https:\u002F\u002Fgithub.com\u002Fcwdx",{"label":167,"href":168},"X","https:\u002F\u002Fx.com\u002FChrisWijnia",{"files":170},[171,176,181],{"path":172,"lines":173,"code":174,"html":175},"app\u002Fcomponents\u002Fexperiments\u002FChessBoard.vue",219,"\u003Cscript setup lang=\"ts\">\nimport type { NormalMove, Role } from 'chessops\u002Ftypes'\nimport { BOARD_THEMES, isDarkSquare, NAMES, squareGlyph, squareName, type BoardTheme, type ChessFont } from '#core\u002Fdomain\u002Fchess960'\nimport type { Ply } from '~\u002Fcomposables\u002FuseChessGame'\n\n\u002F\u002F The playable board: 64 squares in the chess font, each glyph a piece (or empty) already shaded for its square.\n\u002F\u002F Click, drag, or use the keyboard (arrows move a roving focus; Enter or Space picks and places).\nconst props = defineProps\u003C{\n  squares: string[]\n  font: ChessFont\n  theme: BoardTheme\n  flipped: boolean\n  interactive: boolean\n  targets: (from: number) => Map\u003Cnumber, NormalMove>\n  needsPromotion: (move: NormalMove) => boolean\n  check?: number\n  last?: Ply\n  plyCount: number\n  \u002F** While an engine thinks, the person's colour: their pieces stay movable, and a move becomes a premove. *\u002F\n  premoveColor?: 'white' | 'black'\n  premove?: { from: number; to: number }\n  \u002F** Moves to draw as arrows (Jev's candidates while it decides), each with its weight 0–1. *\u002F\n  arrows?: { from: number; to: number; p: number }[]\n}>()\nconst emit = defineEmits\u003C{ move: [NormalMove]; premove: [{ from: number; to: number }]; cancelPremove: [] }>()\n\nconst family = computed(() => `'Chess ${props.font}', var(--font-mono)`)\nconst themeVars = computed(() => {\n  const t = BOARD_THEMES[props.theme]\n  return { '--board-ground': `var(--color-${t.ground})`, '--board-ink': `var(--color-${t.ink})`, '--board-mark': `var(--color-${t.mark})` }\n})\n\u002F\u002F an arrow runs between square centres on an 8×8 grid laid over the squares; its width and ink follow its weight\nconst centre = (sq: number) => { const f = sq & 7, r = sq >> 3; return props.flipped ? [7 - f + 0.5, r + 0.5] : [f + 0.5, 7 - r + 0.5] }\nconst arrowLines = computed(() => (props.arrows ?? []).filter((a) => a.p >= 0.02).map((a) => {\n  const [x1, y1] = centre(a.from), [x2, y2] = centre(a.to)\n  \u002F\u002F stop short of the centre, so the head (0.42 wide, its tip 0.3 past the line's end) points at the square\n  const len = Math.hypot(x2! - x1!, y2! - y1!), cut = 0.35 \u002F len\n  return { key: `${a.from}-${a.to}`, x1, y1, x2: x2! - (x2! - x1!) * cut, y2: y2! - (y2! - y1!) * cut, w: 0.07 + a.p * 0.13, o: 0.35 + a.p * 0.6 }\n}))\nconst order = computed(() => Array.from({ length: 64 }, (_, i) => {\n  const row = i >> 3, col = i & 7\n  return props.flipped ? row * 8 + (7 - col) : (7 - row) * 8 + col\n}))\n\nconst selected = ref\u003Cnumber>()\nconst legal = computed(() => (selected.value === undefined ? new Map\u003Cnumber, NormalMove>() : props.targets(selected.value)))\nconst promoting = ref\u003CNormalMove>()\nconst hidden = ref(new Set\u003Cnumber>())\nconst focus = ref(12)\nconst board = ref\u003CHTMLElement>()\n\nconst ownPiece = (sq: number, white: boolean) => { const p = props.squares[sq]; return !!p && (p === p.toUpperCase()) === white }\nconst whiteToMove = computed(() => props.plyCount % 2 === 0)\nconst premoving = computed(() => !props.interactive && !!props.premoveColor)\nconst mover = computed(() => (props.interactive ? (whiteToMove.value ? 'white' : 'black') : props.premoveColor))\n\nfunction pick(sq: number) {\n  if (premoving.value) {\n    \u002F\u002F any square will do for a premove; it is checked once the engine has moved\n    if (selected.value !== undefined && sq !== selected.value) { emit('premove', { from: selected.value, to: sq }); selected.value = undefined; return }\n    if (selected.value === undefined && !ownPiece(sq, mover.value === 'white')) { emit('cancelPremove'); return }\n    selected.value = selected.value === sq ? undefined : sq\n    return\n  }\n  if (!props.interactive) return\n  const move = legal.value.get(sq)\n  if (move) {\n    selected.value = undefined\n    if (props.needsPromotion(move)) { promoting.value = move; return }\n    emit('move', move)\n    return\n  }\n  selected.value = selected.value !== sq && ownPiece(sq, mover.value === 'white') && props.targets(sq).size ? sq : undefined\n}\nfunction promote(role: Role) {\n  if (!promoting.value) return\n  emit('move', { ...promoting.value, promotion: role })\n  promoting.value = undefined\n}\nwatch(() => props.plyCount, () => { if (!premoving.value) selected.value = undefined; promoting.value = undefined })\n\nfunction label(sq: number) {\n  const p = props.squares[sq]\n  const piece = p ? `${p === p.toUpperCase() ? 'white' : 'black'} ${NAMES[p.toUpperCase()]!.toLowerCase()}` : 'empty'\n  const extra = sq === selected.value ? ', selected' : legal.value.has(sq) ? ', move here' : ''\n  return `${squareName(sq)}, ${piece}${extra}`\n}\nfunction key(e: KeyboardEvent) {\n  const step: Record\u003Cstring, [number, number]> = { ArrowUp: [0, 1], ArrowDown: [0, -1], ArrowLeft: [-1, 0], ArrowRight: [1, 0] }\n  const d = step[e.key]\n  if (!d) return\n  e.preventDefault()\n  const [df, dr] = props.flipped ? [-d[0], -d[1]] : d\n  const f = Math.min(7, Math.max(0, (focus.value & 7) + df)), r = Math.min(7, Math.max(0, (focus.value >> 3) + dr))\n  focus.value = r * 8 + f\n  board.value?.querySelector\u003CHTMLElement>(`[data-sq=\"${focus.value}\"]`)?.focus()\n}\n\n\u002F\u002F A piece in flight (animation and drag). A White glyph is only an outline, so the board would show through it:\n\u002F\u002F the same piece's solid Black glyph goes underneath in Paper to fill the body.\nfunction makeTile(piece: string, size: number) {\n  const tile = document.createElement('span')\n  tile.className = 'board__tile'\n  tile.append(\n    Object.assign(document.createElement('span'), { className: 'board__fill', textContent: squareGlyph(piece.toLowerCase(), false, props.font) }),\n    Object.assign(document.createElement('span'), { textContent: squareGlyph(piece, false, props.font) }),\n  )\n  Object.assign(tile.style, { width: `${size}px`, height: `${size}px`, fontFamily: family.value })\n  board.value!.append(tile)\n  return tile\n}\n\n\u002F\u002F A drag starts only after a few pixels of movement, so a press without movement stays a click.\nlet drag: { from: number; x: number; y: number; tile?: HTMLElement; size: number } | undefined\nlet dropped = false\nlet swallowClick = false\nconst movable = (sq: number) => (props.interactive || premoving.value) && !!mover.value && ownPiece(sq, mover.value === 'white')\nfunction down(sq: number, e: PointerEvent) {\n  if (!movable(sq) || e.button !== 0) return\n  const size = board.value!.querySelector\u003CHTMLElement>(`[data-sq=\"${sq}\"]`)!.getBoundingClientRect().width\n  drag = { from: sq, x: e.clientX, y: e.clientY, size }\n  window.addEventListener('pointermove', moveDrag)\n  window.addEventListener('pointerup', endDrag, { once: true })\n  window.addEventListener('pointercancel', cancelDrag, { once: true })\n}\nfunction place(tile: HTMLElement, x: number, y: number, size: number) {\n  const o = board.value!.getBoundingClientRect()\n  tile.style.left = `${x - o.left - size \u002F 2}px`\n  tile.style.top = `${y - o.top - size \u002F 2}px`\n}\nfunction moveDrag(e: PointerEvent) {\n  if (!drag) return\n  if (!drag.tile) {\n    if (Math.hypot(e.clientX - drag.x, e.clientY - drag.y) \u003C 5) return\n    selected.value = drag.from\n    hidden.value = new Set([drag.from])\n    drag.tile = makeTile(props.squares[drag.from]!, drag.size)\n    drag.tile.classList.add('is-dragging')\n  }\n  place(drag.tile, e.clientX, e.clientY, drag.size)\n}\nfunction cleanup() {\n  window.removeEventListener('pointermove', moveDrag)\n  drag?.tile?.remove()\n  hidden.value = new Set()\n  drag = undefined\n}\nfunction endDrag(e: PointerEvent) {\n  window.removeEventListener('pointercancel', cancelDrag)\n  if (!drag?.tile) return cleanup()\n  swallowClick = true\n  const target = document.elementFromPoint(e.clientX, e.clientY)?.closest\u003CHTMLElement>('[data-sq]')\n  const to = target ? Number(target.dataset.sq) : undefined\n  const from = drag.from\n  cleanup()\n  if (premoving.value) { selected.value = undefined; if (to !== undefined && to !== from) emit('premove', { from, to }); return }\n  if (to !== undefined && legal.value.has(to)) { dropped = true; pick(to) } else selected.value = undefined\n}\nfunction cancelDrag() { window.removeEventListener('pointerup', endDrag); cleanup() }\nfunction clicked(sq: number) {\n  if (swallowClick) { swallowClick = false; return }\n  focus.value = sq\n  pick(sq)\n}\nonBeforeUnmount(cleanup)\n\n\u002F\u002F Pieces are hidden on their new squares while their glyphs slide in from the old ones.\nasync function animate(ply: Ply) {\n  if (dropped) { dropped = false; return }\n  if (useReducedMotion() || !board.value) return\n  const paths: [number, number][] = ply.castle\n    ? [[ply.move.from, ply.castle.kingTo], [ply.castle.rookFrom, ply.castle.rookTo]]\n    : [[ply.move.from, ply.move.to]]\n  hidden.value = new Set(paths.map(([, to]) => to))\n  await nextTick()\n  const rect = (sq: number) => board.value!.querySelector\u003CHTMLElement>(`[data-sq=\"${sq}\"]`)!.getBoundingClientRect()\n  const origin = board.value.getBoundingClientRect()\n  await Promise.all(paths.map(([from, to]) => {\n    const a = rect(from), b = rect(to)\n    const tile = makeTile(props.squares[to]!, a.width)\n    Object.assign(tile.style, { left: `${a.left - origin.left}px`, top: `${a.top - origin.top}px` })\n    const dx = b.left - a.left, dy = b.top - a.top\n    return tile.animate(\n      [{ transform: 'translate(0, 0)' }, { transform: `translate(${dx}px, ${dy}px)` }],\n      { duration: 240, easing: 'cubic-bezier(.2, .7, .1, 1)' },\n    ).finished.finally(() => tile.remove())\n  }))\n  hidden.value = new Set()\n}\nwatch(() => props.last, (ply, prev) => { if (ply && ply !== prev && props.plyCount > 0) void animate(ply) })\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv ref=\"board\" class=\"board__grid\" role=\"grid\" aria-label=\"Chess board\" :style=\"{ fontFamily: family, ...themeVars }\" @keydown=\"key\">\n    \u003Cbutton\n      v-for=\"sq in order\" :key=\"sq\" type=\"button\" class=\"board__sq\" :data-sq=\"sq\"\n      :class=\"{\n        'is-selected': sq === selected,\n        'is-target': legal.has(sq),\n        'is-capture': legal.has(sq) && !!squares[sq],\n        'is-last': last && (sq === last.move.from || sq === (last.castle?.kingTo ?? last.move.to)),\n        'is-check': sq === check,\n        'is-movable': movable(sq),\n        'is-premove': premove && (sq === premove.from || sq === premove.to),\n      }\"\n      :tabindex=\"sq === focus ? 0 : -1\" :aria-label=\"label(sq)\" :aria-disabled=\"!interactive\"\n      @click=\"clicked(sq)\" @pointerdown=\"down(sq, $event)\"\n    >{{ squareGlyph(hidden.has(sq) ? '' : squares[sq]!, isDarkSquare(sq), font) }}\u003C\u002Fbutton>\n    \u003Csvg v-if=\"arrowLines.length\" class=\"board__arrows\" viewBox=\"0 0 8 8\" aria-hidden=\"true\">\n      \u003Cdefs>\u003Cmarker id=\"board-arrow\" viewBox=\"0 0 4 4\" refX=\"1\" refY=\"2\" markerUnits=\"userSpaceOnUse\" markerWidth=\"0.42\" markerHeight=\"0.42\" orient=\"auto\">\u003Cpath d=\"M0,0 L4,2 L0,4 z\" fill=\"context-stroke\" \u002F>\u003C\u002Fmarker>\u003C\u002Fdefs>\n      \u003Cline v-for=\"a in arrowLines\" :key=\"a.key\" :x1=\"a.x1\" :y1=\"a.y1\" :x2=\"a.x2\" :y2=\"a.y2\" :stroke-width=\"a.w\" :opacity=\"a.o\" marker-end=\"url(#board-arrow)\" \u002F>\n    \u003C\u002Fsvg>\n    \u003Cdiv v-if=\"promoting\" class=\"board__promote\" role=\"dialog\" aria-label=\"Promote to\">\n      \u003Cbutton v-for=\"r in (['queen', 'rook', 'bishop', 'knight'] as Role[])\" :key=\"r\" type=\"button\" :aria-label=\"`Promote to ${r}`\" @click=\"promote(r)\">\n        {{ squareGlyph(({ queen: 'Q', rook: 'R', bishop: 'B', knight: 'N' } as Record\u003Cstring, string>)[r]![whiteToMove ? 'toUpperCase' : 'toLowerCase'](), false, font) }}\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>","\u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">script\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">setup\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">lang\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;ts&quot;\u003C\u002Fspan>&gt;\u003C\u002Fspan>\u003Cspan class=\"language-javascript\">\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> type { \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>, \u003Cspan class=\"hljs-title class_\">Role\u003C\u002Fspan> } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;chessops\u002Ftypes&#x27;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> { \u003Cspan class=\"hljs-variable constant_\">BOARD_THEMES\u003C\u002Fspan>, isDarkSquare, \u003Cspan class=\"hljs-variable constant_\">NAMES\u003C\u002Fspan>, squareGlyph, squareName, type \u003Cspan class=\"hljs-title class_\">BoardTheme\u003C\u002Fspan>, type \u003Cspan class=\"hljs-title class_\">ChessFont\u003C\u002Fspan> } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;#core\u002Fdomain\u002Fchess960&#x27;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> type { \u003Cspan class=\"hljs-title class_\">Ply\u003C\u002Fspan> } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;~\u002Fcomposables\u002FuseChessGame&#x27;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-comment\">\u002F\u002F The playable board: 64 squares in the chess font, each glyph a piece (or empty) already shaded for its square.\u003C\u002Fspan>\n\u003Cspan class=\"hljs-comment\">\u002F\u002F Click, drag, or use the keyboard (arrows move a roving focus; Enter or Space picks and places).\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> props = defineProps&lt;{\n  \u003Cspan class=\"hljs-attr\">squares\u003C\u002Fspan>: string[]\n  \u003Cspan class=\"hljs-attr\">font\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">ChessFont\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">theme\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">BoardTheme\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">flipped\u003C\u002Fspan>: boolean\n  \u003Cspan class=\"hljs-attr\">interactive\u003C\u002Fspan>: boolean\n  \u003Cspan class=\"hljs-attr\">targets\u003C\u002Fspan>: \u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>: number\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Map\u003C\u002Fspan>&lt;number, \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>&gt;\n  \u003Cspan class=\"hljs-attr\">needsPromotion\u003C\u002Fspan>: \u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">move: NormalMove\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> boolean\n  check?: number\n  last?: \u003Cspan class=\"hljs-title class_\">Ply\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">plyCount\u003C\u002Fspan>: number\n  \u003Cspan class=\"hljs-comment\">\u002F** While an engine thinks, the person&#x27;s colour: their pieces stay movable, and a move becomes a premove. *\u002F\u003C\u002Fspan>\n  premoveColor?: \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;black&#x27;\u003C\u002Fspan>\n  premove?: { \u003Cspan class=\"hljs-attr\">from\u003C\u002Fspan>: number; \u003Cspan class=\"hljs-attr\">to\u003C\u002Fspan>: number }\n  \u003Cspan class=\"hljs-comment\">\u002F** Moves to draw as arrows (Jev&#x27;s candidates while it decides), each with its weight 0–1. *\u002F\u003C\u002Fspan>\n  arrows?: { \u003Cspan class=\"hljs-attr\">from\u003C\u002Fspan>: number; \u003Cspan class=\"hljs-attr\">to\u003C\u002Fspan>: number; \u003Cspan class=\"hljs-attr\">p\u003C\u002Fspan>: number }[]\n}&gt;()\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> emit = defineEmits&lt;{ \u003Cspan class=\"hljs-attr\">move\u003C\u002Fspan>: [\u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>]; \u003Cspan class=\"hljs-attr\">premove\u003C\u002Fspan>: [{ \u003Cspan class=\"hljs-attr\">from\u003C\u002Fspan>: number; \u003Cspan class=\"hljs-attr\">to\u003C\u002Fspan>: number }]; \u003Cspan class=\"hljs-attr\">cancelPremove\u003C\u002Fspan>: [] }&gt;()\n\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> family = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">`&#x27;Chess \u003Cspan class=\"hljs-subst\">${props.font}\u003C\u002Fspan>&#x27;, var(--font-mono)`\u003C\u002Fspan>)\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> themeVars = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> t = \u003Cspan class=\"hljs-variable constant_\">BOARD_THEMES\u003C\u002Fspan>[props.\u003Cspan class=\"hljs-property\">theme\u003C\u002Fspan>]\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { \u003Cspan class=\"hljs-string\">&#x27;--board-ground&#x27;\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`var(--color-\u003Cspan class=\"hljs-subst\">${t.ground}\u003C\u002Fspan>)`\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;--board-ink&#x27;\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`var(--color-\u003Cspan class=\"hljs-subst\">${t.ink}\u003C\u002Fspan>)`\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;--board-mark&#x27;\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`var(--color-\u003Cspan class=\"hljs-subst\">${t.mark}\u003C\u002Fspan>)`\u003C\u002Fspan> }\n})\n\u003Cspan class=\"hljs-comment\">\u002F\u002F an arrow runs between square centres on an 8×8 grid laid over the squares; its width and ink follow its weight\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">centre\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">sq: number\u003C\u002Fspan>) =&gt; { \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> f = sq &amp; \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan>, r = sq &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>; \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> props.\u003Cspan class=\"hljs-property\">flipped\u003C\u002Fspan> ? [\u003Cspan class=\"hljs-number\">7\u003C\u002Fspan> - f + \u003Cspan class=\"hljs-number\">0.5\u003C\u002Fspan>, r + \u003Cspan class=\"hljs-number\">0.5\u003C\u002Fspan>] : [f + \u003Cspan class=\"hljs-number\">0.5\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan> - r + \u003Cspan class=\"hljs-number\">0.5\u003C\u002Fspan>] }\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> arrowLines = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> (props.\u003Cspan class=\"hljs-property\">arrows\u003C\u002Fspan> ?? []).\u003Cspan class=\"hljs-title function_\">filter\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">a\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> a.\u003Cspan class=\"hljs-property\">p\u003C\u002Fspan> &gt;= \u003Cspan class=\"hljs-number\">0.02\u003C\u002Fspan>).\u003Cspan class=\"hljs-title function_\">map\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">a\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [x1, y1] = \u003Cspan class=\"hljs-title function_\">centre\u003C\u002Fspan>(a.\u003Cspan class=\"hljs-property\">from\u003C\u002Fspan>), [x2, y2] = \u003Cspan class=\"hljs-title function_\">centre\u003C\u002Fspan>(a.\u003Cspan class=\"hljs-property\">to\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-comment\">\u002F\u002F stop short of the centre, so the head (0.42 wide, its tip 0.3 past the line&#x27;s end) points at the square\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> len = \u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">hypot\u003C\u002Fspan>(x2! - x1!, y2! - y1!), cut = \u003Cspan class=\"hljs-number\">0.35\u003C\u002Fspan> \u002F len\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { \u003Cspan class=\"hljs-attr\">key\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${a.\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>}\u003C\u002Fspan>-\u003Cspan class=\"hljs-subst\">${a.to}\u003C\u002Fspan>`\u003C\u002Fspan>, x1, y1, \u003Cspan class=\"hljs-attr\">x2\u003C\u002Fspan>: x2! - (x2! - x1!) * cut, \u003Cspan class=\"hljs-attr\">y2\u003C\u002Fspan>: y2! - (y2! - y1!) * cut, \u003Cspan class=\"hljs-attr\">w\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">0.07\u003C\u002Fspan> + a.\u003Cspan class=\"hljs-property\">p\u003C\u002Fspan> * \u003Cspan class=\"hljs-number\">0.13\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">o\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">0.35\u003C\u002Fspan> + a.\u003Cspan class=\"hljs-property\">p\u003C\u002Fspan> * \u003Cspan class=\"hljs-number\">0.6\u003C\u002Fspan> }\n}))\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> order = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Array\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">from\u003C\u002Fspan>({ \u003Cspan class=\"hljs-attr\">length\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">64\u003C\u002Fspan> }, \u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">_, i\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> row = i &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>, col = i &amp; \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> props.\u003Cspan class=\"hljs-property\">flipped\u003C\u002Fspan> ? row * \u003Cspan class=\"hljs-number\">8\u003C\u002Fspan> + (\u003Cspan class=\"hljs-number\">7\u003C\u002Fspan> - col) : (\u003Cspan class=\"hljs-number\">7\u003C\u002Fspan> - row) * \u003Cspan class=\"hljs-number\">8\u003C\u002Fspan> + col\n}))\n\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> selected = ref&lt;number&gt;()\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> legal = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> (selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> === \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> ? \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Map\u003C\u002Fspan>&lt;number, \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>&gt;() : props.\u003Cspan class=\"hljs-title function_\">targets\u003C\u002Fspan>(selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>)))\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> promoting = ref&lt;\u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>&gt;()\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> hidden = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Set\u003C\u002Fspan>&lt;number&gt;())\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> focus = \u003Cspan class=\"hljs-title function_\">ref\u003C\u002Fspan>(\u003Cspan class=\"hljs-number\">12\u003C\u002Fspan>)\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> board = ref&lt;\u003Cspan class=\"hljs-title class_\">HTMLElement\u003C\u002Fspan>&gt;()\n\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">ownPiece\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">sq: number, white: boolean\u003C\u002Fspan>) =&gt; { \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = props.\u003Cspan class=\"hljs-property\">squares\u003C\u002Fspan>[sq]; \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> !!p &amp;&amp; (p === p.\u003Cspan class=\"hljs-title function_\">toUpperCase\u003C\u002Fspan>()) === white }\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> whiteToMove = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> props.\u003Cspan class=\"hljs-property\">plyCount\u003C\u002Fspan> % \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan> === \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>)\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> premoving = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> !props.\u003Cspan class=\"hljs-property\">interactive\u003C\u002Fspan> &amp;&amp; !!props.\u003Cspan class=\"hljs-property\">premoveColor\u003C\u002Fspan>)\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> mover = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> (props.\u003Cspan class=\"hljs-property\">interactive\u003C\u002Fspan> ? (whiteToMove.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;black&#x27;\u003C\u002Fspan>) : props.\u003Cspan class=\"hljs-property\">premoveColor\u003C\u002Fspan>))\n\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">pick\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">sq: number\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (premoving.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F any square will do for a premove; it is checked once the engine has moved\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> !== \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> &amp;&amp; sq !== selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) { \u003Cspan class=\"hljs-title function_\">emit\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;premove&#x27;\u003C\u002Fspan>, { \u003Cspan class=\"hljs-attr\">from\u003C\u002Fspan>: selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">to\u003C\u002Fspan>: sq }); selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>; \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> }\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> === \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> &amp;&amp; !\u003Cspan class=\"hljs-title function_\">ownPiece\u003C\u002Fspan>(sq, mover.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan>)) { \u003Cspan class=\"hljs-title function_\">emit\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;cancelPremove&#x27;\u003C\u002Fspan>); \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> }\n    selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> === sq ? \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> : sq\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n  }\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!props.\u003Cspan class=\"hljs-property\">interactive\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> move = legal.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">get\u003C\u002Fspan>(sq)\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (move) {\n    selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (props.\u003Cspan class=\"hljs-title function_\">needsPromotion\u003C\u002Fspan>(move)) { promoting.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = move; \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> }\n    \u003Cspan class=\"hljs-title function_\">emit\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;move&#x27;\u003C\u002Fspan>, move)\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n  }\n  selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> !== sq &amp;&amp; \u003Cspan class=\"hljs-title function_\">ownPiece\u003C\u002Fspan>(sq, mover.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan>) &amp;&amp; props.\u003Cspan class=\"hljs-title function_\">targets\u003C\u002Fspan>(sq).\u003Cspan class=\"hljs-property\">size\u003C\u002Fspan> ? sq : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n}\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">promote\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">role: Role\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!promoting.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-title function_\">emit\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;move&#x27;\u003C\u002Fspan>, { ...promoting.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">promotion\u003C\u002Fspan>: role })\n  promoting.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n}\n\u003Cspan class=\"hljs-title function_\">watch\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> props.\u003Cspan class=\"hljs-property\">plyCount\u003C\u002Fspan>, \u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> { \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!premoving.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>; promoting.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> })\n\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">label\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">sq: number\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = props.\u003Cspan class=\"hljs-property\">squares\u003C\u002Fspan>[sq]\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> piece = p ? \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${p === p.toUpperCase() ? \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;black&#x27;\u003C\u002Fspan>}\u003C\u002Fspan> \u003Cspan class=\"hljs-subst\">${NAMES[p.toUpperCase()]!.toLowerCase()}\u003C\u002Fspan>`\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;empty&#x27;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> extra = sq === selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;, selected&#x27;\u003C\u002Fspan> : legal.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">has\u003C\u002Fspan>(sq) ? \u003Cspan class=\"hljs-string\">&#x27;, move here&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${squareName(sq)}\u003C\u002Fspan>, \u003Cspan class=\"hljs-subst\">${piece}\u003C\u002Fspan>\u003Cspan class=\"hljs-subst\">${extra}\u003C\u002Fspan>`\u003C\u002Fspan>\n}\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">key\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">e: KeyboardEvent\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">step\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Record\u003C\u002Fspan>&lt;string, [number, number]&gt; = { \u003Cspan class=\"hljs-title class_\">ArrowUp\u003C\u002Fspan>: [\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>], \u003Cspan class=\"hljs-title class_\">ArrowDown\u003C\u002Fspan>: [\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, -\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>], \u003Cspan class=\"hljs-title class_\">ArrowLeft\u003C\u002Fspan>: [-\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>], \u003Cspan class=\"hljs-title class_\">ArrowRight\u003C\u002Fspan>: [\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>] }\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> d = step[e.\u003Cspan class=\"hljs-property\">key\u003C\u002Fspan>]\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!d) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n  e.\u003Cspan class=\"hljs-title function_\">preventDefault\u003C\u002Fspan>()\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [df, dr] = props.\u003Cspan class=\"hljs-property\">flipped\u003C\u002Fspan> ? [-d[\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>], -d[\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>]] : d\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> f = \u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">min\u003C\u002Fspan>(\u003Cspan class=\"hljs-number\">7\u003C\u002Fspan>, \u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">max\u003C\u002Fspan>(\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, (focus.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> &amp; \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan>) + df)), r = \u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">min\u003C\u002Fspan>(\u003Cspan class=\"hljs-number\">7\u003C\u002Fspan>, \u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">max\u003C\u002Fspan>(\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, (focus.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>) + dr))\n  focus.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = r * \u003Cspan class=\"hljs-number\">8\u003C\u002Fspan> + f\n  board.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>?.\u003Cspan class=\"hljs-property\">querySelector\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">HTMLElement\u003C\u002Fspan>&gt;(\u003Cspan class=\"hljs-string\">`[data-sq=&quot;\u003Cspan class=\"hljs-subst\">${focus.value}\u003C\u002Fspan>&quot;]`\u003C\u002Fspan>)?.\u003Cspan class=\"hljs-title function_\">focus\u003C\u002Fspan>()\n}\n\n\u003Cspan class=\"hljs-comment\">\u002F\u002F A piece in flight (animation and drag). A White glyph is only an outline, so the board would show through it:\u003C\u002Fspan>\n\u003Cspan class=\"hljs-comment\">\u002F\u002F the same piece&#x27;s solid Black glyph goes underneath in Paper to fill the body.\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">makeTile\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">piece: string, size: number\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> tile = \u003Cspan class=\"hljs-variable language_\">document\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">createElement\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;span&#x27;\u003C\u002Fspan>)\n  tile.\u003Cspan class=\"hljs-property\">className\u003C\u002Fspan> = \u003Cspan class=\"hljs-string\">&#x27;board__tile&#x27;\u003C\u002Fspan>\n  tile.\u003Cspan class=\"hljs-title function_\">append\u003C\u002Fspan>(\n    \u003Cspan class=\"hljs-title class_\">Object\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">assign\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable language_\">document\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">createElement\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;span&#x27;\u003C\u002Fspan>), { \u003Cspan class=\"hljs-attr\">className\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;board__fill&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">textContent\u003C\u002Fspan>: \u003Cspan class=\"hljs-title function_\">squareGlyph\u003C\u002Fspan>(piece.\u003Cspan class=\"hljs-title function_\">toLowerCase\u003C\u002Fspan>(), \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>, props.\u003Cspan class=\"hljs-property\">font\u003C\u002Fspan>) }),\n    \u003Cspan class=\"hljs-title class_\">Object\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">assign\u003C\u002Fspan>(\u003Cspan class=\"hljs-variable language_\">document\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">createElement\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;span&#x27;\u003C\u002Fspan>), { \u003Cspan class=\"hljs-attr\">textContent\u003C\u002Fspan>: \u003Cspan class=\"hljs-title function_\">squareGlyph\u003C\u002Fspan>(piece, \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>, props.\u003Cspan class=\"hljs-property\">font\u003C\u002Fspan>) }),\n  )\n  \u003Cspan class=\"hljs-title class_\">Object\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">assign\u003C\u002Fspan>(tile.\u003Cspan class=\"hljs-property\">style\u003C\u002Fspan>, { \u003Cspan class=\"hljs-attr\">width\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${size}\u003C\u002Fspan>px`\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">height\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${size}\u003C\u002Fspan>px`\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">fontFamily\u003C\u002Fspan>: family.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> })\n  board.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>!.\u003Cspan class=\"hljs-title function_\">append\u003C\u002Fspan>(tile)\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> tile\n}\n\n\u003Cspan class=\"hljs-comment\">\u002F\u002F A drag starts only after a few pixels of movement, so a press without movement stays a click.\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">drag\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">from\u003C\u002Fspan>: number; \u003Cspan class=\"hljs-attr\">x\u003C\u002Fspan>: number; \u003Cspan class=\"hljs-attr\">y\u003C\u002Fspan>: number; tile?: \u003Cspan class=\"hljs-title class_\">HTMLElement\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">size\u003C\u002Fspan>: number } | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> dropped = \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> swallowClick = \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">movable\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">sq: number\u003C\u002Fspan>) =&gt; (props.\u003Cspan class=\"hljs-property\">interactive\u003C\u002Fspan> || premoving.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) &amp;&amp; !!mover.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> &amp;&amp; \u003Cspan class=\"hljs-title function_\">ownPiece\u003C\u002Fspan>(sq, mover.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan>)\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">down\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">sq: number, e: PointerEvent\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-title function_\">movable\u003C\u002Fspan>(sq) || e.\u003Cspan class=\"hljs-property\">button\u003C\u002Fspan> !== \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> size = board.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>!.\u003Cspan class=\"hljs-property\">querySelector\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">HTMLElement\u003C\u002Fspan>&gt;(\u003Cspan class=\"hljs-string\">`[data-sq=&quot;\u003Cspan class=\"hljs-subst\">${sq}\u003C\u002Fspan>&quot;]`\u003C\u002Fspan>)!.\u003Cspan class=\"hljs-title function_\">getBoundingClientRect\u003C\u002Fspan>().\u003Cspan class=\"hljs-property\">width\u003C\u002Fspan>\n  drag = { \u003Cspan class=\"hljs-attr\">from\u003C\u002Fspan>: sq, \u003Cspan class=\"hljs-attr\">x\u003C\u002Fspan>: e.\u003Cspan class=\"hljs-property\">clientX\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">y\u003C\u002Fspan>: e.\u003Cspan class=\"hljs-property\">clientY\u003C\u002Fspan>, size }\n  \u003Cspan class=\"hljs-variable language_\">window\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">addEventListener\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;pointermove&#x27;\u003C\u002Fspan>, moveDrag)\n  \u003Cspan class=\"hljs-variable language_\">window\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">addEventListener\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;pointerup&#x27;\u003C\u002Fspan>, endDrag, { \u003Cspan class=\"hljs-attr\">once\u003C\u002Fspan>: \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan> })\n  \u003Cspan class=\"hljs-variable language_\">window\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">addEventListener\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;pointercancel&#x27;\u003C\u002Fspan>, cancelDrag, { \u003Cspan class=\"hljs-attr\">once\u003C\u002Fspan>: \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan> })\n}\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">place\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">tile: HTMLElement, x: number, y: number, size: number\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> o = board.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>!.\u003Cspan class=\"hljs-title function_\">getBoundingClientRect\u003C\u002Fspan>()\n  tile.\u003Cspan class=\"hljs-property\">style\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">left\u003C\u002Fspan> = \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${x - o.left - size \u002F \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>}\u003C\u002Fspan>px`\u003C\u002Fspan>\n  tile.\u003Cspan class=\"hljs-property\">style\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">top\u003C\u002Fspan> = \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${y - o.top - size \u002F \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>}\u003C\u002Fspan>px`\u003C\u002Fspan>\n}\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">moveDrag\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">e: PointerEvent\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!drag) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!drag.\u003Cspan class=\"hljs-property\">tile\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (\u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">hypot\u003C\u002Fspan>(e.\u003Cspan class=\"hljs-property\">clientX\u003C\u002Fspan> - drag.\u003Cspan class=\"hljs-property\">x\u003C\u002Fspan>, e.\u003Cspan class=\"hljs-property\">clientY\u003C\u002Fspan> - drag.\u003Cspan class=\"hljs-property\">y\u003C\u002Fspan>) &lt; \u003Cspan class=\"hljs-number\">5\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n    selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = drag.\u003Cspan class=\"hljs-property\">from\u003C\u002Fspan>\n    hidden.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Set\u003C\u002Fspan>([drag.\u003Cspan class=\"hljs-property\">from\u003C\u002Fspan>])\n    drag.\u003Cspan class=\"hljs-property\">tile\u003C\u002Fspan> = \u003Cspan class=\"hljs-title function_\">makeTile\u003C\u002Fspan>(props.\u003Cspan class=\"hljs-property\">squares\u003C\u002Fspan>[drag.\u003Cspan class=\"hljs-property\">from\u003C\u002Fspan>]!, drag.\u003Cspan class=\"hljs-property\">size\u003C\u002Fspan>)\n    drag.\u003Cspan class=\"hljs-property\">tile\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">classList\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">add\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;is-dragging&#x27;\u003C\u002Fspan>)\n  }\n  \u003Cspan class=\"hljs-title function_\">place\u003C\u002Fspan>(drag.\u003Cspan class=\"hljs-property\">tile\u003C\u002Fspan>, e.\u003Cspan class=\"hljs-property\">clientX\u003C\u002Fspan>, e.\u003Cspan class=\"hljs-property\">clientY\u003C\u002Fspan>, drag.\u003Cspan class=\"hljs-property\">size\u003C\u002Fspan>)\n}\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">cleanup\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-variable language_\">window\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">removeEventListener\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;pointermove&#x27;\u003C\u002Fspan>, moveDrag)\n  drag?.\u003Cspan class=\"hljs-property\">tile\u003C\u002Fspan>?.\u003Cspan class=\"hljs-title function_\">remove\u003C\u002Fspan>()\n  hidden.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Set\u003C\u002Fspan>()\n  drag = \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n}\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">endDrag\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">e: PointerEvent\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-variable language_\">window\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">removeEventListener\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;pointercancel&#x27;\u003C\u002Fspan>, cancelDrag)\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!drag?.\u003Cspan class=\"hljs-property\">tile\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">cleanup\u003C\u002Fspan>()\n  swallowClick = \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> target = \u003Cspan class=\"hljs-variable language_\">document\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">elementFromPoint\u003C\u002Fspan>(e.\u003Cspan class=\"hljs-property\">clientX\u003C\u002Fspan>, e.\u003Cspan class=\"hljs-property\">clientY\u003C\u002Fspan>)?.\u003Cspan class=\"hljs-property\">closest\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">HTMLElement\u003C\u002Fspan>&gt;(\u003Cspan class=\"hljs-string\">&#x27;[data-sq]&#x27;\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> to = target ? \u003Cspan class=\"hljs-title class_\">Number\u003C\u002Fspan>(target.\u003Cspan class=\"hljs-property\">dataset\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">sq\u003C\u002Fspan>) : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> = drag.\u003Cspan class=\"hljs-property\">from\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-title function_\">cleanup\u003C\u002Fspan>()\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (premoving.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) { selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>; \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (to !== \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> &amp;&amp; to !== \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>) \u003Cspan class=\"hljs-title function_\">emit\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;premove&#x27;\u003C\u002Fspan>, { \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>, to }); \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> }\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (to !== \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> &amp;&amp; legal.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">has\u003C\u002Fspan>(to)) { dropped = \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>; \u003Cspan class=\"hljs-title function_\">pick\u003C\u002Fspan>(to) } \u003Cspan class=\"hljs-keyword\">else\u003C\u002Fspan> selected.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n}\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">cancelDrag\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) { \u003Cspan class=\"hljs-variable language_\">window\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">removeEventListener\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;pointerup&#x27;\u003C\u002Fspan>, endDrag); \u003Cspan class=\"hljs-title function_\">cleanup\u003C\u002Fspan>() }\n\u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">clicked\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">sq: number\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (swallowClick) { swallowClick = \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>; \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> }\n  focus.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = sq\n  \u003Cspan class=\"hljs-title function_\">pick\u003C\u002Fspan>(sq)\n}\n\u003Cspan class=\"hljs-title function_\">onBeforeUnmount\u003C\u002Fspan>(cleanup)\n\n\u003Cspan class=\"hljs-comment\">\u002F\u002F Pieces are hidden on their new squares while their glyphs slide in from the old ones.\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">async\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">animate\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">ply: Ply\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (dropped) { dropped = \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>; \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> }\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (\u003Cspan class=\"hljs-title function_\">useReducedMotion\u003C\u002Fspan>() || !board.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">paths\u003C\u002Fspan>: [number, number][] = ply.\u003Cspan class=\"hljs-property\">castle\u003C\u002Fspan>\n    ? [[ply.\u003Cspan class=\"hljs-property\">move\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">from\u003C\u002Fspan>, ply.\u003Cspan class=\"hljs-property\">castle\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">kingTo\u003C\u002Fspan>], [ply.\u003Cspan class=\"hljs-property\">castle\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">rookFrom\u003C\u002Fspan>, ply.\u003Cspan class=\"hljs-property\">castle\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">rookTo\u003C\u002Fspan>]]\n    : [[ply.\u003Cspan class=\"hljs-property\">move\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">from\u003C\u002Fspan>, ply.\u003Cspan class=\"hljs-property\">move\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">to\u003C\u002Fspan>]]\n  hidden.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Set\u003C\u002Fspan>(paths.\u003Cspan class=\"hljs-title function_\">map\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">[, to]\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> to))\n  \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">nextTick\u003C\u002Fspan>()\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">rect\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">sq: number\u003C\u002Fspan>) =&gt; board.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>!.\u003Cspan class=\"hljs-property\">querySelector\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">HTMLElement\u003C\u002Fspan>&gt;(\u003Cspan class=\"hljs-string\">`[data-sq=&quot;\u003Cspan class=\"hljs-subst\">${sq}\u003C\u002Fspan>&quot;]`\u003C\u002Fspan>)!.\u003Cspan class=\"hljs-title function_\">getBoundingClientRect\u003C\u002Fspan>()\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> origin = board.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">getBoundingClientRect\u003C\u002Fspan>()\n  \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Promise\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">all\u003C\u002Fspan>(paths.\u003Cspan class=\"hljs-title function_\">map\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">[\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>, to]\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> a = \u003Cspan class=\"hljs-title function_\">rect\u003C\u002Fspan>(\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>), b = \u003Cspan class=\"hljs-title function_\">rect\u003C\u002Fspan>(to)\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> tile = \u003Cspan class=\"hljs-title function_\">makeTile\u003C\u002Fspan>(props.\u003Cspan class=\"hljs-property\">squares\u003C\u002Fspan>[to]!, a.\u003Cspan class=\"hljs-property\">width\u003C\u002Fspan>)\n    \u003Cspan class=\"hljs-title class_\">Object\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">assign\u003C\u002Fspan>(tile.\u003Cspan class=\"hljs-property\">style\u003C\u002Fspan>, { \u003Cspan class=\"hljs-attr\">left\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${a.left - origin.left}\u003C\u002Fspan>px`\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">top\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${a.top - origin.top}\u003C\u002Fspan>px`\u003C\u002Fspan> })\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> dx = b.\u003Cspan class=\"hljs-property\">left\u003C\u002Fspan> - a.\u003Cspan class=\"hljs-property\">left\u003C\u002Fspan>, dy = b.\u003Cspan class=\"hljs-property\">top\u003C\u002Fspan> - a.\u003Cspan class=\"hljs-property\">top\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> tile.\u003Cspan class=\"hljs-title function_\">animate\u003C\u002Fspan>(\n      [{ \u003Cspan class=\"hljs-attr\">transform\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;translate(0, 0)&#x27;\u003C\u002Fspan> }, { \u003Cspan class=\"hljs-attr\">transform\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`translate(\u003Cspan class=\"hljs-subst\">${dx}\u003C\u002Fspan>px, \u003Cspan class=\"hljs-subst\">${dy}\u003C\u002Fspan>px)`\u003C\u002Fspan> }],\n      { \u003Cspan class=\"hljs-attr\">duration\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">240\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">easing\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;cubic-bezier(.2, .7, .1, 1)&#x27;\u003C\u002Fspan> },\n    ).\u003Cspan class=\"hljs-property\">finished\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">finally\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> tile.\u003Cspan class=\"hljs-title function_\">remove\u003C\u002Fspan>())\n  }))\n  hidden.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Set\u003C\u002Fspan>()\n}\n\u003Cspan class=\"hljs-title function_\">watch\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> props.\u003Cspan class=\"hljs-property\">last\u003C\u002Fspan>, \u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">ply, prev\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> { \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (ply &amp;&amp; ply !== prev &amp;&amp; props.\u003Cspan class=\"hljs-property\">plyCount\u003C\u002Fspan> &gt; \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">void\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">animate\u003C\u002Fspan>(ply) })\n\u003C\u002Fspan>\u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">script\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">template\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">ref\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;board&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">class\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;board__grid&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">role\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;grid&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">aria-label\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;Chess board&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:style\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;{ fontFamily: family, ...themeVars }&quot;\u003C\u002Fspan> @\u003Cspan class=\"hljs-attr\">keydown\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;key&quot;\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">button\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-attr\">v-for\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;sq in order&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:key\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;sq&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">type\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;button&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">class\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;board__sq&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:data-sq\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;sq&quot;\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-attr\">:class\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;{\n        &#x27;is-selected&#x27;: sq === selected,\n        &#x27;is-target&#x27;: legal.has(sq),\n        &#x27;is-capture&#x27;: legal.has(sq) &amp;&amp; !!squares[sq],\n        &#x27;is-last&#x27;: last &amp;&amp; (sq === last.move.from || sq === (last.castle?.kingTo ?? last.move.to)),\n        &#x27;is-check&#x27;: sq === check,\n        &#x27;is-movable&#x27;: movable(sq),\n        &#x27;is-premove&#x27;: premove &amp;&amp; (sq === premove.from || sq === premove.to),\n      }&quot;\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-attr\">:tabindex\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;sq === focus ? 0 : -1&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:aria-label\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;label(sq)&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:aria-disabled\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;!interactive&quot;\u003C\u002Fspan>\n      @\u003Cspan class=\"hljs-attr\">click\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;clicked(sq)&quot;\u003C\u002Fspan> @\u003Cspan class=\"hljs-attr\">pointerdown\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;down(sq, $event)&quot;\u003C\u002Fspan>\n    &gt;\u003C\u002Fspan>{{ squareGlyph(hidden.has(sq) ? &#x27;&#x27; : squares[sq]!, isDarkSquare(sq), font) }}\u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">button\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">svg\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">v-if\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;arrowLines.length&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">class\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;board__arrows&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">viewBox\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;0 0 8 8&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">aria-hidden\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;true&quot;\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">defs\u003C\u002Fspan>&gt;\u003C\u002Fspan>\u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">marker\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">id\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;board-arrow&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">viewBox\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;0 0 4 4&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">refX\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;1&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">refY\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;2&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">markerUnits\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;userSpaceOnUse&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">markerWidth\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;0.42&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">markerHeight\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;0.42&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">orient\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;auto&quot;\u003C\u002Fspan>&gt;\u003C\u002Fspan>\u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">path\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">d\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;M0,0 L4,2 L0,4 z&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">fill\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;context-stroke&quot;\u003C\u002Fspan> \u002F&gt;\u003C\u002Fspan>\u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">marker\u003C\u002Fspan>&gt;\u003C\u002Fspan>\u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">defs\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">line\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">v-for\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;a in arrowLines&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:key\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;a.key&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:x1\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;a.x1&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:y1\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;a.y1&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:x2\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;a.x2&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:y2\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;a.y2&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:stroke-width\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;a.w&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:opacity\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;a.o&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">marker-end\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;url(#board-arrow)&quot;\u003C\u002Fspan> \u002F&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">svg\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">v-if\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;promoting&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">class\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;board__promote&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">role\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;dialog&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">aria-label\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;Promote to&quot;\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-tag\">&lt;\u003Cspan class=\"hljs-name\">button\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">v-for\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;r in ([&#x27;queen&#x27;, &#x27;rook&#x27;, &#x27;bishop&#x27;, &#x27;knight&#x27;] as Role[])&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:key\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;r&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">type\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;button&quot;\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">:aria-label\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;`Promote to ${r}`&quot;\u003C\u002Fspan> @\u003Cspan class=\"hljs-attr\">click\u003C\u002Fspan>=\u003Cspan class=\"hljs-string\">&quot;promote(r)&quot;\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n        {{ squareGlyph(({ queen: &#x27;Q&#x27;, rook: &#x27;R&#x27;, bishop: &#x27;B&#x27;, knight: &#x27;N&#x27; } as Record&lt;string, string&gt;)[r]![whiteToMove ? &#x27;toUpperCase&#x27; : &#x27;toLowerCase&#x27;](), false, font) }}\n      \u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">button\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">div\u003C\u002Fspan>&gt;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-tag\">&lt;\u002F\u003Cspan class=\"hljs-name\">template\u003C\u002Fspan>&gt;\u003C\u002Fspan>",{"path":177,"lines":178,"code":179,"html":180},"app\u002Fcomposables\u002FuseChessGame.ts",245,"import type { Chess } from 'chessops\u002Fchess'\nimport type { NormalMove, Role } from 'chessops\u002Ftypes'\nimport { simpleMove, startFen, startSquares } from '#core\u002Fdomain\u002Fchess960'\n\n\u002F**\n * A game of chess or Chess960 on chessops (Lichess's rules library, GPL-3.0). chessops is imported on mount, so it ships in its\n * own chunk that only this page requests (link prefetch never fetches it); until then the board shows the start\n * position and cannot be played.\n *\u002F\ntype Ops = {\n  Chess: typeof import('chessops\u002Fchess').Chess\n  castlingSide: typeof import('chessops\u002Fchess').castlingSide\n  parseFen: typeof import('chessops\u002Ffen').parseFen\n  makeFen: typeof import('chessops\u002Ffen').makeFen\n  makeSanAndPlay: typeof import('chessops\u002Fsan').makeSanAndPlay\n  makeSan: typeof import('chessops\u002Fsan').makeSan\n  pgn: typeof import('chessops\u002Fpgn')\n  kingCastlesTo: typeof import('chessops\u002Futil').kingCastlesTo\n  parseUci: typeof import('chessops\u002Futil').parseUci\n  makeUci: typeof import('chessops\u002Futil').makeUci\n}\n\u002F** Jev plays on the server (server\u002Fapi\u002Fchess\u002Fjev.post.ts). *\u002F\nexport type Player = 'human' | 'simple' | 'jev'\nexport type EngineDecision = {\n  engine: Exclude\u003CPlayer, 'human'>\n  san: string\n  ms: number\n  candidates?: { uci?: string; san: string; p: number }[]\n  confidence?: number\n  model?: string\n  options?: number\n  note?: string\n  \u002F** The only legal move: played without asking. *\u002F\n  forced?: boolean\n  \u002F** Jev's first pick gave material away and it chose again, warned. *\u002F\n  reasked?: boolean\n  \u002F** Jev was rate-limited and nothing was played (watching); try again after this many seconds. *\u002F\n  resting?: number\n  \u002F** The reading after the move: White's winning chances 0–1 (worked out), the mover's king danger, sharpness 0–1, the plan (Jev's). *\u002F\n  reading?: { eval?: number; kingRisk?: number; sharpness?: number; plan?: string }\n}\nexport type Ply = { move: NormalMove; san: string; before: Chess; castle?: { kingTo: number; rookFrom: number; rookTo: number } }\nexport type Outcome = { winner?: 'white' | 'black'; reason: string }\n\nconst LETTER: Record\u003CRole, string> = { pawn: 'p', knight: 'n', bishop: 'b', rook: 'r', queen: 'q', king: 'k' }\n\nexport function useChessGame(rank: Ref\u003Cstring>) {\n  let ops: Ops | null = null\n  const pos = shallowRef\u003CChess | null>(null)\n  const plies = shallowRef\u003CPly[]>([])\n  const ready = computed(() => pos.value !== null)\n\n  async function load() {\n    const [chess, fen, san, pgn, util] = await Promise.all([\n      import('chessops\u002Fchess'), import('chessops\u002Ffen'), import('chessops\u002Fsan'), import('chessops\u002Fpgn'), import('chessops\u002Futil'),\n    ])\n    ops = { Chess: chess.Chess, castlingSide: chess.castlingSide, parseFen: fen.parseFen, makeFen: fen.makeFen, makeSanAndPlay: san.makeSanAndPlay, makeSan: san.makeSan, pgn, kingCastlesTo: util.kingCastlesTo, parseUci: util.parseUci, makeUci: util.makeUci }\n    reset()\n  }\n  function reset() {\n    if (!ops) return\n    pos.value = ops.Chess.fromSetup(ops.parseFen(startFen(rank.value)).unwrap()).unwrap()\n    plies.value = []\n  }\n  watch(rank, reset)\n\n  \u002F** 64 FEN piece letters for a position, a1 = 0. *\u002F\n  const squaresOf = (p: Chess) => Array.from({ length: 64 }, (_, sq) => {\n    const piece = p.board.get(sq)\n    if (!piece) return ''\n    const letter = LETTER[piece.role]\n    return piece.color === 'white' ? letter.toUpperCase() : letter\n  })\n  const checkOf = (p: Chess) => (p.isCheck() ? p.board.kingOf(p.turn) : undefined)\n  const squares = computed\u003Cstring[]>(() => (pos.value ? squaresOf(pos.value) : startSquares(rank.value)))\n  const turn = computed(() => pos.value?.turn ?? 'white')\n  const check = computed(() => (pos.value ? checkOf(pos.value) : undefined))\n  \u002F** The position after `k` moves (0 is the start), for stepping back through the game. *\u002F\n  const positionAt = (k: number) => (k >= plies.value.length ? pos.value : plies.value[k]!.before)\n  const squaresAt = (k: number) => { const p = positionAt(k); return p ? squaresOf(p) : startSquares(rank.value) }\n  const checkAt = (k: number) => { const p = positionAt(k); return p ? checkOf(p) : undefined }\n  \u002F** A UCI move in the position after `k` moves, in SAN (for naming an engine's suggestion). *\u002F\n  const sanAt = (k: number, uci: string) => { const p = positionAt(k), m = ops?.parseUci(uci); return p && ops && m ? ops.makeSan(p, m) : uci }\n  \u002F** The FEN after `k` moves. *\u002F\n  const fenAt = (k: number) => { const p = positionAt(k); return p && ops ? ops.makeFen(p.toSetup()) : startFen(rank.value) }\n\n  \u002F**\n   * Where the piece on `from` may go. chessops castles by moving the king onto its own rook; the king's final\n   * square (g- or c-file) is accepted too when that is not an ordinary king move, since that is what people click.\n   *\u002F\n  function targets(from: number): Map\u003Cnumber, NormalMove> {\n    const p = pos.value, out = new Map\u003Cnumber, NormalMove>()\n    if (!p || !ops) return out\n    for (const to of p.dests(from)) {\n      const move = { from, to }\n      out.set(to, move)\n      const side = ops.castlingSide(p, move)\n      if (side) {\n        const kingTo = ops.kingCastlesTo(p.turn, side)\n        if (!out.has(kingTo) && !p.dests(from).has(kingTo)) out.set(kingTo, move)\n      }\n    }\n    return out\n  }\n  const needsPromotion = (move: NormalMove) => {\n    const p = pos.value\n    return !!p && p.board.get(move.from)?.role === 'pawn' && (move.to >> 3 === 7 || move.to >> 3 === 0)\n  }\n\n  function play(move: NormalMove): Ply | undefined {\n    const p = pos.value\n    if (!p || !ops || !p.isLegal(move)) return\n    const before = p.clone()\n    const side = ops.castlingSide(p, move)\n    const back = p.turn === 'white' ? 0 : 56\n    const castle = side ? { kingTo: back + (side === 'h' ? 6 : 2), rookFrom: move.to, rookTo: back + (side === 'h' ? 5 : 3) } : undefined\n    const next = p.clone()\n    const san = ops.makeSanAndPlay(next, move)\n    const ply: Ply = { move, san, before, castle }\n    pos.value = next\n    plies.value = [...plies.value, ply]\n    return ply\n  }\n  \u002F** The game as UCI moves, to save it; `replay` plays them back from the start, stopping at the first illegal one. *\u002F\n  const moves = computed(() => {\n    const plies_ = plies.value \u002F\u002F read first, so the value tracks the game even before chessops loads\n    return ops ? plies_.map((p) => ops!.makeUci(p.move)) : []\n  })\n  function replay(ucis: string[]) {\n    if (!ops) return\n    reset()\n    for (const uci of ucis) {\n      const m = ops.parseUci(uci) as NormalMove | undefined\n      if (!m || !play(m)) break\n    }\n  }\n  function undo() {\n    const last = plies.value.at(-1)\n    if (!last) return\n    pos.value = last.before\n    plies.value = plies.value.slice(0, -1)\n  }\n\n  const outcome = computed\u003COutcome | undefined>(() => {\n    const p = pos.value\n    if (!p) return\n    if (p.isCheckmate()) return { winner: p.turn === 'white' ? 'black' : 'white', reason: 'Checkmate' }\n    if (p.isStalemate()) return { reason: 'Stalemate' }\n    if (p.isInsufficientMaterial()) return { reason: 'Insufficient material' }\n    if (p.halfmoves >= 100) return { reason: 'Fifty-move rule' }\n    \u002F\u002F chessops leaves move history to the caller, so threefold repetition is counted here\n    if (ops) {\n      const key = (x: Chess) => ops!.makeFen(x.toSetup()).split(' ').slice(0, 4).join(' ')\n      const now = key(p)\n      if (plies.value.filter((ply) => key(ply.before) === now).length >= 2) return { reason: 'Threefold repetition' }\n    }\n    return\n  })\n\n  const fen = computed(() => (pos.value && ops ? ops.makeFen(pos.value.toSetup()) : startFen(rank.value)))\n  \u002F**\n   * The game as PGN. `marks` suffix moves (`!`, `?`, which PGN allows after a move), `comments` add a `{…}` after the\n   * move with that index, and `tags` go first in the header.\n   *\u002F\n  function pgnText(notes: { marks?: string[]; comments?: Record\u003Cnumber, string>; tags?: Record\u003Cstring, string> } = {}) {\n    \u002F\u002F read the game state first, so a computed calling this tracks it even while chessops is still loading\n    const plies_ = plies.value, o = outcome.value\n    if (!pos.value || !ops) return ''\n    const { pgn } = ops\n    const game = pgn.defaultGame\u003Cimport('chessops\u002Fpgn').PgnNodeData>(pgn.emptyHeaders)\n    \u002F\u002F the page's tags first (Event, Site, Date, White, Black: the order of PGN's own roster), then the start and result\n    for (const [k, v] of Object.entries(notes.tags ?? {})) game.headers.set(k, v)\n    \u002F\u002F chessops drops the Variant header for standard rules, so a Chess960 game sets it after the start position\n    pgn.setStartingPosition(game.headers, ops.Chess.fromSetup(ops.parseFen(startFen(rank.value)).unwrap()).unwrap())\n    if (rank.value !== 'RNBQKBNR') game.headers.set('Variant', 'Chess960')\n    game.headers.set('Result', pgn.makeOutcome(o ? { winner: o.winner } : undefined))\n    let node: import('chessops\u002Fpgn').Node\u003Cimport('chessops\u002Fpgn').PgnNodeData> = game.moves\n    plies_.forEach((ply, i) => {\n      const comment = notes.comments?.[i]\n      const child = new pgn.ChildNode({ san: ply.san + (notes.marks?.[i] ?? ''), comments: comment ? [comment] : undefined })\n      node.children.push(child)\n      node = child\n    })\n    \u002F\u002F laid out to read: the tags, then one move number per line (Jev's comments stay with their move) and the result\n    \u002F\u002F alone at the end; PGN allows a line break between any two tokens, so it still copies as valid PGN\n    const [tags, movetext = ''] = pgn.makePgn(game).trim().split(\u002F\\n\\n\u002F)\n    const lines = movetext.replace(\u002F\\s+\u002Fg, ' ').trim().replace(\u002F (?=\\d+\\. )\u002Fg, '\\n').replace(\u002F (1-0|0-1|1\\\u002F2-1\\\u002F2|\\*)$\u002F, '\\n$1')\n    return lines ? `${tags}\\n\\n${lines}` : tags!\n  }\n\n  \u002F** The simple engine (core\u002Fdomain\u002Fchess960.ts). *\u002F\n  function computerMove(): NormalMove | undefined {\n    const p = pos.value\n    return p && !outcome.value ? simpleMove(p) : undefined\n  }\n\n  \u002F**\n   * The engine adapter. If Jev cannot answer, the simple engine moves instead and the decision says so, except when\n   * `fallback` is off (watching) and Jev is rate-limited: then nothing moves and the decision says how long to rest. A late answer\n   * (the position changed meanwhile, by a reset or an undo) is never played.\n   *\u002F\n  async function engineMove(engine: Exclude\u003CPlayer, 'human'>, minMs = 0, level: 'easy' | 'normal' | 'hard' = 'hard', onDecided?: (d: EngineDecision) => void, fallback = true): Promise\u003CEngineDecision | undefined> {\n    const p = pos.value\n    if (!p || !ops || outcome.value) return\n    const at = plies.value.length, t0 = performance.now()\n    const local = (note?: string): { move?: NormalMove; decision: EngineDecision } => {\n      const move = computerMove()\n      return { move, decision: { engine: 'simple', san: '', ms: Math.round(performance.now() - t0), note } }\n    }\n    let move: NormalMove | undefined, decision: EngineDecision\n    if (engine === 'jev') {\n      try {\n        const r = await $fetch\u003C{ uci: string; san: string; candidates: { uci: string; san: string; p: number }[]; confidence?: number; model?: string; ms: number; options: number; forced?: boolean; reasked?: boolean; reading: EngineDecision['reading'] }>('\u002Fapi\u002Fchess\u002Fjev', {\n          method: 'POST',\n          body: { start: startFen(rank.value), moves: moves.value, history: plies.value.map((x) => x.san), variant: rank.value === 'RNBQKBNR' ? 'chess' : 'chess960', level },\n        })\n        const m = ops.parseUci(r.uci) as NormalMove | undefined\n        if (!m || !pos.value?.isLegal(m)) throw new Error('illegal')\n        move = m\n        decision = { engine: 'jev', san: r.san, ms: r.ms, candidates: r.candidates, confidence: r.confidence, model: r.model, options: r.options, forced: r.forced, reasked: r.reasked, reading: r.reading }\n      } catch (e) {\n        const err = e as { statusCode?: number; response?: Response }\n        const status = err.statusCode\n        \u002F\u002F watching, a stand-in would change the match: nothing is played, and the page waits and tries again\n        if (!fallback && status === 429) return { engine: 'jev', san: '', ms: 0, resting: Number(err.response?.headers.get('retry-after')) || 30 }\n        ;({ move, decision } = local(status === 429 ? 'Jev is resting (too many moves); the simple engine played.' : 'Jev did not answer; the simple engine played.'))\n      }\n    } else ({ move, decision } = local())\n    \u002F\u002F a minimum time per move, so a fast engine can still be watched; the decision is shown meanwhile\n    if (plies.value.length === at && pos.value === p) onDecided?.(decision)\n    const wait = minMs - (performance.now() - t0)\n    if (wait > 0) await new Promise((r) => setTimeout(r, wait))\n    if (!move || plies.value.length !== at || pos.value !== p) return\n    const ply = play(move)\n    return ply ? { ...decision, san: ply.san } : undefined\n  }\n\n  \u002F** Jev's guess at the reply to the current position, or undefined if it cannot say. *\u002F\n  async function predictReply(): Promise\u003C{ san: string; p: number } | undefined> {\n    if (!pos.value || outcome.value) return\n    return $fetch\u003C{ san: string; p: number }>('\u002Fapi\u002Fchess\u002Fjev-predict', { method: 'POST', body: { fen: fen.value } }).catch(() => undefined)\n  }\n\n  return { position: pos, load, ready, squares, squaresAt, checkAt, sanAt, fenAt, turn, check, targets, needsPromotion, play, undo, reset, plies, moves, replay, outcome, fen, pgnText, engineMove, predictReply }\n}","\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> { \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan> } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fchess&#x27;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> { \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>, \u003Cspan class=\"hljs-title class_\">Role\u003C\u002Fspan> } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;chessops\u002Ftypes&#x27;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> { simpleMove, startFen, startSquares } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;#core\u002Fdomain\u002Fchess960&#x27;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-comment\">\u002F**\n * A game of chess or Chess960 on chessops (Lichess&#x27;s rules library, GPL-3.0). chessops is imported on mount, so it ships in its\n * own chunk that only this page requests (link prefetch never fetches it); until then the board shows the start\n * position and cannot be played.\n *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Ops\u003C\u002Fspan> = {\n  \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fchess&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">Chess\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">castlingSide\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fchess&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">castlingSide\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">parseFen\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Ffen&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">parseFen\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">makeFen\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Ffen&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">makeFen\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">makeSanAndPlay\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fsan&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">makeSanAndPlay\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">makeSan\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fsan&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">makeSan\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">pgn\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fpgn&#x27;\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-attr\">kingCastlesTo\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Futil&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">kingCastlesTo\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">parseUci\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Futil&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">parseUci\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">makeUci\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Futil&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">makeUci\u003C\u002Fspan>\n}\n\u003Cspan class=\"hljs-comment\">\u002F** Jev plays on the server (server\u002Fapi\u002Fchess\u002Fjev.post.ts). *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Player\u003C\u002Fspan> = \u003Cspan class=\"hljs-string\">&#x27;human&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;simple&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;jev&#x27;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">EngineDecision\u003C\u002Fspan> = {\n  \u003Cspan class=\"hljs-attr\">engine\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Exclude\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">Player\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;human&#x27;\u003C\u002Fspan>&gt;\n  \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">ms\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">candidates\u003C\u002Fspan>?: { \u003Cspan class=\"hljs-attr\">uci\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">p\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan> }[]\n  \u003Cspan class=\"hljs-attr\">confidence\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">model\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">options\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">note\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-comment\">\u002F** The only legal move: played without asking. *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">forced\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">boolean\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-comment\">\u002F** Jev&#x27;s first pick gave material away and it chose again, warned. *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">reasked\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">boolean\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-comment\">\u002F** Jev was rate-limited and nothing was played (watching); try again after this many seconds. *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">resting\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-comment\">\u002F** The reading after the move: White&#x27;s winning chances 0–1 (worked out), the mover&#x27;s king danger, sharpness 0–1, the plan (Jev&#x27;s). *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">reading\u003C\u002Fspan>?: { \u003Cspan class=\"hljs-attr\">eval\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">kingRisk\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">sharpness\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">plan\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan> }\n}\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Ply\u003C\u002Fspan> = { \u003Cspan class=\"hljs-attr\">move\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">before\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">castle\u003C\u002Fspan>?: { \u003Cspan class=\"hljs-attr\">kingTo\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">rookFrom\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">rookTo\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan> } }\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Outcome\u003C\u002Fspan> = { \u003Cspan class=\"hljs-attr\">winner\u003C\u002Fspan>?: \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;black&#x27;\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">reason\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan> }\n\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">LETTER\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Record\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">Role\u003C\u002Fspan>, \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>&gt; = { \u003Cspan class=\"hljs-attr\">pawn\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;p&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">knight\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;n&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">bishop\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;b&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">rook\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;r&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">queen\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;q&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">king\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;k&#x27;\u003C\u002Fspan> }\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">useChessGame\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">rank\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Ref\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>&gt;\u003C\u002Fspan>) {\n  \u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">ops\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Ops\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan> = \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> pos = shallowRef&lt;\u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>&gt;(\u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> plies = shallowRef&lt;\u003Cspan class=\"hljs-title class_\">Ply\u003C\u002Fspan>[]&gt;([])\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> ready = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> !== \u003Cspan class=\"hljs-literal\">null\u003C\u002Fspan>)\n\n  \u003Cspan class=\"hljs-keyword\">async\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">load\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [chess, fen, san, pgn, util] = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Promise\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">all\u003C\u002Fspan>([\n      \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fchess&#x27;\u003C\u002Fspan>), \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Ffen&#x27;\u003C\u002Fspan>), \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fsan&#x27;\u003C\u002Fspan>), \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fpgn&#x27;\u003C\u002Fspan>), \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Futil&#x27;\u003C\u002Fspan>),\n    ])\n    ops = { \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan>: chess.\u003Cspan class=\"hljs-property\">Chess\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">castlingSide\u003C\u002Fspan>: chess.\u003Cspan class=\"hljs-property\">castlingSide\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">parseFen\u003C\u002Fspan>: fen.\u003Cspan class=\"hljs-property\">parseFen\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">makeFen\u003C\u002Fspan>: fen.\u003Cspan class=\"hljs-property\">makeFen\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">makeSanAndPlay\u003C\u002Fspan>: san.\u003Cspan class=\"hljs-property\">makeSanAndPlay\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">makeSan\u003C\u002Fspan>: san.\u003Cspan class=\"hljs-property\">makeSan\u003C\u002Fspan>, pgn, \u003Cspan class=\"hljs-attr\">kingCastlesTo\u003C\u002Fspan>: util.\u003Cspan class=\"hljs-property\">kingCastlesTo\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">parseUci\u003C\u002Fspan>: util.\u003Cspan class=\"hljs-property\">parseUci\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">makeUci\u003C\u002Fspan>: util.\u003Cspan class=\"hljs-property\">makeUci\u003C\u002Fspan> }\n    \u003Cspan class=\"hljs-title function_\">reset\u003C\u002Fspan>()\n  }\n  \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">reset\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!ops) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n    pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = ops.\u003Cspan class=\"hljs-property\">Chess\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">fromSetup\u003C\u002Fspan>(ops.\u003Cspan class=\"hljs-title function_\">parseFen\u003C\u002Fspan>(\u003Cspan class=\"hljs-title function_\">startFen\u003C\u002Fspan>(rank.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>)).\u003Cspan class=\"hljs-title function_\">unwrap\u003C\u002Fspan>()).\u003Cspan class=\"hljs-title function_\">unwrap\u003C\u002Fspan>()\n    plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = []\n  }\n  \u003Cspan class=\"hljs-title function_\">watch\u003C\u002Fspan>(rank, reset)\n\n  \u003Cspan class=\"hljs-comment\">\u002F** 64 FEN piece letters for a position, a1 = 0. *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">squaresOf\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">p\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; \u003Cspan class=\"hljs-title class_\">Array\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">from\u003C\u002Fspan>({ \u003Cspan class=\"hljs-attr\">length\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">64\u003C\u002Fspan> }, \u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">_, sq\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> piece = p.\u003Cspan class=\"hljs-property\">board\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">get\u003C\u002Fspan>(sq)\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!piece) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> letter = \u003Cspan class=\"hljs-variable constant_\">LETTER\u003C\u002Fspan>[piece.\u003Cspan class=\"hljs-property\">role\u003C\u002Fspan>]\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> piece.\u003Cspan class=\"hljs-property\">color\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> ? letter.\u003Cspan class=\"hljs-title function_\">toUpperCase\u003C\u002Fspan>() : letter\n  })\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">checkOf\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">p\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; (p.\u003Cspan class=\"hljs-title function_\">isCheck\u003C\u002Fspan>() ? p.\u003Cspan class=\"hljs-property\">board\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">kingOf\u003C\u002Fspan>(p.\u003Cspan class=\"hljs-property\">turn\u003C\u002Fspan>) : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> squares = computed&lt;\u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>[]&gt;(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> (pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> ? \u003Cspan class=\"hljs-title function_\">squaresOf\u003C\u002Fspan>(pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) : \u003Cspan class=\"hljs-title function_\">startSquares\u003C\u002Fspan>(rank.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>)))\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> turn = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>?.\u003Cspan class=\"hljs-property\">turn\u003C\u002Fspan> ?? \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> check = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> (pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> ? \u003Cspan class=\"hljs-title function_\">checkOf\u003C\u002Fspan>(pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>))\n  \u003Cspan class=\"hljs-comment\">\u002F** The position after `k` moves (0 is the start), for stepping back through the game. *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">positionAt\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">k\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; (k &gt;= plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan> ? pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> : plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>[k]!.\u003Cspan class=\"hljs-property\">before\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">squaresAt\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">k\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; { \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = \u003Cspan class=\"hljs-title function_\">positionAt\u003C\u002Fspan>(k); \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> p ? \u003Cspan class=\"hljs-title function_\">squaresOf\u003C\u002Fspan>(p) : \u003Cspan class=\"hljs-title function_\">startSquares\u003C\u002Fspan>(rank.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) }\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">checkAt\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">k\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; { \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = \u003Cspan class=\"hljs-title function_\">positionAt\u003C\u002Fspan>(k); \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> p ? \u003Cspan class=\"hljs-title function_\">checkOf\u003C\u002Fspan>(p) : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> }\n  \u003Cspan class=\"hljs-comment\">\u002F** A UCI move in the position after `k` moves, in SAN (for naming an engine&#x27;s suggestion). *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">sanAt\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">k\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">uci\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; { \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = \u003Cspan class=\"hljs-title function_\">positionAt\u003C\u002Fspan>(k), m = ops?.\u003Cspan class=\"hljs-title function_\">parseUci\u003C\u002Fspan>(uci); \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> p &amp;&amp; ops &amp;&amp; m ? ops.\u003Cspan class=\"hljs-title function_\">makeSan\u003C\u002Fspan>(p, m) : uci }\n  \u003Cspan class=\"hljs-comment\">\u002F** The FEN after `k` moves. *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">fenAt\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">k\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; { \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = \u003Cspan class=\"hljs-title function_\">positionAt\u003C\u002Fspan>(k); \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> p &amp;&amp; ops ? ops.\u003Cspan class=\"hljs-title function_\">makeFen\u003C\u002Fspan>(p.\u003Cspan class=\"hljs-title function_\">toSetup\u003C\u002Fspan>()) : \u003Cspan class=\"hljs-title function_\">startFen\u003C\u002Fspan>(rank.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) }\n\n  \u003Cspan class=\"hljs-comment\">\u002F**\n   * Where the piece on `from` may go. chessops castles by moving the king onto its own rook; the king&#x27;s final\n   * square (g- or c-file) is accepted too when that is not an ordinary king move, since that is what people click.\n   *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">targets\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">from\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>): \u003Cspan class=\"hljs-title class_\">Map\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>, \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>&gt; {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>, out = \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Map\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>, \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>&gt;()\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!p || !ops) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> out\n    \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> to \u003Cspan class=\"hljs-keyword\">of\u003C\u002Fspan> p.\u003Cspan class=\"hljs-title function_\">dests\u003C\u002Fspan>(\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>)) {\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> move = { \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>, to }\n      out.\u003Cspan class=\"hljs-title function_\">set\u003C\u002Fspan>(to, move)\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> side = ops.\u003Cspan class=\"hljs-title function_\">castlingSide\u003C\u002Fspan>(p, move)\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (side) {\n        \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> kingTo = ops.\u003Cspan class=\"hljs-title function_\">kingCastlesTo\u003C\u002Fspan>(p.\u003Cspan class=\"hljs-property\">turn\u003C\u002Fspan>, side)\n        \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!out.\u003Cspan class=\"hljs-title function_\">has\u003C\u002Fspan>(kingTo) &amp;&amp; !p.\u003Cspan class=\"hljs-title function_\">dests\u003C\u002Fspan>(\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>).\u003Cspan class=\"hljs-title function_\">has\u003C\u002Fspan>(kingTo)) out.\u003Cspan class=\"hljs-title function_\">set\u003C\u002Fspan>(kingTo, move)\n      }\n    }\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> out\n  }\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">needsPromotion\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">move\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> !!p &amp;&amp; p.\u003Cspan class=\"hljs-property\">board\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">get\u003C\u002Fspan>(move.\u003Cspan class=\"hljs-property\">from\u003C\u002Fspan>)?.\u003Cspan class=\"hljs-property\">role\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;pawn&#x27;\u003C\u002Fspan> &amp;&amp; (move.\u003Cspan class=\"hljs-property\">to\u003C\u002Fspan> &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan> === \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan> || move.\u003Cspan class=\"hljs-property\">to\u003C\u002Fspan> &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan> === \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>)\n  }\n\n  \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">play\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">move\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>\u003C\u002Fspan>): \u003Cspan class=\"hljs-title class_\">Ply\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!p || !ops || !p.\u003Cspan class=\"hljs-title function_\">isLegal\u003C\u002Fspan>(move)) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> before = p.\u003Cspan class=\"hljs-title function_\">clone\u003C\u002Fspan>()\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> side = ops.\u003Cspan class=\"hljs-title function_\">castlingSide\u003C\u002Fspan>(p, move)\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> back = p.\u003Cspan class=\"hljs-property\">turn\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> : \u003Cspan class=\"hljs-number\">56\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> castle = side ? { \u003Cspan class=\"hljs-attr\">kingTo\u003C\u002Fspan>: back + (side === \u003Cspan class=\"hljs-string\">&#x27;h&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-number\">6\u003C\u002Fspan> : \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>), \u003Cspan class=\"hljs-attr\">rookFrom\u003C\u002Fspan>: move.\u003Cspan class=\"hljs-property\">to\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">rookTo\u003C\u002Fspan>: back + (side === \u003Cspan class=\"hljs-string\">&#x27;h&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-number\">5\u003C\u002Fspan> : \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>) } : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> next = p.\u003Cspan class=\"hljs-title function_\">clone\u003C\u002Fspan>()\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> san = ops.\u003Cspan class=\"hljs-title function_\">makeSanAndPlay\u003C\u002Fspan>(next, move)\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">ply\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Ply\u003C\u002Fspan> = { move, san, before, castle }\n    pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = next\n    plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = [...plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>, ply]\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> ply\n  }\n  \u003Cspan class=\"hljs-comment\">\u002F** The game as UCI moves, to save it; `replay` plays them back from the start, stopping at the first illegal one. *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> moves = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> plies_ = plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> \u003Cspan class=\"hljs-comment\">\u002F\u002F read first, so the value tracks the game even before chessops loads\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> ops ? plies_.\u003Cspan class=\"hljs-title function_\">map\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">p\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> ops!.\u003Cspan class=\"hljs-title function_\">makeUci\u003C\u002Fspan>(p.\u003Cspan class=\"hljs-property\">move\u003C\u002Fspan>)) : []\n  })\n  \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">replay\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">ucis\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>[]\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!ops) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-title function_\">reset\u003C\u002Fspan>()\n    \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> uci \u003Cspan class=\"hljs-keyword\">of\u003C\u002Fspan> ucis) {\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> m = ops.\u003Cspan class=\"hljs-title function_\">parseUci\u003C\u002Fspan>(uci) \u003Cspan class=\"hljs-keyword\">as\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!m || !\u003Cspan class=\"hljs-title function_\">play\u003C\u002Fspan>(m)) \u003Cspan class=\"hljs-keyword\">break\u003C\u002Fspan>\n    }\n  }\n  \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">undo\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> last = plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">at\u003C\u002Fspan>(-\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>)\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!last) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n    pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = last.\u003Cspan class=\"hljs-property\">before\u003C\u002Fspan>\n    plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> = plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">slice\u003C\u002Fspan>(\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, -\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>)\n  }\n\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> outcome = computed&lt;\u003Cspan class=\"hljs-title class_\">Outcome\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>&gt;(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!p) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (p.\u003Cspan class=\"hljs-title function_\">isCheckmate\u003C\u002Fspan>()) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { \u003Cspan class=\"hljs-attr\">winner\u003C\u002Fspan>: p.\u003Cspan class=\"hljs-property\">turn\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;black&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">reason\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Checkmate&#x27;\u003C\u002Fspan> }\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (p.\u003Cspan class=\"hljs-title function_\">isStalemate\u003C\u002Fspan>()) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { \u003Cspan class=\"hljs-attr\">reason\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Stalemate&#x27;\u003C\u002Fspan> }\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (p.\u003Cspan class=\"hljs-title function_\">isInsufficientMaterial\u003C\u002Fspan>()) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { \u003Cspan class=\"hljs-attr\">reason\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Insufficient material&#x27;\u003C\u002Fspan> }\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (p.\u003Cspan class=\"hljs-property\">halfmoves\u003C\u002Fspan> &gt;= \u003Cspan class=\"hljs-number\">100\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { \u003Cspan class=\"hljs-attr\">reason\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Fifty-move rule&#x27;\u003C\u002Fspan> }\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F chessops leaves move history to the caller, so threefold repetition is counted here\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (ops) {\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">key\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">x\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; ops!.\u003Cspan class=\"hljs-title function_\">makeFen\u003C\u002Fspan>(x.\u003Cspan class=\"hljs-title function_\">toSetup\u003C\u002Fspan>()).\u003Cspan class=\"hljs-title function_\">split\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27; &#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-title function_\">slice\u003C\u002Fspan>(\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">4\u003C\u002Fspan>).\u003Cspan class=\"hljs-title function_\">join\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27; &#x27;\u003C\u002Fspan>)\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> now = \u003Cspan class=\"hljs-title function_\">key\u003C\u002Fspan>(p)\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">filter\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">ply\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">key\u003C\u002Fspan>(ply.\u003Cspan class=\"hljs-property\">before\u003C\u002Fspan>) === now).\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan> &gt;= \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { \u003Cspan class=\"hljs-attr\">reason\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Threefold repetition&#x27;\u003C\u002Fspan> }\n    }\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n  })\n\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> fen = \u003Cspan class=\"hljs-title function_\">computed\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> (pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> &amp;&amp; ops ? ops.\u003Cspan class=\"hljs-title function_\">makeFen\u003C\u002Fspan>(pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">toSetup\u003C\u002Fspan>()) : \u003Cspan class=\"hljs-title function_\">startFen\u003C\u002Fspan>(rank.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>)))\n  \u003Cspan class=\"hljs-comment\">\u002F**\n   * The game as PGN. `marks` suffix moves (`!`, `?`, which PGN allows after a move), `comments` add a `{…}` after the\n   * move with that index, and `tags` go first in the header.\n   *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">pgnText\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">notes\u003C\u002Fspan>: { marks?: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>[]; comments?: Record&lt;\u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>, \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>&gt;; tags?: Record&lt;\u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>, \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>&gt; } = {}\u003C\u002Fspan>) {\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F read the game state first, so a computed calling this tracks it even while chessops is still loading\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> plies_ = plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>, o = outcome.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> || !ops) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> { pgn } = ops\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> game = pgn.\u003Cspan class=\"hljs-property\">defaultGame\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fpgn&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">PgnNodeData\u003C\u002Fspan>&gt;(pgn.\u003Cspan class=\"hljs-property\">emptyHeaders\u003C\u002Fspan>)\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F the page&#x27;s tags first (Event, Site, Date, White, Black: the order of PGN&#x27;s own roster), then the start and result\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [k, v] \u003Cspan class=\"hljs-keyword\">of\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Object\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">entries\u003C\u002Fspan>(notes.\u003Cspan class=\"hljs-property\">tags\u003C\u002Fspan> ?? {})) game.\u003Cspan class=\"hljs-property\">headers\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">set\u003C\u002Fspan>(k, v)\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F chessops drops the Variant header for standard rules, so a Chess960 game sets it after the start position\u003C\u002Fspan>\n    pgn.\u003Cspan class=\"hljs-title function_\">setStartingPosition\u003C\u002Fspan>(game.\u003Cspan class=\"hljs-property\">headers\u003C\u002Fspan>, ops.\u003Cspan class=\"hljs-property\">Chess\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">fromSetup\u003C\u002Fspan>(ops.\u003Cspan class=\"hljs-title function_\">parseFen\u003C\u002Fspan>(\u003Cspan class=\"hljs-title function_\">startFen\u003C\u002Fspan>(rank.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>)).\u003Cspan class=\"hljs-title function_\">unwrap\u003C\u002Fspan>()).\u003Cspan class=\"hljs-title function_\">unwrap\u003C\u002Fspan>())\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (rank.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> !== \u003Cspan class=\"hljs-string\">&#x27;RNBQKBNR&#x27;\u003C\u002Fspan>) game.\u003Cspan class=\"hljs-property\">headers\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">set\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;Variant&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;Chess960&#x27;\u003C\u002Fspan>)\n    game.\u003Cspan class=\"hljs-property\">headers\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">set\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;Result&#x27;\u003C\u002Fspan>, pgn.\u003Cspan class=\"hljs-title function_\">makeOutcome\u003C\u002Fspan>(o ? { \u003Cspan class=\"hljs-attr\">winner\u003C\u002Fspan>: o.\u003Cspan class=\"hljs-property\">winner\u003C\u002Fspan> } : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>))\n    \u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">node\u003C\u002Fspan>: \u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fpgn&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">Node\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fpgn&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-property\">PgnNodeData\u003C\u002Fspan>&gt; = game.\u003Cspan class=\"hljs-property\">moves\u003C\u002Fspan>\n    plies_.\u003Cspan class=\"hljs-title function_\">forEach\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">ply, i\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> {\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> comment = notes.\u003Cspan class=\"hljs-property\">comments\u003C\u002Fspan>?.[i]\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> child = \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> pgn.\u003Cspan class=\"hljs-title class_\">ChildNode\u003C\u002Fspan>({ \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: ply.\u003Cspan class=\"hljs-property\">san\u003C\u002Fspan> + (notes.\u003Cspan class=\"hljs-property\">marks\u003C\u002Fspan>?.[i] ?? \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>), \u003Cspan class=\"hljs-attr\">comments\u003C\u002Fspan>: comment ? [comment] : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> })\n      node.\u003Cspan class=\"hljs-property\">children\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">push\u003C\u002Fspan>(child)\n      node = child\n    })\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F laid out to read: the tags, then one move number per line (Jev&#x27;s comments stay with their move) and the result\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F alone at the end; PGN allows a line break between any two tokens, so it still copies as valid PGN\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [tags, movetext = \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>] = pgn.\u003Cspan class=\"hljs-title function_\">makePgn\u003C\u002Fspan>(game).\u003Cspan class=\"hljs-title function_\">trim\u003C\u002Fspan>().\u003Cspan class=\"hljs-title function_\">split\u003C\u002Fspan>(\u003Cspan class=\"hljs-regexp\">\u002F\\n\\n\u002F\u003C\u002Fspan>)\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> lines = movetext.\u003Cspan class=\"hljs-title function_\">replace\u003C\u002Fspan>(\u003Cspan class=\"hljs-regexp\">\u002F\\s+\u002Fg\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27; &#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-title function_\">trim\u003C\u002Fspan>().\u003Cspan class=\"hljs-title function_\">replace\u003C\u002Fspan>(\u003Cspan class=\"hljs-regexp\">\u002F (?=\\d+\\. )\u002Fg\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;\\n&#x27;\u003C\u002Fspan>).\u003Cspan class=\"hljs-title function_\">replace\u003C\u002Fspan>(\u003Cspan class=\"hljs-regexp\">\u002F (1-0|0-1|1\\\u002F2-1\\\u002F2|\\*)$\u002F\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;\\n$1&#x27;\u003C\u002Fspan>)\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> lines ? \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${tags}\u003C\u002Fspan>\\n\\n\u003Cspan class=\"hljs-subst\">${lines}\u003C\u002Fspan>`\u003C\u002Fspan> : tags!\n  }\n\n  \u003Cspan class=\"hljs-comment\">\u002F** The simple engine (core\u002Fdomain\u002Fchess960.ts). *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">computerMove\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>): \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> p &amp;&amp; !outcome.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> ? \u003Cspan class=\"hljs-title function_\">simpleMove\u003C\u002Fspan>(p) : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n  }\n\n  \u003Cspan class=\"hljs-comment\">\u002F**\n   * The engine adapter. If Jev cannot answer, the simple engine moves instead and the decision says so, except when\n   * `fallback` is off (watching) and Jev is rate-limited: then nothing moves and the decision says how long to rest. A late answer\n   * (the position changed meanwhile, by a reset or an undo) is never played.\n   *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">async\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">engineMove\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">engine\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Exclude\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">Player\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;human&#x27;\u003C\u002Fspan>&gt;, minMs = \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">level\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;easy&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;normal&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;hard&#x27;\u003C\u002Fspan> = \u003Cspan class=\"hljs-string\">&#x27;hard&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">onDecided\u003C\u002Fspan>?: (d: EngineDecision) =&gt; \u003Cspan class=\"hljs-built_in\">void\u003C\u002Fspan>, fallback = \u003Cspan class=\"hljs-literal\">true\u003C\u002Fspan>\u003C\u002Fspan>): \u003Cspan class=\"hljs-title class_\">Promise\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">EngineDecision\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>&gt; {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> p = pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!p || !ops || outcome.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> at = plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan>, t0 = performance.\u003Cspan class=\"hljs-title function_\">now\u003C\u002Fspan>()\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> local = (\u003Cspan class=\"hljs-attr\">note\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>): { \u003Cspan class=\"hljs-attr\">move\u003C\u002Fspan>?: \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">decision\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">EngineDecision\u003C\u002Fspan> } =&gt; {\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> move = \u003Cspan class=\"hljs-title function_\">computerMove\u003C\u002Fspan>()\n      \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { move, \u003Cspan class=\"hljs-attr\">decision\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">engine\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;simple&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ms\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">round\u003C\u002Fspan>(performance.\u003Cspan class=\"hljs-title function_\">now\u003C\u002Fspan>() - t0), note } }\n    }\n    \u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">move\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">decision\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">EngineDecision\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (engine === \u003Cspan class=\"hljs-string\">&#x27;jev&#x27;\u003C\u002Fspan>) {\n      \u003Cspan class=\"hljs-keyword\">try\u003C\u002Fspan> {\n        \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> r = \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> $fetch&lt;{ \u003Cspan class=\"hljs-attr\">uci\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">candidates\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">uci\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">p\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan> }[]; \u003Cspan class=\"hljs-attr\">confidence\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">model\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">ms\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">options\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">forced\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">boolean\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">reasked\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">boolean\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">reading\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">EngineDecision\u003C\u002Fspan>[\u003Cspan class=\"hljs-string\">&#x27;reading&#x27;\u003C\u002Fspan>] }&gt;(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Fchess\u002Fjev&#x27;\u003C\u002Fspan>, {\n          \u003Cspan class=\"hljs-attr\">method\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;POST&#x27;\u003C\u002Fspan>,\n          \u003Cspan class=\"hljs-attr\">body\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">start\u003C\u002Fspan>: \u003Cspan class=\"hljs-title function_\">startFen\u003C\u002Fspan>(rank.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>), \u003Cspan class=\"hljs-attr\">moves\u003C\u002Fspan>: moves.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">history\u003C\u002Fspan>: plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">map\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">x\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> x.\u003Cspan class=\"hljs-property\">san\u003C\u002Fspan>), \u003Cspan class=\"hljs-attr\">variant\u003C\u002Fspan>: rank.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;RNBQKBNR&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;chess&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;chess960&#x27;\u003C\u002Fspan>, level },\n        })\n        \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> m = ops.\u003Cspan class=\"hljs-title function_\">parseUci\u003C\u002Fspan>(r.\u003Cspan class=\"hljs-property\">uci\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">as\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n        \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!m || !pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>?.\u003Cspan class=\"hljs-title function_\">isLegal\u003C\u002Fspan>(m)) \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Error\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;illegal&#x27;\u003C\u002Fspan>)\n        move = m\n        decision = { \u003Cspan class=\"hljs-attr\">engine\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;jev&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: r.\u003Cspan class=\"hljs-property\">san\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ms\u003C\u002Fspan>: r.\u003Cspan class=\"hljs-property\">ms\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">candidates\u003C\u002Fspan>: r.\u003Cspan class=\"hljs-property\">candidates\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">confidence\u003C\u002Fspan>: r.\u003Cspan class=\"hljs-property\">confidence\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">model\u003C\u002Fspan>: r.\u003Cspan class=\"hljs-property\">model\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">options\u003C\u002Fspan>: r.\u003Cspan class=\"hljs-property\">options\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">forced\u003C\u002Fspan>: r.\u003Cspan class=\"hljs-property\">forced\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">reasked\u003C\u002Fspan>: r.\u003Cspan class=\"hljs-property\">reasked\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">reading\u003C\u002Fspan>: r.\u003Cspan class=\"hljs-property\">reading\u003C\u002Fspan> }\n      } \u003Cspan class=\"hljs-keyword\">catch\u003C\u002Fspan> (e) {\n        \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> err = e \u003Cspan class=\"hljs-keyword\">as\u003C\u002Fspan> { \u003Cspan class=\"hljs-attr\">statusCode\u003C\u002Fspan>?: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">response\u003C\u002Fspan>?: \u003Cspan class=\"hljs-title class_\">Response\u003C\u002Fspan> }\n        \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> status = err.\u003Cspan class=\"hljs-property\">statusCode\u003C\u002Fspan>\n        \u003Cspan class=\"hljs-comment\">\u002F\u002F watching, a stand-in would change the match: nothing is played, and the page waits and tries again\u003C\u002Fspan>\n        \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!fallback &amp;&amp; status === \u003Cspan class=\"hljs-number\">429\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { \u003Cspan class=\"hljs-attr\">engine\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;jev&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ms\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">resting\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Number\u003C\u002Fspan>(err.\u003Cspan class=\"hljs-property\">response\u003C\u002Fspan>?.\u003Cspan class=\"hljs-property\">headers\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">get\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;retry-after&#x27;\u003C\u002Fspan>)) || \u003Cspan class=\"hljs-number\">30\u003C\u002Fspan> }\n        ;({ move, decision } = \u003Cspan class=\"hljs-title function_\">local\u003C\u002Fspan>(status === \u003Cspan class=\"hljs-number\">429\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;Jev is resting (too many moves); the simple engine played.&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;Jev did not answer; the simple engine played.&#x27;\u003C\u002Fspan>))\n      }\n    } \u003Cspan class=\"hljs-title function_\">else\u003C\u002Fspan> ({ move, decision } = \u003Cspan class=\"hljs-title function_\">local\u003C\u002Fspan>())\n    \u003Cspan class=\"hljs-comment\">\u002F\u002F a minimum time per move, so a fast engine can still be watched; the decision is shown meanwhile\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan> === at &amp;&amp; pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> === p) onDecided?.(decision)\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> wait = minMs - (performance.\u003Cspan class=\"hljs-title function_\">now\u003C\u002Fspan>() - t0)\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (wait &gt; \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">await\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Promise\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">r\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> \u003Cspan class=\"hljs-built_in\">setTimeout\u003C\u002Fspan>(r, wait))\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!move || plies.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan> !== at || pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> !== p) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> ply = \u003Cspan class=\"hljs-title function_\">play\u003C\u002Fspan>(move)\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> ply ? { ...decision, \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: ply.\u003Cspan class=\"hljs-property\">san\u003C\u002Fspan> } : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n  }\n\n  \u003Cspan class=\"hljs-comment\">\u002F** Jev&#x27;s guess at the reply to the current position, or undefined if it cannot say. *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">async\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">predictReply\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>): \u003Cspan class=\"hljs-title class_\">Promise\u003C\u002Fspan>&lt;{ \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">p\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan> } | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>&gt; {\n    \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!pos.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> || outcome.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> $fetch&lt;{ \u003Cspan class=\"hljs-attr\">san\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">p\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan> }&gt;(\u003Cspan class=\"hljs-string\">&#x27;\u002Fapi\u002Fchess\u002Fjev-predict&#x27;\u003C\u002Fspan>, { \u003Cspan class=\"hljs-attr\">method\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;POST&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">body\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">fen\u003C\u002Fspan>: fen.\u003Cspan class=\"hljs-property\">value\u003C\u002Fspan> } }).\u003Cspan class=\"hljs-title function_\">catch\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">() =&gt;\u003C\u002Fspan> \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>)\n  }\n\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { \u003Cspan class=\"hljs-attr\">position\u003C\u002Fspan>: pos, load, ready, squares, squaresAt, checkAt, sanAt, fenAt, turn, check, targets, needsPromotion, play, undo, reset, plies, moves, replay, outcome, fen, pgnText, engineMove, predictReply }\n}",{"path":182,"lines":183,"code":184,"html":185},"core\u002Fdomain\u002Fchess960.ts",171,"\u002F\u002F Chess960 starting positions by the standard numbering (518 is the classical setup), and the chess-font glyphs\n\u002F\u002F the board is drawn with. The rules of play are chessops'.\nimport type { Chess } from 'chessops\u002Fchess'\nimport type { NormalMove, Role } from 'chessops\u002Ftypes'\nimport type { PaletteColor } from '.\u002Fpalette'\n\nexport const STANDARD = 518\nexport const POSITIONS = 960\n\nexport const isPositionId = (n: number) => Number.isInteger(n) && n >= 0 && n \u003C POSITIONS\n\nconst divmod = (a: number, b: number) => [Math.floor(a \u002F b), a % b] as const\nconst KNIGHTS: [number, number][] = [[0, 1], [0, 2], [0, 3], [0, 4], [1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]\n\n\u002F** White's back rank for position `n`, a to h, e.g. 518 → \"RNBQKBNR\". *\u002F\nexport function backRank(n: number): string {\n  if (!isPositionId(n)) throw new Error(`Chess960 position must be 0–959, not ${n}`)\n  const rank = new Array\u003Cstring>(8).fill('')\n  const empty = () => rank.flatMap((v, i) => (v ? [] : [i]))\n  const [n2, b1] = divmod(n, 4)\n  rank[b1 * 2 + 1] = 'B' \u002F\u002F light-squared bishop: b, d, f, h\n  const [n3, b2] = divmod(n2, 4)\n  rank[b2 * 2] = 'B' \u002F\u002F dark-squared bishop: a, c, e, g\n  const [n4, q] = divmod(n3, 6)\n  rank[empty()[q]!] = 'Q'\n  const [k1, k2] = KNIGHTS[n4]!\n  const free = empty()\n  rank[free[k1]!] = 'N'\n  rank[free[k2]!] = 'N'\n  const [r1, k, r2] = empty()\n  rank[r1!] = 'R'; rank[k!] = 'K'; rank[r2!] = 'R'\n  return rank.join('')\n}\n\n\u002F** The starting position as FEN, the input chessops parses (castling rights on both sides, outermost rooks). *\u002F\nexport const startFen = (rank: string) => `${rank.toLowerCase()}\u002Fpppppppp\u002F8\u002F8\u002F8\u002F8\u002FPPPPPPPP\u002F${rank} w KQkq - 0 1`\n\n\u002F** The starting board as 64 FEN piece letters ('' for empty), indexed like chessops: a1 = 0, h1 = 7, h8 = 63. *\u002F\nexport function startSquares(rank: string): string[] {\n  const back = [...rank]\n  return Array.from({ length: 64 }, (_, sq) => {\n    const r = sq >> 3, f = sq & 7\n    return r === 0 ? back[f]! : r === 1 ? 'P' : r === 6 ? 'p' : r === 7 ? back[f]!.toLowerCase() : ''\n  })\n}\n\n\u002F** Square index to name, e.g. 0 → a1, 63 → h8. *\u002F\nexport const squareName = (sq: number) => `${'abcdefgh'[sq & 7]}${(sq >> 3) + 1}`\n\u002F** a1 is dark, so a square is dark when its file and rank indices add up to an even number. *\u002F\nexport const isDarkSquare = (sq: number) => ((sq & 7) + (sq >> 3)) % 2 === 0\n\n\u002F\u002F Chess fonts draw a piece (or empty square) per character, already shaded for its square: each map gives\n\u002F\u002F [on a dark square, on a light square]. Most fonts share Merida's layout; Utrecht swaps the colours and\n\u002F\u002F Lucena's empty light square is wider.\ntype CharMap = Record\u003Cstring, [string, string]>\nconst MERIDA: CharMap = {\n  'k': ['L', 'l'], 'q': ['W', 'w'], 'r': ['T', 't'], 'b': ['V', 'v'], 'n': ['M', 'm'], 'p': ['O', 'o'],\n  'K': ['K', 'k'], 'Q': ['Q', 'q'], 'R': ['R', 'r'], 'B': ['B', 'b'], 'N': ['N', 'n'], 'P': ['P', 'p'],\n  '+': ['+', ' '],\n}\nconst UTRECHT: CharMap = {\n  'k': ['L', 'K'], 'q': ['W', 'Q'], 'r': ['T', 'R'], 'b': ['V', 'B'], 'n': ['M', 'N'], 'p': ['O', 'P'],\n  'K': ['l', 'k'], 'Q': ['w', 'q'], 'R': ['t', 'r'], 'B': ['v', 'b'], 'N': ['m', 'n'], 'P': ['o', 'p'],\n  '+': ['\u002F', ' '],\n}\nconst LUCENA: CharMap = { ...MERIDA, '+': ['+', '  '] }\n\nexport const FONTS = {\n  merida: MERIDA, adventurer: MERIDA, alfonsox: MERIDA, condal: MERIDA, harlequin: MERIDA, kingdom: MERIDA,\n  leipzig: MERIDA, line: MERIDA, lucena: LUCENA, magnetic: MERIDA, mark: MERIDA, maya: MERIDA,\n  mediaeval: MERIDA, millennia2: MERIDA, miscel: MERIDA, motif: MERIDA, utrecht: UTRECHT,\n} as const satisfies Record\u003Cstring, CharMap>\nexport type ChessFont = keyof typeof FONTS\nexport const isChessFont = (v: unknown): v is ChessFont => typeof v === 'string' && v in FONTS\n\n\u002F**\n * Board colour themes, all from the palette. The board is drawn by the piece font (hatched dark squares, outlined\n * white pieces, solid black ones), so a theme is a light ground under darker lines, in Ink or in colour: a dark ground would swap which\n * pieces read as white. `mark` shades the last move and the selected square.\n *\u002F\nexport const BOARD_THEMES = {\n  paper: { ground: 'paper', ink: 'ink', mark: 'highlight' },\n  stone: { ground: 'stone', ink: 'ink', mark: 'paper' },\n  sea: { ground: 'sea', ink: 'ink', mark: 'paper' },\n  slate: { ground: 'slate', ink: 'ink', mark: 'paper' },\n  mauve: { ground: 'mauve', ink: 'ink', mark: 'paper' },\n  sand: { ground: 'sand', ink: 'ink', mark: 'paper' },\n  signal: { ground: 'amber', ink: 'ink', mark: 'amber-light' },\n  \u002F\u002F the lines in colour\n  honey: { ground: 'amber-light', ink: 'amber-deep', mark: 'paper' },\n  rust: { ground: 'paper', ink: 'rust', mark: 'stone' },\n  clay: { ground: 'enamel', ink: 'clay', mark: 'paper' },\n  graphite: { ground: 'paper', ink: 'dark', mark: 'enamel' },\n} as const satisfies Record\u003Cstring, { ground: PaletteColor | 'highlight'; ink: PaletteColor; mark: PaletteColor | 'highlight' }>\nexport type BoardTheme = keyof typeof BOARD_THEMES\nexport const isBoardTheme = (v: unknown): v is BoardTheme => typeof v === 'string' && v in BOARD_THEMES\n\nexport const squareGlyph = (piece: string, dark: boolean, font: ChessFont = 'merida') => FONTS[font][piece || '+']![dark ? 0 : 1]\n\nexport const pieceGlyph = (piece: string, font: ChessFont = 'merida', dark = false) => FONTS[font][piece]?.[dark ? 0 : 1] ?? ''\n\n\u002F** A font's sample: the White pieces on alternating squares, light first, as one line. *\u002F\nexport const fontSample = (font: ChessFont) => [...'KQRBNP'].map((piece, i) => FONTS[font][piece]![i % 2 === 0 ? 1 : 0]).join('')\n\nexport const NAMES: Record\u003Cstring, string> = { R: 'Rook', N: 'Knight', B: 'Bishop', Q: 'Queen', K: 'King', P: 'Pawn' }\nconst FILES = 'abcdefgh'\n\nexport const setup = (rank: string) =>\n  [...rank].map((piece, i) => ({ piece, name: NAMES[piece]!, white: `${FILES[i]}1`, black: `${FILES[i]}8` }))\n\n\u002F**\n * The simple engine: mate if it can, otherwise the best capture or check, avoiding squares it would lose. It works on\n * a chessops position it is handed (types only here, so core never loads chessops).\n *\u002F\nexport function simpleMove(p: Chess): NormalMove | undefined {\n  const VALUE: Record\u003CRole, number> = { pawn: 1, knight: 3, bishop: 3, rook: 5, queen: 9, king: 0 }\n  let best: { move: NormalMove; score: number } | undefined\n  for (const [from, tos] of p.allDests()) {\n    const mover = p.board.get(from)!\n    for (const to of tos) {\n      const move: NormalMove = { from, to, promotion: mover.role === 'pawn' && (to >> 3 === 7 || to >> 3 === 0) ? 'queen' : undefined }\n      const after = p.clone()\n      after.play(move)\n      let score = Math.random() * 0.5\n      if (after.isCheckmate()) score += 1000\n      if (after.isCheck()) score += 0.6\n      const taken = p.board.get(to)\n      if (taken && taken.color !== mover.color) score += VALUE[taken.role] * 10\n      if (move.promotion) score += 80\n      const landed = after.board.get(move.to)\n      if (landed && [...after.allDests().values()].some((d) => d.has(move.to))) score -= VALUE[landed.role] * 9\n      if (!best || score > best.score) best = { move, score }\n    }\n  }\n  return best?.move\n}\n\nconst POINTS: Record\u003CRole, number> = { pawn: 1, knight: 3, bishop: 3, rook: 5, queen: 9, king: 0 }\n\u002F** White's material minus Black's, in points. *\u002F\nexport function materialBalance(p: Chess): number {\n  let n = 0\n  for (const sq of p.board.occupied) { const x = p.board.get(sq)!; n += (x.color === 'white' ? 1 : -1) * POINTS[x.role] }\n  return n\n}\n\n\u002F**\n * What a game's material says about it. `balance` is White's lead before each move and after the last (length plies\n * + 1). A move is marked when, with the reply, it won (`!`) or lost (`?`) 3 points or more, and the move that allowed a\n * mate is marked `?`. The summary names the mate, and when the winner went ahead for good and by how much when that\n * was 3 points or more; in a draw, how the material ended.\n *\u002F\nexport function materialStory(balance: number[], sans: string[], winner?: 'white' | 'black'): { marks: ('' | '!' | '?')[]; summary: string } {\n  const marks = sans.map((_, i) => {\n    const s = i % 2 === 0 ? 1 : -1, j = Math.min(i + 2, balance.length - 1)\n    const d = s * (balance[j]! - balance[i]!)\n    return d >= 3 ? '!' : d \u003C= -3 ? '?' : ''\n  }) as ('' | '!' | '?')[]\n  const end = balance.at(-1)!\n  if (!winner) return { marks, summary: end === 0 ? 'Material ended even.' : `${end > 0 ? 'White' : 'Black'} ended ${Math.abs(end)} point${Math.abs(end) === 1 ? '' : 's'} up.` }\n  const s = winner === 'white' ? 1 : -1, name = winner === 'white' ? 'White' : 'Black'\n  \u002F\u002F a win is a checkmate: the loser's last move allowed it\n  if (sans.length >= 2 && !marks[sans.length - 2]) marks[sans.length - 2] = '?'\n  const mate = `${name} mated on move ${Math.floor((sans.length - 1) \u002F 2) + 1} with ${sans.at(-1)}.`\n  let from = balance.length - 1\n  while (from > 0 && s * balance[from - 1]! > 0) from--\n  if (s * balance[from]! \u003C= 0) return { marks, summary: `${mate} No material lead needed.` }\n  const most = Math.max(...balance.slice(from).map((b) => s * b))\n  if (most \u003C 3) return { marks, summary: mate }\n  const ply = from - 1 \u002F\u002F the move that put the winner ahead\n  return { marks, summary: `${mate} Ahead from move ${Math.floor(ply \u002F 2) + 1} (${sans[ply]}), by up to ${most} points.` }\n}","\u003Cspan class=\"hljs-comment\">\u002F\u002F Chess960 starting positions by the standard numbering (518 is the classical setup), and the chess-font glyphs\u003C\u002Fspan>\n\u003Cspan class=\"hljs-comment\">\u002F\u002F the board is drawn with. The rules of play are chessops&#x27;.\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> { \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan> } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;chessops\u002Fchess&#x27;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> { \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>, \u003Cspan class=\"hljs-title class_\">Role\u003C\u002Fspan> } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;chessops\u002Ftypes&#x27;\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">import\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> { \u003Cspan class=\"hljs-title class_\">PaletteColor\u003C\u002Fspan> } \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> \u003Cspan class=\"hljs-string\">&#x27;.\u002Fpalette&#x27;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">STANDARD\u003C\u002Fspan> = \u003Cspan class=\"hljs-number\">518\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">POSITIONS\u003C\u002Fspan> = \u003Cspan class=\"hljs-number\">960\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">isPositionId\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">n\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; \u003Cspan class=\"hljs-title class_\">Number\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">isInteger\u003C\u002Fspan>(n) &amp;&amp; n &gt;= \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> &amp;&amp; n &lt; \u003Cspan class=\"hljs-variable constant_\">POSITIONS\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">divmod\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">a\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">b\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; [\u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">floor\u003C\u002Fspan>(a \u002F b), a % b] \u003Cspan class=\"hljs-keyword\">as\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">KNIGHTS\u003C\u002Fspan>: [\u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>, \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>][] = [[\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>], [\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>], [\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>], [\u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">4\u003C\u002Fspan>], [\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>], [\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>], [\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">4\u003C\u002Fspan>], [\u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>], [\u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">4\u003C\u002Fspan>], [\u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>, \u003Cspan class=\"hljs-number\">4\u003C\u002Fspan>]]\n\n\u003Cspan class=\"hljs-comment\">\u002F** White&#x27;s back rank for position `n`, a to h, e.g. 518 → &quot;RNBQKBNR&quot;. *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">backRank\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">n\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>): \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan> {\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!\u003Cspan class=\"hljs-title function_\">isPositionId\u003C\u002Fspan>(n)) \u003Cspan class=\"hljs-keyword\">throw\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Error\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">`Chess960 position must be 0–959, not \u003Cspan class=\"hljs-subst\">${n}\u003C\u002Fspan>`\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> rank = \u003Cspan class=\"hljs-keyword\">new\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Array\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>&gt;(\u003Cspan class=\"hljs-number\">8\u003C\u002Fspan>).\u003Cspan class=\"hljs-title function_\">fill\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>)\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">empty\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003C\u002Fspan>) =&gt; rank.\u003Cspan class=\"hljs-title function_\">flatMap\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">v, i\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> (v ? [] : [i]))\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [n2, b1] = \u003Cspan class=\"hljs-title function_\">divmod\u003C\u002Fspan>(n, \u003Cspan class=\"hljs-number\">4\u003C\u002Fspan>)\n  rank[b1 * \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan> + \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>] = \u003Cspan class=\"hljs-string\">&#x27;B&#x27;\u003C\u002Fspan> \u003Cspan class=\"hljs-comment\">\u002F\u002F light-squared bishop: b, d, f, h\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [n3, b2] = \u003Cspan class=\"hljs-title function_\">divmod\u003C\u002Fspan>(n2, \u003Cspan class=\"hljs-number\">4\u003C\u002Fspan>)\n  rank[b2 * \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>] = \u003Cspan class=\"hljs-string\">&#x27;B&#x27;\u003C\u002Fspan> \u003Cspan class=\"hljs-comment\">\u002F\u002F dark-squared bishop: a, c, e, g\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [n4, q] = \u003Cspan class=\"hljs-title function_\">divmod\u003C\u002Fspan>(n3, \u003Cspan class=\"hljs-number\">6\u003C\u002Fspan>)\n  rank[\u003Cspan class=\"hljs-title function_\">empty\u003C\u002Fspan>()[q]!] = \u003Cspan class=\"hljs-string\">&#x27;Q&#x27;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [k1, k2] = \u003Cspan class=\"hljs-variable constant_\">KNIGHTS\u003C\u002Fspan>[n4]!\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> free = \u003Cspan class=\"hljs-title function_\">empty\u003C\u002Fspan>()\n  rank[free[k1]!] = \u003Cspan class=\"hljs-string\">&#x27;N&#x27;\u003C\u002Fspan>\n  rank[free[k2]!] = \u003Cspan class=\"hljs-string\">&#x27;N&#x27;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [r1, k, r2] = \u003Cspan class=\"hljs-title function_\">empty\u003C\u002Fspan>()\n  rank[r1!] = \u003Cspan class=\"hljs-string\">&#x27;R&#x27;\u003C\u002Fspan>; rank[k!] = \u003Cspan class=\"hljs-string\">&#x27;K&#x27;\u003C\u002Fspan>; rank[r2!] = \u003Cspan class=\"hljs-string\">&#x27;R&#x27;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> rank.\u003Cspan class=\"hljs-title function_\">join\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>)\n}\n\n\u003Cspan class=\"hljs-comment\">\u002F** The starting position as FEN, the input chessops parses (castling rights on both sides, outermost rooks). *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">startFen\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">rank\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${rank.toLowerCase()}\u003C\u002Fspan>\u002Fpppppppp\u002F8\u002F8\u002F8\u002F8\u002FPPPPPPPP\u002F\u003Cspan class=\"hljs-subst\">${rank}\u003C\u002Fspan> w KQkq - 0 1`\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-comment\">\u002F** The starting board as 64 FEN piece letters (&#x27;&#x27; for empty), indexed like chessops: a1 = 0, h1 = 7, h8 = 63. *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">startSquares\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">rank\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>\u003C\u002Fspan>): \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>[] {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> back = [...rank]\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Array\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">from\u003C\u002Fspan>({ \u003Cspan class=\"hljs-attr\">length\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">64\u003C\u002Fspan> }, \u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">_, sq\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> r = sq &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>, f = sq &amp; \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan>\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> r === \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> ? back[f]! : r === \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;P&#x27;\u003C\u002Fspan> : r === \u003Cspan class=\"hljs-number\">6\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;p&#x27;\u003C\u002Fspan> : r === \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan> ? back[f]!.\u003Cspan class=\"hljs-title function_\">toLowerCase\u003C\u002Fspan>() : \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>\n  })\n}\n\n\u003Cspan class=\"hljs-comment\">\u002F** Square index to name, e.g. 0 → a1, 63 → h8. *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">squareName\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">sq\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${\u003Cspan class=\"hljs-string\">&#x27;abcdefgh&#x27;\u003C\u002Fspan>[sq &amp; \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan>]}\u003C\u002Fspan>\u003Cspan class=\"hljs-subst\">${(sq &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>) + \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>}\u003C\u002Fspan>`\u003C\u002Fspan>\n\u003Cspan class=\"hljs-comment\">\u002F** a1 is dark, so a square is dark when its file and rank indices add up to an even number. *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">isDarkSquare\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">sq\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; ((sq &amp; \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan>) + (sq &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>)) % \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan> === \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-comment\">\u002F\u002F Chess fonts draw a piece (or empty square) per character, already shaded for its square: each map gives\u003C\u002Fspan>\n\u003Cspan class=\"hljs-comment\">\u002F\u002F [on a dark square, on a light square]. Most fonts share Merida&#x27;s layout; Utrecht swaps the colours and\u003C\u002Fspan>\n\u003Cspan class=\"hljs-comment\">\u002F\u002F Lucena&#x27;s empty light square is wider.\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">CharMap\u003C\u002Fspan> = \u003Cspan class=\"hljs-title class_\">Record\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>, [\u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>, \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>]&gt;\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">MERIDA\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">CharMap\u003C\u002Fspan> = {\n  \u003Cspan class=\"hljs-string\">&#x27;k&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;L&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;l&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;q&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;W&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;w&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;r&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;T&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;t&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;b&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;V&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;v&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;n&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;M&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;m&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;p&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;O&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;o&#x27;\u003C\u002Fspan>],\n  \u003Cspan class=\"hljs-string\">&#x27;K&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;K&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;k&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;Q&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;Q&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;q&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;R&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;R&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;r&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;B&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;B&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;b&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;N&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;N&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;n&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;P&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;P&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;p&#x27;\u003C\u002Fspan>],\n  \u003Cspan class=\"hljs-string\">&#x27;+&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;+&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27; &#x27;\u003C\u002Fspan>],\n}\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">UTRECHT\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">CharMap\u003C\u002Fspan> = {\n  \u003Cspan class=\"hljs-string\">&#x27;k&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;L&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;K&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;q&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;W&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;Q&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;r&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;T&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;R&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;b&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;V&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;B&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;n&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;M&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;N&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;p&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;O&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;P&#x27;\u003C\u002Fspan>],\n  \u003Cspan class=\"hljs-string\">&#x27;K&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;l&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;k&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;Q&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;w&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;q&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;R&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;t&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;r&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;B&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;v&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;b&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;N&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;m&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;n&#x27;\u003C\u002Fspan>], \u003Cspan class=\"hljs-string\">&#x27;P&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;o&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;p&#x27;\u003C\u002Fspan>],\n  \u003Cspan class=\"hljs-string\">&#x27;+&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;\u002F&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27; &#x27;\u003C\u002Fspan>],\n}\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">LUCENA\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">CharMap\u003C\u002Fspan> = { ...\u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;+&#x27;\u003C\u002Fspan>: [\u003Cspan class=\"hljs-string\">&#x27;+&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-string\">&#x27;  &#x27;\u003C\u002Fspan>] }\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">FONTS\u003C\u002Fspan> = {\n  \u003Cspan class=\"hljs-attr\">merida\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">adventurer\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">alfonsox\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">condal\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">harlequin\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">kingdom\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>,\n  \u003Cspan class=\"hljs-attr\">leipzig\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">line\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">lucena\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">LUCENA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">magnetic\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">maya\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>,\n  \u003Cspan class=\"hljs-attr\">mediaeval\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">millennia2\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">miscel\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">motif\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">MERIDA\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">utrecht\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">UTRECHT\u003C\u002Fspan>,\n} \u003Cspan class=\"hljs-keyword\">as\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">satisfies\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Record\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>, \u003Cspan class=\"hljs-title class_\">CharMap\u003C\u002Fspan>&gt;\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">ChessFont\u003C\u002Fspan> = keyof \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">FONTS\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> isChessFont = (\u003Cspan class=\"hljs-attr\">v\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">unknown\u003C\u002Fspan>): v is \u003Cspan class=\"hljs-title class_\">ChessFont\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> v === \u003Cspan class=\"hljs-string\">&#x27;string&#x27;\u003C\u002Fspan> &amp;&amp; v \u003Cspan class=\"hljs-keyword\">in\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">FONTS\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-comment\">\u002F**\n * Board colour themes, all from the palette. The board is drawn by the piece font (hatched dark squares, outlined\n * white pieces, solid black ones), so a theme is a light ground under darker lines, in Ink or in colour: a dark ground would swap which\n * pieces read as white. `mark` shades the last move and the selected square.\n *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">BOARD_THEMES\u003C\u002Fspan> = {\n  \u003Cspan class=\"hljs-attr\">paper\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;ink&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;highlight&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-attr\">stone\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;stone&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;ink&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-attr\">sea\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;sea&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;ink&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-attr\">slate\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;slate&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;ink&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-attr\">mauve\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;mauve&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;ink&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-attr\">sand\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;sand&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;ink&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-attr\">signal\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;amber&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;ink&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;amber-light&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-comment\">\u002F\u002F the lines in colour\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-attr\">honey\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;amber-light&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;amber-deep&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-attr\">rust\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;rust&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;stone&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-attr\">clay\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;enamel&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;clay&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan> },\n  \u003Cspan class=\"hljs-attr\">graphite\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;paper&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;dark&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;enamel&#x27;\u003C\u002Fspan> },\n} \u003Cspan class=\"hljs-keyword\">as\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">satisfies\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">Record\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>, { \u003Cspan class=\"hljs-attr\">ground\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">PaletteColor\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;highlight&#x27;\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">ink\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">PaletteColor\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">mark\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">PaletteColor\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;highlight&#x27;\u003C\u002Fspan> }&gt;\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">type\u003C\u002Fspan> \u003Cspan class=\"hljs-title class_\">BoardTheme\u003C\u002Fspan> = keyof \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">BOARD_THEMES\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> isBoardTheme = (\u003Cspan class=\"hljs-attr\">v\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">unknown\u003C\u002Fspan>): v is \u003Cspan class=\"hljs-title class_\">BoardTheme\u003C\u002Fspan> =&gt; \u003Cspan class=\"hljs-keyword\">typeof\u003C\u002Fspan> v === \u003Cspan class=\"hljs-string\">&#x27;string&#x27;\u003C\u002Fspan> &amp;&amp; v \u003Cspan class=\"hljs-keyword\">in\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">BOARD_THEMES\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">squareGlyph\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">piece\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">dark\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">boolean\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">font\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">ChessFont\u003C\u002Fspan> = \u003Cspan class=\"hljs-string\">&#x27;merida&#x27;\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; \u003Cspan class=\"hljs-variable constant_\">FONTS\u003C\u002Fspan>[font][piece || \u003Cspan class=\"hljs-string\">&#x27;+&#x27;\u003C\u002Fspan>]![dark ? \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> : \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>]\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">pieceGlyph\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">piece\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">font\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">ChessFont\u003C\u002Fspan> = \u003Cspan class=\"hljs-string\">&#x27;merida&#x27;\u003C\u002Fspan>, dark = \u003Cspan class=\"hljs-literal\">false\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; \u003Cspan class=\"hljs-variable constant_\">FONTS\u003C\u002Fspan>[font][piece]?.[dark ? \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> : \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>] ?? \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-comment\">\u002F** A font&#x27;s sample: the White pieces on alternating squares, light first, as one line. *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">fontSample\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">font\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">ChessFont\u003C\u002Fspan>\u003C\u002Fspan>) =&gt; [...\u003Cspan class=\"hljs-string\">&#x27;KQRBNP&#x27;\u003C\u002Fspan>].\u003Cspan class=\"hljs-title function_\">map\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">piece, i\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">FONTS\u003C\u002Fspan>[font][piece]![i % \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan> === \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> ? \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan> : \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>]).\u003Cspan class=\"hljs-title function_\">join\u003C\u002Fspan>(\u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>)\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">NAMES\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Record\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>, \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>&gt; = { \u003Cspan class=\"hljs-attr\">R\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Rook&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">N\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Knight&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">B\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Bishop&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">Q\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Queen&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">K\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;King&#x27;\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">P\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">&#x27;Pawn&#x27;\u003C\u002Fspan> }\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-variable constant_\">FILES\u003C\u002Fspan> = \u003Cspan class=\"hljs-string\">&#x27;abcdefgh&#x27;\u003C\u002Fspan>\n\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">setup\u003C\u002Fspan> = (\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">rank\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>\u003C\u002Fspan>) =&gt;\n  [...rank].\u003Cspan class=\"hljs-title function_\">map\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">piece, i\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> ({ piece, \u003Cspan class=\"hljs-attr\">name\u003C\u002Fspan>: \u003Cspan class=\"hljs-variable constant_\">NAMES\u003C\u002Fspan>[piece]!, \u003Cspan class=\"hljs-attr\">white\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${FILES[i]}\u003C\u002Fspan>1`\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">black\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${FILES[i]}\u003C\u002Fspan>8`\u003C\u002Fspan> }))\n\n\u003Cspan class=\"hljs-comment\">\u002F**\n * The simple engine: mate if it can, otherwise the best capture or check, avoiding squares it would lose. It works on\n * a chessops position it is handed (types only here, so core never loads chessops).\n *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">simpleMove\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">p\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan>\u003C\u002Fspan>): \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan> | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">VALUE\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Record\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">Role\u003C\u002Fspan>, \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>&gt; = { \u003Cspan class=\"hljs-attr\">pawn\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">knight\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">bishop\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">rook\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">5\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">queen\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">9\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">king\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> }\n  \u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">best\u003C\u002Fspan>: { \u003Cspan class=\"hljs-attr\">move\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan>; \u003Cspan class=\"hljs-attr\">score\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan> } | \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> [\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>, tos] \u003Cspan class=\"hljs-keyword\">of\u003C\u002Fspan> p.\u003Cspan class=\"hljs-title function_\">allDests\u003C\u002Fspan>()) {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> mover = p.\u003Cspan class=\"hljs-property\">board\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">get\u003C\u002Fspan>(\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>)!\n    \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> to \u003Cspan class=\"hljs-keyword\">of\u003C\u002Fspan> tos) {\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">move\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">NormalMove\u003C\u002Fspan> = { \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>, to, \u003Cspan class=\"hljs-attr\">promotion\u003C\u002Fspan>: mover.\u003Cspan class=\"hljs-property\">role\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;pawn&#x27;\u003C\u002Fspan> &amp;&amp; (to &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan> === \u003Cspan class=\"hljs-number\">7\u003C\u002Fspan> || to &gt;&gt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan> === \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>) ? \u003Cspan class=\"hljs-string\">&#x27;queen&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-literal\">undefined\u003C\u002Fspan> }\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> after = p.\u003Cspan class=\"hljs-title function_\">clone\u003C\u002Fspan>()\n      after.\u003Cspan class=\"hljs-title function_\">play\u003C\u002Fspan>(move)\n      \u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> score = \u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">random\u003C\u002Fspan>() * \u003Cspan class=\"hljs-number\">0.5\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (after.\u003Cspan class=\"hljs-title function_\">isCheckmate\u003C\u002Fspan>()) score += \u003Cspan class=\"hljs-number\">1000\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (after.\u003Cspan class=\"hljs-title function_\">isCheck\u003C\u002Fspan>()) score += \u003Cspan class=\"hljs-number\">0.6\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> taken = p.\u003Cspan class=\"hljs-property\">board\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">get\u003C\u002Fspan>(to)\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (taken &amp;&amp; taken.\u003Cspan class=\"hljs-property\">color\u003C\u002Fspan> !== mover.\u003Cspan class=\"hljs-property\">color\u003C\u002Fspan>) score += \u003Cspan class=\"hljs-variable constant_\">VALUE\u003C\u002Fspan>[taken.\u003Cspan class=\"hljs-property\">role\u003C\u002Fspan>] * \u003Cspan class=\"hljs-number\">10\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (move.\u003Cspan class=\"hljs-property\">promotion\u003C\u002Fspan>) score += \u003Cspan class=\"hljs-number\">80\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> landed = after.\u003Cspan class=\"hljs-property\">board\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">get\u003C\u002Fspan>(move.\u003Cspan class=\"hljs-property\">to\u003C\u002Fspan>)\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (landed &amp;&amp; [...after.\u003Cspan class=\"hljs-title function_\">allDests\u003C\u002Fspan>().\u003Cspan class=\"hljs-title function_\">values\u003C\u002Fspan>()].\u003Cspan class=\"hljs-title function_\">some\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">d\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> d.\u003Cspan class=\"hljs-title function_\">has\u003C\u002Fspan>(move.\u003Cspan class=\"hljs-property\">to\u003C\u002Fspan>))) score -= \u003Cspan class=\"hljs-variable constant_\">VALUE\u003C\u002Fspan>[landed.\u003Cspan class=\"hljs-property\">role\u003C\u002Fspan>] * \u003Cspan class=\"hljs-number\">9\u003C\u002Fspan>\n      \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!best || score &gt; best.\u003Cspan class=\"hljs-property\">score\u003C\u002Fspan>) best = { move, score }\n    }\n  }\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> best?.\u003Cspan class=\"hljs-property\">move\u003C\u002Fspan>\n}\n\n\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> \u003Cspan class=\"hljs-attr\">POINTS\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Record\u003C\u002Fspan>&lt;\u003Cspan class=\"hljs-title class_\">Role\u003C\u002Fspan>, \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>&gt; = { \u003Cspan class=\"hljs-attr\">pawn\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">knight\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">bishop\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">rook\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">5\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">queen\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">9\u003C\u002Fspan>, \u003Cspan class=\"hljs-attr\">king\u003C\u002Fspan>: \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> }\n\u003Cspan class=\"hljs-comment\">\u002F** White&#x27;s material minus Black&#x27;s, in points. *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">materialBalance\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">p\u003C\u002Fspan>: \u003Cspan class=\"hljs-title class_\">Chess\u003C\u002Fspan>\u003C\u002Fspan>): \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan> {\n  \u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> n = \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">for\u003C\u002Fspan> (\u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> sq \u003Cspan class=\"hljs-keyword\">of\u003C\u002Fspan> p.\u003Cspan class=\"hljs-property\">board\u003C\u002Fspan>.\u003Cspan class=\"hljs-property\">occupied\u003C\u002Fspan>) { \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> x = p.\u003Cspan class=\"hljs-property\">board\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">get\u003C\u002Fspan>(sq)!; n += (x.\u003Cspan class=\"hljs-property\">color\u003C\u002Fspan> === \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan> : -\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>) * \u003Cspan class=\"hljs-variable constant_\">POINTS\u003C\u002Fspan>[x.\u003Cspan class=\"hljs-property\">role\u003C\u002Fspan>] }\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> n\n}\n\n\u003Cspan class=\"hljs-comment\">\u002F**\n * What a game&#x27;s material says about it. `balance` is White&#x27;s lead before each move and after the last (length plies\n * + 1). A move is marked when, with the reply, it won (`!`) or lost (`?`) 3 points or more, and the move that allowed a\n * mate is marked `?`. The summary names the mate, and when the winner went ahead for good and by how much when that\n * was 3 points or more; in a draw, how the material ended.\n *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"hljs-keyword\">export\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">function\u003C\u002Fspan> \u003Cspan class=\"hljs-title function_\">materialStory\u003C\u002Fspan>(\u003Cspan class=\"hljs-params\">\u003Cspan class=\"hljs-attr\">balance\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">number\u003C\u002Fspan>[], \u003Cspan class=\"hljs-attr\">sans\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan>[], \u003Cspan class=\"hljs-attr\">winner\u003C\u002Fspan>?: \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;black&#x27;\u003C\u002Fspan>\u003C\u002Fspan>): { \u003Cspan class=\"hljs-attr\">marks\u003C\u002Fspan>: (\u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;!&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;?&#x27;\u003C\u002Fspan>)[]; \u003Cspan class=\"hljs-attr\">summary\u003C\u002Fspan>: \u003Cspan class=\"hljs-built_in\">string\u003C\u002Fspan> } {\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> marks = sans.\u003Cspan class=\"hljs-title function_\">map\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">_, i\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> {\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> s = i % \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan> === \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> ? \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan> : -\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, j = \u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">min\u003C\u002Fspan>(i + \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>, balance.\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan> - \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>)\n    \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> d = s * (balance[j]! - balance[i]!)\n    \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> d &gt;= \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;!&#x27;\u003C\u002Fspan> : d &lt;= -\u003Cspan class=\"hljs-number\">3\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;?&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan>\n  }) \u003Cspan class=\"hljs-title function_\">as\u003C\u002Fspan> (\u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;!&#x27;\u003C\u002Fspan> | \u003Cspan class=\"hljs-string\">&#x27;?&#x27;\u003C\u002Fspan>)[]\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> end = balance.\u003Cspan class=\"hljs-title function_\">at\u003C\u002Fspan>(-\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>)!\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (!winner) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { marks, \u003Cspan class=\"hljs-attr\">summary\u003C\u002Fspan>: end === \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;Material ended even.&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${end &gt; \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;White&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;Black&#x27;\u003C\u002Fspan>}\u003C\u002Fspan> ended \u003Cspan class=\"hljs-subst\">${\u003Cspan class=\"hljs-built_in\">Math\u003C\u002Fspan>.abs(end)}\u003C\u002Fspan> point\u003Cspan class=\"hljs-subst\">${\u003Cspan class=\"hljs-built_in\">Math\u003C\u002Fspan>.abs(end) === \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;s&#x27;\u003C\u002Fspan>}\u003C\u002Fspan> up.`\u003C\u002Fspan> }\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> s = winner === \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan> : -\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>, name = winner === \u003Cspan class=\"hljs-string\">&#x27;white&#x27;\u003C\u002Fspan> ? \u003Cspan class=\"hljs-string\">&#x27;White&#x27;\u003C\u002Fspan> : \u003Cspan class=\"hljs-string\">&#x27;Black&#x27;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-comment\">\u002F\u002F a win is a checkmate: the loser&#x27;s last move allowed it\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (sans.\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan> &gt;= \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan> &amp;&amp; !marks[sans.\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan> - \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>]) marks[sans.\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan> - \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>] = \u003Cspan class=\"hljs-string\">&#x27;?&#x27;\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> mate = \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${name}\u003C\u002Fspan> mated on move \u003Cspan class=\"hljs-subst\">${\u003Cspan class=\"hljs-built_in\">Math\u003C\u002Fspan>.floor((sans.length - \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>) \u002F \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>) + \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>}\u003C\u002Fspan> with \u003Cspan class=\"hljs-subst\">${sans.at(-\u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>)}\u003C\u002Fspan>.`\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">let\u003C\u002Fspan> \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> = balance.\u003Cspan class=\"hljs-property\">length\u003C\u002Fspan> - \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">while\u003C\u002Fspan> (\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> &gt; \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan> &amp;&amp; s * balance[\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> - \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>]! &gt; \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>--\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (s * balance[\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>]! &lt;= \u003Cspan class=\"hljs-number\">0\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { marks, \u003Cspan class=\"hljs-attr\">summary\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${mate}\u003C\u002Fspan> No material lead needed.`\u003C\u002Fspan> }\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> most = \u003Cspan class=\"hljs-title class_\">Math\u003C\u002Fspan>.\u003Cspan class=\"hljs-title function_\">max\u003C\u002Fspan>(...balance.\u003Cspan class=\"hljs-title function_\">slice\u003C\u002Fspan>(\u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan>).\u003Cspan class=\"hljs-title function_\">map\u003C\u002Fspan>(\u003Cspan class=\"hljs-function\">(\u003Cspan class=\"hljs-params\">b\u003C\u002Fspan>) =&gt;\u003C\u002Fspan> s * b))\n  \u003Cspan class=\"hljs-keyword\">if\u003C\u002Fspan> (most &lt; \u003Cspan class=\"hljs-number\">3\u003C\u002Fspan>) \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { marks, \u003Cspan class=\"hljs-attr\">summary\u003C\u002Fspan>: mate }\n  \u003Cspan class=\"hljs-keyword\">const\u003C\u002Fspan> ply = \u003Cspan class=\"hljs-keyword\">from\u003C\u002Fspan> - \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan> \u003Cspan class=\"hljs-comment\">\u002F\u002F the move that put the winner ahead\u003C\u002Fspan>\n  \u003Cspan class=\"hljs-keyword\">return\u003C\u002Fspan> { marks, \u003Cspan class=\"hljs-attr\">summary\u003C\u002Fspan>: \u003Cspan class=\"hljs-string\">`\u003Cspan class=\"hljs-subst\">${mate}\u003C\u002Fspan> Ahead from move \u003Cspan class=\"hljs-subst\">${\u003Cspan class=\"hljs-built_in\">Math\u003C\u002Fspan>.floor(ply \u002F \u003Cspan class=\"hljs-number\">2\u003C\u002Fspan>) + \u003Cspan class=\"hljs-number\">1\u003C\u002Fspan>}\u003C\u002Fspan> (\u003Cspan class=\"hljs-subst\">${sans[ply]}\u003C\u002Fspan>), by up to \u003Cspan class=\"hljs-subst\">${most}\u003C\u002Fspan> points.`\u003C\u002Fspan> }\n}",1790333479781]