diff --git a/package.json b/package.json index d206732..701321c 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "eslint-config-prettier": "^10.1.8", "eslint-import-resolver-typescript": "^4.4.4", "eslint-plugin-import-x": "^4.16.2", - "eslint-plugin-unicorn": "^74.0.0", + "eslint-plugin-unicorn": "^75.0.0", "husky": "^9.1.7", "lint-staged": "^17.0.5", "prettier": "^3.8.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4c89249..32beb13 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,8 +40,8 @@ importers: specifier: ^4.16.2 version: 4.17.1(@typescript-eslint/utils@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0))(eslint@10.10.0) eslint-plugin-unicorn: - specifier: ^74.0.0 - version: 74.0.0(eslint@10.10.0) + specifier: ^75.0.0 + version: 75.0.0(eslint@10.10.0) husky: specifier: ^9.1.7 version: 9.1.7 @@ -65,7 +65,7 @@ importers: version: 8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0) vitest: specifier: ^5.0.0 - version: 5.0.1(@vitest/coverage-v8@5.0.1)(vite@8.0.16(yaml@2.9.0)) + version: 5.0.1(@vitest/coverage-v8@5.0.1)(vite@8.0.16(yaml@2.9.1)) packages: @@ -968,8 +968,8 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - baseline-browser-mapping@2.11.22: - resolution: {integrity: sha512-pWc4w51fBFd7mav43/zKRC+RI6f4yfzQoVlfvE8dECePyfkn1bzLp01Fj0QACcyCZyFhiEMyD2qScfKRWgWibA==} + baseline-browser-mapping@2.11.24: + resolution: {integrity: sha512-hYrgxie335U08WqICoGqKRzV1HFXv6zdxwJE4ekCb80CM9a0SVVsN4QPwT67RraRo+9h8IATk6uxHJw7QSkdOg==} engines: {node: '>=6.0.0'} hasBin: true @@ -981,8 +981,8 @@ packages: resolution: {integrity: sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==} engines: {node: 20 || >=22} - browserslist@4.28.9: - resolution: {integrity: sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==} + browserslist@4.29.0: + resolution: {integrity: sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1015,10 +1015,6 @@ packages: resolution: {integrity: sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==} engines: {node: '>= 12.0.0'} - convert-hrtime@5.0.0: - resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} - engines: {node: '>=12'} - core-js-compat@3.50.0: resolution: {integrity: sha512-XGpFGbMLHwSt74YLTKho7Ib242qi6O8MSX+sRokV4oz7iKXvQWGYZthjIhjRGMxjzVkAubBO512dKGYcefmX3Q==} engines: {node: '>=6.4.0'} @@ -1059,8 +1055,8 @@ packages: oxc-resolver: optional: true - electron-to-chromium@1.5.427: - resolution: {integrity: sha512-n14zb3FdsChZ2BNobqNHAJMcP3ifFv4paox2LvCrfVAQcqGiSURgbJl+PfMpHVCNFkStnNc+RRVtPBTVW5PDgw==} + electron-to-chromium@1.5.430: + resolution: {integrity: sha512-e1QEj72Y4zd8RlNZVmoTg+iCOSVwpk05IOiiQwdrkwCSVlZfPthevErhE+nckGd2YbsXfp1SkisznhGVIXP2NQ==} empathic@2.0.1: resolution: {integrity: sha512-YGRs8knHhKHVShLkFET/rWAU8kmHbOV5LwN938RHI0pljAJ1Gf6SzXsSmRaEzcXTtOOmVqJ5+WtQPL5uigY50Q==} @@ -1126,8 +1122,8 @@ packages: eslint-import-resolver-node: optional: true - eslint-plugin-unicorn@74.0.0: - resolution: {integrity: sha512-AGnsGi2SxHg1HEAXxn9nSnZfyjvTWkxm8E8hpd/9tD6dLjBUdcD7+D6ZN64HmmCXTSXlrwVyUqe20Uyb2CaurA==} + eslint-plugin-unicorn@75.0.0: + resolution: {integrity: sha512-72I7eSn+/z5CpgValFBl9mp2JE5UPD+nUUyB/VIGUMF1rCP1i37mA05zfIAVIU312A+qCZ38T8tSZr3oaMv4Mw==} engines: {node: '>=22'} peerDependencies: eslint: '>=10.4' @@ -1221,10 +1217,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - function-timeout@1.0.2: - resolution: {integrity: sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==} - engines: {node: '>=18'} - get-tsconfig@4.14.0: resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} @@ -1297,10 +1289,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-identifier@1.1.0: - resolution: {integrity: sha512-NhOds0mDx9lJu+1lBRO0xbwFo5nobA7GCk/0e5xjr6+6XugX985+0OyGX35BNrTkPAsdLcIKg02HUQJOK8D8kw==} - engines: {node: '>=18'} - isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1420,10 +1408,6 @@ packages: magicast@0.5.5: resolution: {integrity: sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==} - make-asynchronous@1.1.0: - resolution: {integrity: sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==} - engines: {node: '>=18'} - markdown-it@14.3.0: resolution: {integrity: sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==} hasBin: true @@ -1470,10 +1454,6 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - p-event@6.0.1: - resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} - engines: {node: '>=16.17'} - p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -1482,10 +1462,6 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-timeout@6.1.4: - resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} - engines: {node: '>=14.16'} - path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1615,14 +1591,6 @@ packages: resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} engines: {node: '>=12'} - super-regex@1.1.0: - resolution: {integrity: sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==} - engines: {node: '>=18'} - - time-span@5.1.0: - resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} - engines: {node: '>=12'} - tinybench@6.1.4: resolution: {integrity: sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==} engines: {node: '>=20.0.0'} @@ -1694,10 +1662,6 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@4.41.0: - resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} - engines: {node: '>=16'} - typedoc@0.28.20: resolution: {integrity: sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==} engines: {node: '>= 18', pnpm: '>= 10'} @@ -1828,9 +1792,6 @@ packages: jsdom: optional: true - web-worker@1.5.0: - resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1850,6 +1811,11 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yaml@2.9.1: + resolution: {integrity: sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==} + engines: {node: '>= 14.6'} + hasBin: true + yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -2394,7 +2360,7 @@ snapshots: obug: 2.2.1 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 5.0.1(@vitest/coverage-v8@5.0.1)(vite@8.0.16(yaml@2.9.0)) + vitest: 5.0.1(@vitest/coverage-v8@5.0.1)(vite@8.0.16(yaml@2.9.1)) '@vitest/eslint-plugin@1.6.27(@typescript-eslint/eslint-plugin@8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0))(@typescript/typescript6@6.0.2)(eslint@10.10.0))(@typescript/typescript6@6.0.2)(eslint@10.10.0)(vitest@5.0.1)': dependencies: @@ -2404,7 +2370,7 @@ snapshots: optionalDependencies: '@typescript-eslint/eslint-plugin': 8.70.0(@typescript-eslint/parser@8.70.0(@typescript/typescript6@6.0.2)(eslint@10.10.0))(@typescript/typescript6@6.0.2)(eslint@10.10.0) typescript: '@typescript/typescript6@6.0.2' - vitest: 5.0.1(@vitest/coverage-v8@5.0.1)(vite@8.0.16(yaml@2.9.0)) + vitest: 5.0.1(@vitest/coverage-v8@5.0.1)(vite@8.0.16(yaml@2.9.1)) transitivePeerDependencies: - supports-color @@ -2414,14 +2380,14 @@ snapshots: dependencies: '@vitest/istanbul-lib-coverage': 1.0.1 - '@vitest/mocker@5.0.1(vite@8.0.16(yaml@2.9.0))': + '@vitest/mocker@5.0.1(vite@8.0.16(yaml@2.9.1))': dependencies: '@jridgewell/trace-mapping': 0.3.31 '@vitest/spy': 5.0.1 estree-walker: 3.0.3 magic-string: 1.4.1 optionalDependencies: - vite: 8.0.16(yaml@2.9.0) + vite: 8.0.16(yaml@2.9.1) '@vitest/spy@5.0.1': {} @@ -2524,7 +2490,7 @@ snapshots: balanced-match@4.0.4: {} - baseline-browser-mapping@2.11.22: {} + baseline-browser-mapping@2.11.24: {} brace-expansion@5.0.7: dependencies: @@ -2534,13 +2500,13 @@ snapshots: dependencies: balanced-match: 4.0.4 - browserslist@4.28.9: + browserslist@4.29.0: dependencies: - baseline-browser-mapping: 2.11.22 + baseline-browser-mapping: 2.11.24 caniuse-lite: 1.0.30001810 - electron-to-chromium: 1.5.427 + electron-to-chromium: 1.5.430 node-releases: 2.0.55 - update-browserslist-db: 1.3.3(browserslist@4.28.9) + update-browserslist-db: 1.3.3(browserslist@4.29.0) builtin-modules@5.3.0: {} @@ -2564,11 +2530,9 @@ snapshots: comment-parser@1.4.7: {} - convert-hrtime@5.0.0: {} - core-js-compat@3.50.0: dependencies: - browserslist: 4.28.9 + browserslist: 4.29.0 cross-spawn@7.0.6: dependencies: @@ -2590,7 +2554,7 @@ snapshots: dts-resolver@3.0.0: {} - electron-to-chromium@1.5.427: {} + electron-to-chromium@1.5.430: {} empathic@2.0.1: {} @@ -2647,11 +2611,11 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-unicorn@74.0.0(eslint@10.10.0): + eslint-plugin-unicorn@75.0.0(eslint@10.10.0): dependencies: '@eslint-community/eslint-utils': 4.10.1(eslint@10.10.0) '@eslint/css-tree': 4.1.0 - browserslist: 4.28.9 + browserslist: 4.29.0 change-case: 5.4.4 ci-info: 4.4.0 core-js-compat: 3.50.0 @@ -2660,16 +2624,16 @@ snapshots: eslint: 10.10.0 find-up-simple: 1.0.1 globals: 17.12.0 + identifier-regex: 1.1.0 indent-string: 5.0.0 is-builtin-module: 5.0.0 - is-identifier: 1.1.0 pluralize: 8.0.0 quote-js-string: 0.1.0 regjsparser: 0.13.2 reserved-identifiers: 1.2.0 semver: 7.8.5 strip-indent: 4.1.1 - yaml: 2.9.0 + yaml: 2.9.1 eslint-scope@9.1.2: dependencies: @@ -2773,8 +2737,6 @@ snapshots: fsevents@2.3.3: optional: true - function-timeout@1.0.2: {} - get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -2829,11 +2791,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-identifier@1.1.0: - dependencies: - identifier-regex: 1.1.0 - super-regex: 1.1.0 - isexe@2.0.0: {} js-tokens@10.0.0: {} @@ -2930,12 +2887,6 @@ snapshots: '@babel/types': 7.29.8 source-map-js: 1.2.1 - make-asynchronous@1.1.0: - dependencies: - p-event: 6.0.1 - type-fest: 4.41.0 - web-worker: 1.5.0 - markdown-it@14.3.0: dependencies: argparse: 2.0.1 @@ -2978,10 +2929,6 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - p-event@6.0.1: - dependencies: - p-timeout: 6.1.4 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -2990,8 +2937,6 @@ snapshots: dependencies: p-limit: 3.1.0 - p-timeout@6.1.4: {} - path-exists@4.0.0: {} path-key@3.1.1: {} @@ -3110,16 +3055,6 @@ snapshots: strip-indent@4.1.1: {} - super-regex@1.1.0: - dependencies: - function-timeout: 1.0.2 - make-asynchronous: 1.1.0 - time-span: 5.1.0 - - time-span@5.1.0: - dependencies: - convert-hrtime: 5.0.0 - tinybench@6.1.4: {} tinyexec@1.3.0: {} @@ -3170,8 +3105,6 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-fest@4.41.0: {} - typedoc@0.28.20(@typescript/typescript6@6.0.2): dependencies: '@gerrit0/mini-shiki': 3.23.0 @@ -3251,9 +3184,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 - update-browserslist-db@1.3.3(browserslist@4.28.9): + update-browserslist-db@1.3.3(browserslist@4.29.0): dependencies: - browserslist: 4.28.9 + browserslist: 4.29.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -3263,7 +3196,7 @@ snapshots: verkit@0.4.0: {} - vite@8.0.16(yaml@2.9.0): + vite@8.0.16(yaml@2.9.1): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -3272,12 +3205,12 @@ snapshots: tinyglobby: 0.2.17 optionalDependencies: fsevents: 2.3.3 - yaml: 2.9.0 + yaml: 2.9.1 - vitest@5.0.1(@vitest/coverage-v8@5.0.1)(vite@8.0.16(yaml@2.9.0)): + vitest@5.0.1(@vitest/coverage-v8@5.0.1)(vite@8.0.16(yaml@2.9.1)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.1(vite@8.0.16(yaml@2.9.0)) + '@vitest/mocker': 5.0.1(vite@8.0.16(yaml@2.9.1)) chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 @@ -3288,15 +3221,13 @@ snapshots: tinybench: 6.1.4 tinyexec: 1.3.0 tinyglobby: 0.2.17 - vite: 8.0.16(yaml@2.9.0) + vite: 8.0.16(yaml@2.9.1) why-is-node-running: 2.3.0 optionalDependencies: '@vitest/coverage-v8': 5.0.1(vitest@5.0.1) transitivePeerDependencies: - msw - web-worker@1.5.0: {} - which@2.0.2: dependencies: isexe: 2.0.0 @@ -3310,6 +3241,8 @@ snapshots: yaml@2.9.0: {} + yaml@2.9.1: {} + yocto-queue@0.1.0: {} yuku-ast@0.9.5: diff --git a/src/board.ts b/src/board.ts index e80dccc..73e2e9c 100644 --- a/src/board.ts +++ b/src/board.ts @@ -59,10 +59,7 @@ function bitmaskToPiece(value: number): Piece | undefined { } const color: Color = (value & COLOR_MASK) === 0 ? 'white' : 'black'; const type = NUM_TO_PIECE_TYPE[value & TYPE_MASK]; - if (type === undefined) { - return undefined; - } - return { color, type }; + return type === undefined ? undefined : { color, type }; } export { diff --git a/src/moves.ts b/src/moves.ts index 278916a..eb5c848 100644 --- a/src/moves.ts +++ b/src/moves.ts @@ -2,7 +2,9 @@ import type { PieceMove } from './types.js'; // Pawn moves are hardcoded in Position.reach (src/position.ts:345) -/** Knight move offsets on the 0x88 board — 8 L-shaped hops. */ +/** +Knight move offsets on the 0x88 board — 8 L-shaped hops. +*/ const KNIGHT_MOVES: readonly PieceMove[] = [ { offset: -33 }, { offset: -31 }, @@ -14,7 +16,9 @@ const KNIGHT_MOVES: readonly PieceMove[] = [ { offset: 33 }, ]; -/** Bishop move directions on the 0x88 board — 4 diagonals, sliding. */ +/** +Bishop move directions on the 0x88 board — 4 diagonals, sliding. +*/ const BISHOP_MOVES: readonly PieceMove[] = [ { offset: -17, slide: true }, { offset: -15, slide: true }, @@ -22,7 +26,9 @@ const BISHOP_MOVES: readonly PieceMove[] = [ { offset: 17, slide: true }, ]; -/** Rook move directions on the 0x88 board — 4 rank/file directions, sliding. */ +/** +Rook move directions on the 0x88 board — 4 rank/file directions, sliding. +*/ const ROOK_MOVES: readonly PieceMove[] = [ { offset: -16, slide: true }, { offset: -1, slide: true }, @@ -30,7 +36,9 @@ const ROOK_MOVES: readonly PieceMove[] = [ { offset: 16, slide: true }, ]; -/** King move offsets on the 0x88 board — 8 adjacent squares. */ +/** +King move offsets on the 0x88 board — 8 adjacent squares. +*/ const KING_MOVES: readonly PieceMove[] = [ { offset: -17 }, { offset: -16 }, diff --git a/src/position.ts b/src/position.ts index 9a7da08..ca2a7da 100644 --- a/src/position.ts +++ b/src/position.ts @@ -89,15 +89,25 @@ export class Position { #hash: string | undefined; #isCheck: boolean | undefined; - /** Which castling moves remain available. */ + /** + Which castling moves remain available. + */ readonly castlingRights: CastlingRights; - /** En passant target square (rank 3 or 6), if any. */ + /** + En passant target square (rank 3 or 6), if any. + */ readonly enPassantSquare: EnPassantSquare | undefined; - /** Game turn counter — increments after each black move. */ + /** + Game turn counter — increments after each black move. + */ readonly fullmoveNumber: number; - /** Half-moves since last pawn advance or capture (fifty-move rule). */ + /** + Half-moves since last pawn advance or capture (fifty-move rule). + */ readonly halfmoveClock: number; - /** Side to move. */ + /** + Side to move. + */ readonly turn: Color; /** @@ -128,6 +138,30 @@ export class Position { this.turn = options.turn; } + #isAttackedByPieceType( + square: Square, + friendlyColor: Color, + enemyColor: Color, + type: PieceType, + ): boolean { + const squares = this.reach(square, { color: friendlyColor, type }); + for (const sq of squares) { + const p = this.at(sq); + if (p === undefined || p.color !== enemyColor) { + continue; + } + + if ( + (type === 'rook' && (p.type === 'rook' || p.type === 'queen')) || + (type === 'bishop' && (p.type === 'bishop' || p.type === 'queen')) || + p.type === type + ) { + return true; + } + } + return false; + } + // Color trick: from the target square, call reach() pretending a friendly // piece of each type is there. reach() skips friendly pieces and stops at // enemies. If the enemy piece found matches the type we're checking, it @@ -145,23 +179,10 @@ export class Position { 'king', 'pawn', ] as PieceType[]) { - const squares = this.reach(square, { color: friendlyColor, type }); - for (const sq of squares) { - const p = this.at(sq); - if (p !== undefined && p.color === enemyColor) { - if (type === 'rook' && (p.type === 'rook' || p.type === 'queen')) { - return true; - } - if ( - type === 'bishop' && - (p.type === 'bishop' || p.type === 'queen') - ) { - return true; - } - if (p.type === type) { - return true; - } - } + if ( + this.#isAttackedByPieceType(square, friendlyColor, enemyColor, type) + ) { + return true; } } return false; @@ -243,7 +264,9 @@ export class Position { return this.#hash; } - /** Whether the side to move is in check. Computed once and cached. */ + /** + Whether the side to move is in check. Computed once and cached. + */ get isCheck(): boolean { if (this.#isCheck !== undefined) { return this.#isCheck; @@ -302,8 +325,9 @@ export class Position { if (nonKingPieces.length === 1) { const first = nonKingPieces[0]; - if (first === undefined) return false; - return first.type === BISHOP || first.type === KNIGHT; + return first === undefined + ? false + : first.type === BISHOP || first.type === KNIGHT; } const isAllBishops = nonKingPieces.every((p) => p.type === BISHOP); @@ -350,11 +374,13 @@ export class Position { } } - if (type === PAWN) { - const rank = 8 - ((index >> 4) & 0x07); - if (rank === 1 || rank === 8) { - return false; - } + if (type !== PAWN) { + continue; + } + + const rank = 8 - ((index >> 4) & 0x07); + if (rank === 1 || rank === 8) { + return false; } } @@ -377,18 +403,14 @@ export class Position { } } - if (opponentKingSquare === undefined) { - return false; - } - - return !this.#isSquareAttackedBy( - opponentKingSquare, - opponentColor, - this.turn, - ); + return opponentKingSquare === undefined + ? false + : !this.#isSquareAttackedBy(opponentKingSquare, opponentColor, this.turn); } - /** Returns the piece on the given square, or `undefined` if empty. */ + /** + Returns the piece on the given square, or `undefined` if empty. + */ at(square: Square): Piece | undefined { return bitmaskToPiece(this.#board[squareToIndex(square)] ?? 0); } @@ -419,7 +441,9 @@ export class Position { }); } - /** Returns a map of all pieces on the board, optionally filtered by color. */ + /** + Returns a map of all pieces on the board, optionally filtered by color. + */ pieces(color?: Color): Map { const result = new Map(); const colorFilter = @@ -431,10 +455,10 @@ export class Position { continue; } const value = this.#board[index] ?? 0; - if (value === 0) { - continue; - } - if (colorFilter !== undefined && (value & COLOR_MASK) !== colorFilter) { + if ( + value === 0 || + (colorFilter !== undefined && (value & COLOR_MASK) !== colorFilter) + ) { continue; } const p = bitmaskToPiece(value); diff --git a/src/types.ts b/src/types.ts index 9b36811..ebefd23 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,16 +1,26 @@ -/** Side to move — `'white'` or `'black'`. */ +/** +Side to move — `'white'` or `'black'`. +*/ type Color = 'black' | 'white'; -/** Board file (column), `'a'` through `'h'`. */ +/** +Board file (column), `'a'` through `'h'`. +*/ type File = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h'; -/** Chess piece type: bishop, king, knight, pawn, queen, or rook. */ +/** +Chess piece type: bishop, king, knight, pawn, queen, or rook. +*/ type PieceType = 'bishop' | 'king' | 'knight' | 'pawn' | 'queen' | 'rook'; -/** Board rank (row), `'1'` through `'8'`. */ +/** +Board rank (row), `'1'` through `'8'`. +*/ type Rank = '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8'; -/** En passant target square — always on rank 3 or rank 6. */ +/** +En passant target square — always on rank 3 or rank 6. +*/ type EnPassantSquare = `${File}${'3' | '6'}`; /** @@ -20,19 +30,31 @@ type EnPassantSquare = `${File}${'3' | '6'}`; */ type Square = `${File}${Rank}`; -/** Castling availability for one side. */ +/** +Castling availability for one side. +*/ interface SideCastlingRights { - /** Can castle kingside. */ + /** + Can castle kingside. + */ king: boolean; - /** Can castle queenside. */ + /** + Can castle queenside. + */ queen: boolean; } -/** Which castling moves remain available for each side. */ +/** +Which castling moves remain available for each side. +*/ interface CastlingRights { - /** Black's castling rights. */ + /** + Black's castling rights. + */ black: SideCastlingRights; - /** White's castling rights. */ + /** + White's castling rights. + */ white: SideCastlingRights; } @@ -54,11 +76,17 @@ interface DeriveOptions extends Omit { * omitted fields use defaults (standard starting position values). */ interface PositionData { - /** Piece placement as a map from square to piece. Defaults to an empty board. */ + /** + Piece placement as a map from square to piece. Defaults to an empty board. + */ board?: ReadonlyMap; - /** Castling availability. Defaults to all four castling moves available. */ + /** + Castling availability. Defaults to all four castling moves available. + */ castlingRights?: CastlingRights; - /** En passant target square, if any. */ + /** + En passant target square, if any. + */ enPassantSquare?: EnPassantSquare; /** * Game turn counter — starts at `1` and increments after each black move. @@ -71,36 +99,60 @@ interface PositionData { * per side) either player may claim a draw. Defaults to `0`. */ halfmoveClock?: number; - /** Side to move. Defaults to `'white'`. */ + /** + Side to move. Defaults to `'white'`. + */ turn?: Color; } -/** A piece type that a pawn can promote to — bishop, knight, queen, or rook. */ +/** +A piece type that a pawn can promote to — bishop, knight, queen, or rook. +*/ type PromotionPieceType = Exclude; -/** A chess move — origin square, target square, and optional promotion. */ +/** +A chess move — origin square, target square, and optional promotion. +*/ interface Move { - /** The square the piece moves from. */ + /** + The square the piece moves from. + */ from: Square; - /** The piece type to promote to, if this is a promotion move. */ + /** + The piece type to promote to, if this is a promotion move. + */ promotion?: PromotionPieceType; - /** The square the piece moves to. */ + /** + The square the piece moves to. + */ to: Square; } -/** A chess piece — color and type. */ +/** +A chess piece — color and type. +*/ interface Piece { - /** The piece's color. */ + /** + The piece's color. + */ color: Color; - /** The piece's type. */ + /** + The piece's type. + */ type: PieceType; } -/** Describes a piece movement on the 0x88 board. */ +/** +Describes a piece movement on the 0x88 board. +*/ interface PieceMove { - /** 0x88 offset for this move direction. */ + /** + 0x88 offset for this move direction. + */ offset: number; - /** Whether the piece slides along this direction (bishop, rook, queen). */ + /** + Whether the piece slides along this direction (bishop, rook, queen). + */ slide?: boolean; } diff --git a/typedoc-plugin-collapse-square.mjs b/typedoc-plugin-collapse-square.mjs index 5eb10e7..b1f69ea 100644 --- a/typedoc-plugin-collapse-square.mjs +++ b/typedoc-plugin-collapse-square.mjs @@ -35,8 +35,7 @@ function matchCollapsible(members) { * If a union is exactly an expanded type alias, replace it entirely. */ function isExactExpansion(type) { - if (type?.type !== 'union') return; - return matchCollapsible(type.types); + return type?.type === 'union' ? matchCollapsible(type.types) : undefined; } /** @@ -118,7 +117,9 @@ function collapse(type, makeReference) { return type; } -/** @param {import('typedoc').Application} app */ +/** +@param {import('typedoc').Application} app +*/ export function load(app) { app.converter.on(Converter.EVENT_RESOLVE_END, (context) => { const project = context.project; @@ -144,7 +145,9 @@ export function load(app) { // Walk all reflections and replace expanded unions for (const reflection of Object.values(project.reflections)) { - /** @type {any} */ + /** + @type {any} + */ const r = reflection; if (r.type) {