diff --git a/package-lock.json b/package-lock.json index 071f824..da25645 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,30 +1,30 @@ { - "name": "@sudoku/core", - "version": "1.0.0", + "name": "sudoku-core", + "version": "1.1.10", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@sudoku/core", - "version": "1.0.0", + "name": "sudoku-core", + "version": "1.1.10", "hasInstallScript": true, "license": "MIT", "devDependencies": { - "@commitlint/cli": "^18.2.0", - "@commitlint/config-conventional": "^18.1.0", - "@types/jest": "^29.5.8", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", - "eslint": "^8.53.0", - "eslint-config-prettier": "^9.0.0", + "@commitlint/cli": "^18.4.3", + "@commitlint/config-conventional": "^18.4.3", + "@types/jest": "^29.5.10", + "@typescript-eslint/eslint-plugin": "^6.13.1", + "@typescript-eslint/parser": "^6.13.1", + "eslint": "^8.55.0", + "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.0.1", "husky": "^8.0.3", "jest": "^29.7.0", "pinst": "^3.0.0", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "ts-jest": "^29.1.1", - "ts-loader": "^9.5.0", - "typescript": "^5.2.2", + "ts-loader": "^9.5.1", + "typescript": "^5.3.2", "webpack": "^5.89.0", "webpack-cli": "^5.1.4" } @@ -716,16 +716,16 @@ "dev": true }, "node_modules/@commitlint/cli": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.2.0.tgz", - "integrity": "sha512-F/DCG791kMFmWg5eIdogakuGeg4OiI2kD430ed1a1Hh3epvrJdeIAgcGADAMIOmF+m0S1+VlIYUKG2dvQQ1Izw==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-18.4.3.tgz", + "integrity": "sha512-zop98yfB3A6NveYAZ3P1Mb6bIXuCeWgnUfVNkH4yhIMQpQfzFwseadazOuSn0OOfTt0lWuFauehpm9GcqM5lww==", "dev": true, "dependencies": { - "@commitlint/format": "^18.1.0", - "@commitlint/lint": "^18.1.0", - "@commitlint/load": "^18.2.0", - "@commitlint/read": "^18.1.0", - "@commitlint/types": "^18.1.0", + "@commitlint/format": "^18.4.3", + "@commitlint/lint": "^18.4.3", + "@commitlint/load": "^18.4.3", + "@commitlint/read": "^18.4.3", + "@commitlint/types": "^18.4.3", "execa": "^5.0.0", "lodash.isfunction": "^3.0.9", "resolve-from": "5.0.0", @@ -740,9 +740,9 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.1.0.tgz", - "integrity": "sha512-8vvvtV3GOLEMHeKc8PjRL1lfP1Y4B6BG0WroFd9PJeRiOc3nFX1J0wlJenLURzl9Qus6YXVGWf+a/ZlbCKT3AA==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-18.4.3.tgz", + "integrity": "sha512-729eRRaNta7JZF07qf6SAGSghoDEp9mH7yHU0m7ff0q89W97wDrWCyZ3yoV3mcQJwbhlmVmZPTkPcm7qiAu8WA==", "dev": true, "dependencies": { "conventional-changelog-conventionalcommits": "^7.0.2" @@ -752,12 +752,12 @@ } }, "node_modules/@commitlint/config-validator": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.1.0.tgz", - "integrity": "sha512-kbHkIuItXn93o2NmTdwi5Mk1ujyuSIysRE/XHtrcps/27GuUKEIqBJp6TdJ4Sq+ze59RlzYSHMKuDKZbfg9+uQ==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-18.4.3.tgz", + "integrity": "sha512-FPZZmTJBARPCyef9ohRC9EANiQEKSWIdatx5OlgeHKu878dWwpyeFauVkhzuBRJFcCA4Uvz/FDtlDKs008IHcA==", "dev": true, "dependencies": { - "@commitlint/types": "^18.1.0", + "@commitlint/types": "^18.4.3", "ajv": "^8.11.0" }, "engines": { @@ -787,12 +787,12 @@ "dev": true }, "node_modules/@commitlint/ensure": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.1.0.tgz", - "integrity": "sha512-CkPzJ9UBumIo54VDcpmBlaVX81J++wzEhN3DJH9+6PaLeiIG+gkSx8t7C2gfwG7PaiW4HzQtdQlBN5ab+c4vFQ==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-18.4.3.tgz", + "integrity": "sha512-MI4fwD9TWDVn4plF5+7JUyLLbkOdzIRBmVeNlk4dcGlkrVA+/l5GLcpN66q9LkFsFv6G2X31y89ApA3hqnqIFg==", "dev": true, "dependencies": { - "@commitlint/types": "^18.1.0", + "@commitlint/types": "^18.4.3", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "lodash.snakecase": "^4.1.1", @@ -804,21 +804,21 @@ } }, "node_modules/@commitlint/execute-rule": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.1.0.tgz", - "integrity": "sha512-w3Vt4K+O7+nSr9/gFSEfZ1exKUOPSlJaRpnk7Y+XowEhvwT7AIk1HNANH+gETf0zGZ020+hfiMW/Ome+SNCUsg==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-18.4.3.tgz", + "integrity": "sha512-t7FM4c+BdX9WWZCPrrbV5+0SWLgT3kCq7e7/GhHCreYifg3V8qyvO127HF796vyFql75n4TFF+5v1asOOWkV1Q==", "dev": true, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/format": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.1.0.tgz", - "integrity": "sha512-So/w217tGWMZZb1yXcUFNF2qFLyYtSVqbnGoMbX8a+JKcG4oB11Gc1adS0ssUOMivtiNpaLtkSHFynyiwtJtiQ==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-18.4.3.tgz", + "integrity": "sha512-8b+ItXYHxAhRAXFfYki5PpbuMMOmXYuzLxib65z2XTqki59YDQJGpJ/wB1kEE5MQDgSTQWtKUrA8n9zS/1uIDQ==", "dev": true, "dependencies": { - "@commitlint/types": "^18.1.0", + "@commitlint/types": "^18.4.3", "chalk": "^4.1.0" }, "engines": { @@ -826,12 +826,12 @@ } }, "node_modules/@commitlint/is-ignored": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.1.0.tgz", - "integrity": "sha512-fa1fY93J/Nx2GH6r6WOLdBOiL7x9Uc1N7wcpmaJ1C5Qs6P+rPSUTkofe2IOhSJIJoboHfAH6W0ru4xtK689t0Q==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-18.4.3.tgz", + "integrity": "sha512-ZseOY9UfuAI32h9w342Km4AIaTieeFskm2ZKdrG7r31+c6zGBzuny9KQhwI9puc0J3GkUquEgKJblCl7pMnjwg==", "dev": true, "dependencies": { - "@commitlint/types": "^18.1.0", + "@commitlint/types": "^18.4.3", "semver": "7.5.4" }, "engines": { @@ -839,33 +839,33 @@ } }, "node_modules/@commitlint/lint": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.1.0.tgz", - "integrity": "sha512-LGB3eI5UYu5LLayibNrRM4bSbowr1z9uyqvp0c7+0KaSJi+xHxy/QEhb6fy4bMAtbXEvygY0sUu9HxSWg41rVQ==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-18.4.3.tgz", + "integrity": "sha512-18u3MRgEXNbnYkMOWoncvq6QB8/90m9TbERKgdPqVvS+zQ/MsuRhdvHYCIXGXZxUb0YI4DV2PC4bPneBV/fYuA==", "dev": true, "dependencies": { - "@commitlint/is-ignored": "^18.1.0", - "@commitlint/parse": "^18.1.0", - "@commitlint/rules": "^18.1.0", - "@commitlint/types": "^18.1.0" + "@commitlint/is-ignored": "^18.4.3", + "@commitlint/parse": "^18.4.3", + "@commitlint/rules": "^18.4.3", + "@commitlint/types": "^18.4.3" }, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/load": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.2.0.tgz", - "integrity": "sha512-xjX3d3CRlOALwImhOsmLYZh14/+gW/KxsY7+bPKrzmGuFailf9K7ckhB071oYZVJdACnpY4hDYiosFyOC+MpAA==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-18.4.3.tgz", + "integrity": "sha512-v6j2WhvRQJrcJaj5D+EyES2WKTxPpxENmNpNG3Ww8MZGik3jWRXtph0QTzia5ZJyPh2ib5aC/6BIDymkUUM58Q==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^18.1.0", - "@commitlint/execute-rule": "^18.1.0", - "@commitlint/resolve-extends": "^18.1.0", - "@commitlint/types": "^18.1.0", + "@commitlint/config-validator": "^18.4.3", + "@commitlint/execute-rule": "^18.4.3", + "@commitlint/resolve-extends": "^18.4.3", + "@commitlint/types": "^18.4.3", "@types/node": "^18.11.9", "chalk": "^4.1.0", - "cosmiconfig": "^8.0.0", + "cosmiconfig": "^8.3.6", "cosmiconfig-typescript-loader": "^5.0.0", "lodash.isplainobject": "^4.0.6", "lodash.merge": "^4.6.2", @@ -877,22 +877,22 @@ } }, "node_modules/@commitlint/message": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.1.0.tgz", - "integrity": "sha512-8dT/jJg73wf3o2Mut/fqEDTpBYSIEVtX5PWyuY/0uviEYeheZAczFo/VMIkeGzhJJn1IrcvAwWsvJ1lVGY2I/w==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-18.4.3.tgz", + "integrity": "sha512-ddJ7AztWUIoEMAXoewx45lKEYEOeOlBVWjk8hDMUGpprkuvWULpaXczqdjwVtjrKT3JhhN+gMs8pm5G3vB2how==", "dev": true, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/parse": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.1.0.tgz", - "integrity": "sha512-23yv8uBweXWYn8bXk4PjHIsmVA+RkbqPh2h7irupBo2LthVlzMRc4LM6UStasScJ4OlXYYaWOmuP7jcExUF50Q==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-18.4.3.tgz", + "integrity": "sha512-eoH7CXM9L+/Me96KVcfJ27EIIbA5P9sqw3DqjJhRYuhaULIsPHFs5S5GBDCqT0vKZQDx0DgxhMpW6AQbnKrFtA==", "dev": true, "dependencies": { - "@commitlint/types": "^18.1.0", - "conventional-changelog-angular": "^6.0.0", + "@commitlint/types": "^18.4.3", + "conventional-changelog-angular": "^7.0.0", "conventional-commits-parser": "^5.0.0" }, "engines": { @@ -900,13 +900,13 @@ } }, "node_modules/@commitlint/read": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.1.0.tgz", - "integrity": "sha512-rzfzoKUwxmvYO81tI5o1371Nwt3vhcQR36oTNfupPdU1jgSL3nzBIS3B93LcZh3IYKbCIMyMPN5WZ10BXdeoUg==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-18.4.3.tgz", + "integrity": "sha512-H4HGxaYA6OBCimZAtghL+B+SWu8ep4X7BwgmedmqWZRHxRLcX2q0bWBtUm5FsMbluxbOfrJwOs/Z0ah4roP/GQ==", "dev": true, "dependencies": { - "@commitlint/top-level": "^18.1.0", - "@commitlint/types": "^18.1.0", + "@commitlint/top-level": "^18.4.3", + "@commitlint/types": "^18.4.3", "fs-extra": "^11.0.0", "git-raw-commits": "^2.0.11", "minimist": "^1.2.6" @@ -916,13 +916,13 @@ } }, "node_modules/@commitlint/resolve-extends": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.1.0.tgz", - "integrity": "sha512-3mZpzOEJkELt7BbaZp6+bofJyxViyObebagFn0A7IHaLARhPkWTivXdjvZHS12nAORftv88Yhbh8eCPKfSvB7g==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-18.4.3.tgz", + "integrity": "sha512-30sk04LZWf8+SDgJrbJCjM90gTg2LxsD9cykCFeFu+JFHvBFq5ugzp2eO/DJGylAdVaqxej3c7eTSE64hR/lnw==", "dev": true, "dependencies": { - "@commitlint/config-validator": "^18.1.0", - "@commitlint/types": "^18.1.0", + "@commitlint/config-validator": "^18.4.3", + "@commitlint/types": "^18.4.3", "import-fresh": "^3.0.0", "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0", @@ -933,15 +933,15 @@ } }, "node_modules/@commitlint/rules": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.1.0.tgz", - "integrity": "sha512-VJNQ674CRv4znI0DbsjZLVnn647J+BTxHGcrDIsYv7c99gW7TUGeIe5kL80G7l8+5+N0se8v9yn+Prr8xEy6Yw==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-18.4.3.tgz", + "integrity": "sha512-8KIeukDf45BiY+Lul1T0imSNXF0sMrlLG6JpLLKolkmYVQ6PxxoNOriwyZ3UTFFpaVbPy0rcITaV7U9JCAfDTA==", "dev": true, "dependencies": { - "@commitlint/ensure": "^18.1.0", - "@commitlint/message": "^18.1.0", - "@commitlint/to-lines": "^18.1.0", - "@commitlint/types": "^18.1.0", + "@commitlint/ensure": "^18.4.3", + "@commitlint/message": "^18.4.3", + "@commitlint/to-lines": "^18.4.3", + "@commitlint/types": "^18.4.3", "execa": "^5.0.0" }, "engines": { @@ -949,18 +949,18 @@ } }, "node_modules/@commitlint/to-lines": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.1.0.tgz", - "integrity": "sha512-aHIoSDjG0ckxPLYDpODUeSLbEKmF6Jrs1B5JIssbbE9eemBtXtjm9yzdiAx9ZXcwoHlhbTp2fbndDb3YjlvJag==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-18.4.3.tgz", + "integrity": "sha512-fy1TAleik4Zfru1RJ8ZU6cOSvgSVhUellxd3WZV1D5RwHZETt1sZdcA4mQN2y3VcIZsUNKkW0Mq8CM9/L9harQ==", "dev": true, "engines": { "node": ">=v18" } }, "node_modules/@commitlint/top-level": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.1.0.tgz", - "integrity": "sha512-1/USHlolIxJlsfLKecSXH+6PDojIvnzaJGPYwF7MtnTuuXCNQ4izkeqDsRuNMe9nU2VIKpK9OT8Q412kGNmgGw==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-18.4.3.tgz", + "integrity": "sha512-E6fJPBLPFL5R8+XUNSYkj4HekIOuGMyJo3mIx2PkYc3clel+pcWQ7TConqXxNWW4x1ugigiIY2RGot55qUq1hw==", "dev": true, "dependencies": { "find-up": "^5.0.0" @@ -1031,9 +1031,9 @@ } }, "node_modules/@commitlint/types": { - "version": "18.1.0", - "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.1.0.tgz", - "integrity": "sha512-65vGxZmbs+2OVwEItxhp3Ul7X2m2LyLfifYI/NdPwRqblmuES2w2aIRhIjb7cwUIBHHSTT8WXj4ixVHQibmvLQ==", + "version": "18.4.3", + "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-18.4.3.tgz", + "integrity": "sha512-cvzx+vtY/I2hVBZHCLrpoh+sA0hfuzHwDc+BAFPimYLjJkpHnghQM+z8W/KyLGkygJh3BtI3xXXq+dKjnSWEmA==", "dev": true, "dependencies": { "chalk": "^4.1.0" @@ -1076,9 +1076,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -1099,9 +1099,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", - "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.55.0.tgz", + "integrity": "sha512-qQfo2mxH5yVom1kacMtZZJFVdW+E70mqHMJvVg6WTLo+VBuQJ4TojZlfWBjK0ve5BdEeNAVxOsl/nvNMpJOaJA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1703,9 +1703,9 @@ } }, "node_modules/@types/jest": { - "version": "29.5.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz", - "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==", + "version": "29.5.10", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.10.tgz", + "integrity": "sha512-tE4yxKEphEyxj9s4inideLHktW/x6DwesIwWZ9NN1FKf9zbJYsnhBoA9vrHA/IuIOKwPa5PcFBNV4lpMIOEzyQ==", "dev": true, "dependencies": { "expect": "^29.0.0", @@ -1740,9 +1740,9 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", - "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz", + "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "dev": true }, "node_modules/@types/stack-utils": { @@ -1767,16 +1767,16 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", - "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.1.tgz", + "integrity": "sha512-5bQDGkXaxD46bPvQt08BUz9YSaO4S0fB1LB5JHQuXTfkGPI3+UUeS387C/e9jRie5GqT8u5kFTrMvAjtX4O5kA==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/type-utils": "6.10.0", - "@typescript-eslint/utils": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", + "@typescript-eslint/scope-manager": "6.13.1", + "@typescript-eslint/type-utils": "6.13.1", + "@typescript-eslint/utils": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1802,15 +1802,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", - "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.1.tgz", + "integrity": "sha512-fs2XOhWCzRhqMmQf0eicLa/CWSaYss2feXsy7xBD/pLyWke/jCIVc2s1ikEAtSW7ina1HNhv7kONoEfVNEcdDQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", + "@typescript-eslint/scope-manager": "6.13.1", + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1", "debug": "^4.3.4" }, "engines": { @@ -1830,13 +1830,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", - "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", + "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0" + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1847,13 +1847,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", - "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz", + "integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/utils": "6.10.0", + "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/utils": "6.13.1", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1874,9 +1874,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", - "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", + "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1887,13 +1887,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", - "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", + "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1914,17 +1914,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", - "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", + "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/scope-manager": "6.13.1", + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/typescript-estree": "6.13.1", "semver": "^7.5.4" }, "engines": { @@ -1939,12 +1939,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", - "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", + "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/types": "6.13.1", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2752,15 +2752,15 @@ "dev": true }, "node_modules/conventional-changelog-angular": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz", - "integrity": "sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz", + "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==", "dev": true, "dependencies": { "compare-func": "^2.0.0" }, "engines": { - "node": ">=14" + "node": ">=16" } }, "node_modules/conventional-changelog-conventionalcommits": { @@ -3259,15 +3259,15 @@ } }, "node_modules/eslint": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", - "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", + "integrity": "sha512-iyUUAM0PCKj5QpwGfmCAG9XXbZCWsqP/eWAWrG/W0umvjuLRBECwSFdt+rCntju0xEH7teIABPwXpahftIaTdA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.53.0", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.55.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -3314,9 +3314,9 @@ } }, "node_modules/eslint-config-prettier": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz", - "integrity": "sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "bin": { "eslint-config-prettier": "bin/cli.js" @@ -3757,9 +3757,9 @@ "dev": true }, "node_modules/fs-extra": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", - "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", "dev": true, "dependencies": { "graceful-fs": "^4.2.0", @@ -5749,9 +5749,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -6723,9 +6723,9 @@ } }, "node_modules/ts-loader": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.0.tgz", - "integrity": "sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==", + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", "dev": true, "dependencies": { "chalk": "^4.1.0", @@ -6791,9 +6791,9 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.2.tgz", + "integrity": "sha512-6l+RyNy7oAHDfxC4FzSJcz9vnjTKxrLpDG5M2Vu4SHRVNg6xzqZp6LYSR9zjqQTu8DU/f5xwxUdADOkbrIX2gQ==", "dev": true, "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index 89aae3e..89ab162 100644 --- a/package.json +++ b/package.json @@ -53,21 +53,21 @@ "dist" ], "devDependencies": { - "@commitlint/cli": "^18.2.0", - "@commitlint/config-conventional": "^18.1.0", - "@types/jest": "^29.5.8", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", - "eslint": "^8.53.0", - "eslint-config-prettier": "^9.0.0", + "@commitlint/cli": "^18.4.3", + "@commitlint/config-conventional": "^18.4.3", + "@types/jest": "^29.5.10", + "@typescript-eslint/eslint-plugin": "^6.13.1", + "@typescript-eslint/parser": "^6.13.1", + "eslint": "^8.55.0", + "eslint-config-prettier": "^9.1.0", "eslint-plugin-prettier": "^5.0.1", "husky": "^8.0.3", "jest": "^29.7.0", "pinst": "^3.0.0", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "ts-jest": "^29.1.1", - "ts-loader": "^9.5.0", - "typescript": "^5.2.2", + "ts-loader": "^9.5.1", + "typescript": "^5.3.2", "webpack": "^5.89.0", "webpack-cli": "^5.1.4" } diff --git a/src/index.ts b/src/index.ts index 15ccd7b..f01a208 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import { createSudokuInstance } from "./sudoku"; -import { AnalyzeData, Board, Difficulty } from "./types"; +import { AnalyzeData, Board, Difficulty, SolvingStep } from "./types"; -export { type AnalyzeData, type Board, type Difficulty }; +export { type AnalyzeData, type Board, type Difficulty, type SolvingStep }; export function generate(difficulty: Difficulty): Board { const { getBoard } = createSudokuInstance({ difficulty }); @@ -13,12 +13,15 @@ export function analyze(Board: Board): AnalyzeData { return analyzeBoard(); } -export function solve(Board: Board): Board { - const { solveAll } = createSudokuInstance({ initBoard: Board }); - return solveAll(); -} - -export function solveStep(Board: Board): Board { - const { solveStep } = createSudokuInstance({ initBoard: Board }); - return solveStep(); +export function solve(Board: Board): { + board: Board; + steps: SolvingStep[]; +} { + const solvingSteps: SolvingStep[] = []; + const { solveAll } = createSudokuInstance({ + initBoard: Board, + onUpdate: (solvingStep) => solvingSteps.push(solvingStep), + }); + const board = solveAll(); + return { board, steps: solvingSteps }; } diff --git a/src/sudoku.ts b/src/sudoku.ts index b89c3f8..ccf57d9 100644 --- a/src/sudoku.ts +++ b/src/sudoku.ts @@ -1,8 +1,6 @@ // Importing necessary constants import { DIFFICULTY_MEDIUM, - SOLVE_MODE_STEP, - SOLVE_MODE_ALL, BOARD_SIZE, CANDIDATES, NULL_CANDIDATE_LIST, @@ -18,8 +16,8 @@ import { Options, House, AnalyzeData, - SolveType, Difficulty, + Update, } from "./types"; // Importing utility functions @@ -33,7 +31,6 @@ import { isBoardFinished, isEasyEnough, isHardEnough, - uniqueArray, } from "./utils"; // Generating list of all houses (rows, columns, and boxes) in the sudoku board @@ -57,61 +54,71 @@ export function createSudokuInstance(options: Options = {}) { // Define different strategies to solve the Sudoku along with their scores const strategies: Array = [ { - title: "Single Remaining Cell Strategy", - fn: singleRemainingCellStrategy, + title: "Open Singles Strategy", + fn: openSinglesStrategy, score: 0.1, postFn: updateCandidatesBasedOnCellsValue, + type: "value", }, { - title: "Single Candidate Cell Strategy", - fn: singleCandidateCellStrategy, + title: "Visual Elimination Strategy", + fn: visualEliminationStrategy, score: 9, postFn: updateCandidatesBasedOnCellsValue, + type: "value", }, { - title: "Single Candidate Value Strategy", - fn: singleCandidateValueStrategy, + title: "Single Candidate Strategy", + fn: singleCandidateStrategy, score: 8, postFn: updateCandidatesBasedOnCellsValue, + type: "value", }, { title: "Naked Pair Strategy", fn: nakedPairStrategy, score: 50, + type: "elimination", }, { title: "Pointing Elimination Strategy", fn: pointingEliminationStrategy, score: 80, + type: "elimination", }, //harder for human to spot { title: "Hidden Pair Strategy", fn: hiddenPairStrategy, score: 90, + type: "elimination", }, { title: "Naked Triplet Strategy", fn: nakedTripletStrategy, score: 100, + type: "elimination", }, //never gets used unless above strategies are turned off? { title: "Hidden Triplet Strategy", fn: hiddenTripletStrategy, score: 140, + type: "elimination", }, //never gets used unless above strategies are turned off? { title: "Naked Quadruple Strategy", fn: nakedQuadrupleStrategy, score: 150, + type: "elimination", }, //never gets used unless above strategies are turned off? { title: "Hidden Quadruple Strategy", fn: hiddenQuadrupleStrategy, score: 280, + type: "elimination", }, ]; @@ -135,7 +142,7 @@ export function createSudokuInstance(options: Options = {}) { const removeCandidatesFromMultipleCells = ( cells: Array, candidates: Array, - ) => { + ): Array<{ index: number; eliminatedCandidate: number }> => { const cellsUpdated = []; for (let i = 0; i < cells.length; i++) { const cellCandidates = board[cells[i]].candidates; @@ -145,7 +152,10 @@ export function createSudokuInstance(options: Options = {}) { //-1 because candidate '1' is at index 0 etc. if (candidate && cellCandidates[candidate - 1] !== null) { cellCandidates[candidate - 1] = null; //NOTE: also deletes them from board variable - cellsUpdated.push(cells[i]); //will push same cell multiple times + cellsUpdated.push({ + index: cells[i], + eliminatedCandidate: candidate, + }); //will push same cell multiple times } } } @@ -232,13 +242,13 @@ export function createSudokuInstance(options: Options = {}) { // Various strategies to solve the Sudoku are defined here - /* singleRemainingCellStrategy + /* openSinglesStrategy * -------------- * This is the simplest strategy. If there's only one empty cell in a row, column, or box (these are all "houses"), the number that goes into that cell has to be the one number that isn't elsewhere in that house. * For instance, if a row has the numbers 1 through 8, then the last cell in that row must be 9. * -----------------------------------------------------------------*/ - function singleRemainingCellStrategy(): ReturnType { + function openSinglesStrategy(): ReturnType { const groupOfHouses = GROUP_OF_HOUSES; for (let i = 0; i < groupOfHouses.length; i++) { @@ -287,7 +297,7 @@ export function createSudokuInstance(options: Options = {}) { } board = addValueToCellIndex(board, emptyCell.cellIndex, value[0]); //does not update UI - return [emptyCell.cellIndex]; + return [{ index: emptyCell.cellIndex, filledValue: value[0] }]; } /* updateCandidatesBasedOnCellsValue @@ -328,13 +338,13 @@ export function createSudokuInstance(options: Options = {}) { }); }; - /* singleCandidateValueStrategy + /* singleCandidateStrategy * -------------- * This strategy looks at houses where a number only appears as a candidate in one cell. * If every other cell in a house already contains a number or can't possibly contain a certain number, then that number must go in the one cell where it's still a candidate. * For example, if in a row the number 3 can only be placed in the third cell, then it must go there. * -----------------------------------------------------------------*/ - function singleCandidateValueStrategy(): ReturnType | false { + function singleCandidateStrategy(): ReturnType | false { const groupOfHousesLength = GROUP_OF_HOUSES.length; for (let houseType = 0; houseType < groupOfHousesLength; houseType++) { @@ -363,7 +373,7 @@ export function createSudokuInstance(options: Options = {}) { const cellIndex = possibleCells[0]; board = addValueToCellIndex(board, cellIndex, digit); - return [cellIndex]; // one step at a time + return [{ index: cellIndex, filledValue: digit }]; // one step at a time } } } @@ -372,12 +382,12 @@ export function createSudokuInstance(options: Options = {}) { return false; } - /* singleCandidateCellStrategy + /* visualEliminationStrategy * -------------- * Looks for cells with only one candidate * -- returns effectedCells - the updated cell(s), or false * -----------------------------------------------------------------*/ - function singleCandidateCellStrategy(): ReturnType | false { + function visualEliminationStrategy(): ReturnType | false { for (let cellIndex = 0; cellIndex < board.length; cellIndex++) { const cell = board[cellIndex]; const candidates = cell.candidates; @@ -402,7 +412,7 @@ export function createSudokuInstance(options: Options = {}) { board = addValueToCellIndex(board, cellIndex, digit); - return [cellIndex]; // one step at a time + return [{ index: cellIndex, filledValue: digit! }]; // one step at a time } } @@ -481,7 +491,7 @@ export function createSudokuInstance(options: Options = {}) { ); if (cellsUpdated.length > 0) { - return cellsUpdated; + return cellsUpdated as Update[]; } } } @@ -595,7 +605,7 @@ export function createSudokuInstance(options: Options = {}) { ); if (cellsUpdated.length > 0) { - return uniqueArray(cellsWithCandidates.concat(cellsUpdated)); + return cellsUpdated as Update[]; } } } @@ -646,7 +656,7 @@ export function createSudokuInstance(options: Options = {}) { function checkLockedCandidates( house: House, startIndex: number, - ): number[] | boolean { + ): Update[] | boolean { //log("startIndex: "+startIndex); for ( let i = Math.max(startIndex, minIndexes[startIndex]); @@ -729,7 +739,7 @@ export function createSudokuInstance(options: Options = {}) { //log(combinedCandidates); //filter out duplicates - return uniqueArray(cellsWithCandidates); + return cellsUpdated as Update[]; } } } @@ -789,12 +799,10 @@ export function createSudokuInstance(options: Options = {}) { const applySolvingStrategies = ({ strategyIndex = 0, gradingMode = false, - solveMode = SOLVE_MODE_ALL, }: { strategyIndex?: number; gradingMode?: boolean; - solveMode?: SolveType; - } = {}): boolean | undefined => { + } = {}): boolean | "elimination" => { if (isBoardFinished(board)) { if (!gradingMode) { onFinish?.(calculateBoardDifficulty(usedStrategies, strategies)); @@ -802,7 +810,8 @@ export function createSudokuInstance(options: Options = {}) { return false; } - const effectedCells = strategies[strategyIndex].fn(); + const effectedCells: boolean | -1 | Update[] = + strategies[strategyIndex].fn(); strategies[strategyIndex].postFn?.(); if (effectedCells === false) { @@ -810,7 +819,6 @@ export function createSudokuInstance(options: Options = {}) { return applySolvingStrategies({ strategyIndex: strategyIndex + 1, gradingMode, - solveMode, }); } else { onError?.({ message: "No More Strategies To Solve The Board" }); @@ -821,17 +829,12 @@ export function createSudokuInstance(options: Options = {}) { return false; } - if (solveMode === SOLVE_MODE_STEP) { - if (typeof onUpdate === "function") { - onUpdate({ - strategy: strategies[strategyIndex].title, - updatedIndexes: effectedCells as Array, - }); - } - - if (isBoardFinished(board)) { - onFinish?.(calculateBoardDifficulty(usedStrategies, strategies)); - } + if (!gradingMode) { + onUpdate?.({ + strategy: strategies[strategyIndex].title, + updates: effectedCells as Update[], + type: strategies[strategyIndex].type, + }); } if (typeof usedStrategies[strategyIndex] === "undefined") { @@ -839,7 +842,6 @@ export function createSudokuInstance(options: Options = {}) { } usedStrategies[strategyIndex] += 1; - return true; }; @@ -867,7 +869,6 @@ export function createSudokuInstance(options: Options = {}) { board = addValueToCellIndex(board, previousIndex, null); resetCandidates(); board[cellIndex].invalidCandidates = []; - generateBoardAnswerRecursively(previousIndex); }; const generateBoardAnswerRecursively = (cellIndex: number) => { @@ -939,9 +940,7 @@ export function createSudokuInstance(options: Options = {}) { board = boardClone; } - while ( - applySolvingStrategies({ gradingMode: true, solveMode: SOLVE_MODE_ALL }) - ) { + while (applySolvingStrategies({ gradingMode: true })) { // Continue applying solving strategies until no more can be applied } @@ -986,27 +985,12 @@ export function createSudokuInstance(options: Options = {}) { } const solveAll = (): Board => { - while (applySolvingStrategies({ solveMode: SOLVE_MODE_ALL })) { + while (applySolvingStrategies()) { // Continue looping until no more solving strategies can be applied } return getBoard(); }; - const solveStep = (): Board => { - const initialBoard = getBoard().slice(); - - applySolvingStrategies({ solveMode: SOLVE_MODE_STEP }); - - if (!isBoardFinished(board) && areBoardsEqual(initialBoard, getBoard())) { - solveStep(); - } - function areBoardsEqual(board1: Board, board2: Board) { - return board1.filter(Boolean).length === board2.filter(Boolean).length; - } - - return getBoard(); - }; - const getBoard = (): Board => board.map((cell) => cell.value); if (!initBoard) { @@ -1021,7 +1005,6 @@ export function createSudokuInstance(options: Options = {}) { return { solveAll, - solveStep, analyzeBoard, getBoard, generateBoard, diff --git a/src/types.ts b/src/types.ts index c5ff612..f668358 100644 --- a/src/types.ts +++ b/src/types.ts @@ -21,24 +21,33 @@ export type Cell = { invalidCandidates?: Array; }; export type InternalBoard = Array; -export type StrategyFn = () => boolean | Array | -1; + +export type StrategyFn = () => + | boolean + | Array<{ index: number; eliminatedCandidate?: number; filledValue: number }> + | -1; export interface Strategy { title: string; score: number; fn: StrategyFn; postFn?: () => void; + type: "value" | "elimination"; } +export interface Update { + index: number; + eliminatedCandidate?: number; + filledValue: number; +} +export interface SolvingStep { + strategy: string; + updates: Array; + type: "value" | "elimination"; +} export interface Options { onError?: (args: { message: string }) => void; onFinish?: (args: { difficulty: Difficulty; score: number }) => void; - onUpdate?: ({ - strategy, - updatedIndexes, - }: { - strategy: string; - updatedIndexes: Array; - }) => void; + onUpdate?: ({ strategy, updates, type }: SolvingStep) => void; initBoard?: Board; difficulty?: Difficulty; } diff --git a/src/utils.ts b/src/utils.ts index 2820b2d..afaa95a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -183,8 +183,8 @@ export const calculateBoardDifficulty = ( validUsedStrategies.length < 3 ? DIFFICULTY_EASY : validUsedStrategies.length < 4 - ? DIFFICULTY_MEDIUM - : DIFFICULTY_HARD; + ? DIFFICULTY_MEDIUM + : DIFFICULTY_HARD; if (totalScore > 750) difficulty = DIFFICULTY_EXPERT; if (totalScore > 2000) difficulty = DIFFICULTY_MASTER; diff --git a/tests/sudoku.test.ts b/tests/sudoku.test.ts index 6a85e70..9c4966e 100644 --- a/tests/sudoku.test.ts +++ b/tests/sudoku.test.ts @@ -1,4 +1,4 @@ -import { generate, analyze, solve, solveStep } from "../src/index"; // Import the createSudokuInstance module (update path as needed) +import { generate, analyze, solve } from "../src/index"; // Import the createSudokuInstance module (update path as needed) import { EASY_SUDOKU_BOARD_FOR_TEST, EXPERT_SUDOKU_BOARD_FOR_TEST, @@ -68,7 +68,7 @@ describe("sudoku-core", () => { const sudokuBoard = generate("expert"); //Act - const solvedBoard = solve(sudokuBoard); + const { board: solvedBoard } = solve(sudokuBoard); // Assert expect(solvedBoard.every((cell) => cell !== null)).toBe(true); @@ -77,22 +77,21 @@ describe("sudoku-core", () => { describe("solveStep method", () => { it("should solve one more step", () => { //Arrange - const sudokuBoard = generate("expert"); - - for (let index = 0; index < 81; index++) { - //Arrange - const boardBeforeLength = sudokuBoard.filter(Boolean).length; + const sudokuBoard = generate("master"); + const unfilledCellsLength = sudokuBoard.filter( + (cell) => !Boolean(cell), + ).length; - //Act - const solvedBoard = solveStep(sudokuBoard); + //Act + const { steps } = solve(sudokuBoard); - // Assert - const boardAfterLength = solvedBoard.filter(Boolean).length; - expect( - boardAfterLength - boardBeforeLength === 1 || - boardBeforeLength === 81, - ).toBe(true); - } + // Assert + const stepsFillingCount = steps.reduce( + (acc, curr) => + curr.type === "value" ? curr.updates.length + acc : acc, + 0, + ); + expect(stepsFillingCount).toBe(unfilledCellsLength); }); }); describe("analyze method", () => {