From 2763bb5cde9607f9dfac7e7c7ee9e482f8c03c00 Mon Sep 17 00:00:00 2001 From: AmeyGohil Date: Fri, 25 Mar 2022 18:06:45 +0530 Subject: [PATCH] Initial commit --- .eslintrc.js | 84 +++ .vscode/settings.json | 18 + package-lock.json | 486 ++++++++++++++ package.json | 29 + public/favicon.ico | Bin 3870 -> 15406 bytes public/index.html | 4 +- public/logo192.png | Bin 5347 -> 10432 bytes public/logo512.png | Bin 9664 -> 28700 bytes public/manifest.json | 4 +- src/App.css | 38 -- src/App.js | 52 +- src/App.styles.module.scss | 32 + src/App.test.js | 8 +- src/components/AddPuzzleScreen/index.js | 116 ++++ .../AddPuzzleScreen/styles.module.scss | 159 +++++ src/components/CheckSolution/index.js | 44 ++ .../CheckSolution/styles.module.scss | 38 ++ src/components/Controls/index.js | 37 ++ src/components/Controls/styles.module.scss | 48 ++ src/components/Grid/components/Cross.js | 37 ++ src/components/Grid/components/Edge.js | 9 + src/components/Grid/components/Line.js | 36 ++ src/components/Grid/components/LineRow.js | 58 ++ src/components/Grid/components/N.js | 17 + src/components/Grid/components/Node.js | 14 + src/components/Grid/components/NumberRow.js | 81 +++ src/components/Grid/components/Row.js | 6 + src/components/Grid/index.js | 291 +++++++++ src/components/Grid/styles.module.scss | 296 +++++++++ src/components/Icons.js | 89 +++ src/components/Info/index.js | 127 ++++ src/components/Info/styles.module.scss | 171 +++++ src/constants.js | 162 +++++ src/examples.js | 608 ++++++++++++++++++ src/index.css | 6 + src/index.js | 12 +- src/reportWebVitals.js | 4 +- src/setupTests.js | 2 +- src/utils.js | 105 +++ 39 files changed, 3253 insertions(+), 75 deletions(-) create mode 100644 .eslintrc.js create mode 100644 .vscode/settings.json delete mode 100644 src/App.css create mode 100644 src/App.styles.module.scss create mode 100644 src/components/AddPuzzleScreen/index.js create mode 100644 src/components/AddPuzzleScreen/styles.module.scss create mode 100644 src/components/CheckSolution/index.js create mode 100644 src/components/CheckSolution/styles.module.scss create mode 100644 src/components/Controls/index.js create mode 100644 src/components/Controls/styles.module.scss create mode 100644 src/components/Grid/components/Cross.js create mode 100644 src/components/Grid/components/Edge.js create mode 100644 src/components/Grid/components/Line.js create mode 100644 src/components/Grid/components/LineRow.js create mode 100644 src/components/Grid/components/N.js create mode 100644 src/components/Grid/components/Node.js create mode 100644 src/components/Grid/components/NumberRow.js create mode 100644 src/components/Grid/components/Row.js create mode 100644 src/components/Grid/index.js create mode 100644 src/components/Grid/styles.module.scss create mode 100644 src/components/Icons.js create mode 100644 src/components/Info/index.js create mode 100644 src/components/Info/styles.module.scss create mode 100644 src/constants.js create mode 100644 src/examples.js create mode 100644 src/utils.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..43056e9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,84 @@ +module.exports = { + env: { + browser: true, + es2021: true, + es6: true, + }, + extends: [ + "plugin:react/recommended", + "prettier", + "plugin:prettier/recommended", + "plugin:react-hooks/recommended", + "plugin:import/recommended", + ], + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + ecmaVersion: "latest", + sourceType: "module", + }, + plugins: ["react", "prettier", "react-hooks", "import"], + rules: { + "react/prop-types": "off", + "react/no-unescaped-entities": "warn", + "react-hooks/rules-of-hooks": "error", + "react-hooks/exhaustive-deps": "warn", + "no-unused-vars": "warn", + "func-names": "warn", + "object-shorthand": "off", + "class-methods-use-this": "off", + "consistent-return": "off", + "import/no-dynamic-require": "off", + "global-require": "off", + "jsx-a11y/click-events-have-key-events": "off", + "jsx-a11y/mouse-events-have-key-events": "off", + "max-classes-per-file": "off", + "no-nested-ternary": "off", + "no-new": "off", + "no-plusplus": "off", + "no-process-exit": "off", + "no-restricted-globals": "off", + "node/exports-style": "off", + "node/no-unpublished-import": "off", + "node/no-unsupported-features/es-syntax": "off", + "no-console": ["warn", {allow: ["warn", "debug", "error", "info"]}], + "no-unused-expressions": [ + "error", + { + allowShortCircuit: true, + allowTernary: true, + }, + ], + "react/no-array-index-key": "warn", + "no-shadow": "off", + "linebreak-style": ["error", "unix"], + "prettier/prettier": [ + "error", + { + singleQuote: false, + parser: "flow", + bracketSpacing: false, + jsxBracketSameLine: false, + printWidth: 80, + semi: true, + tabWidth: 2, + trailingComma: "es5", + useTabs: false, + proseWrap: "always", + jsxSingleQuote: true, + importOrder: ["^[./]"], + importOrderSeparation: true, + importOrderSortSpecifiers: true, + }, + ], + }, + settings: { + "import/resolver": { + node: { + extensions: [".js", ".json"], + }, + }, + "import/ignore": ["node_modules", ".json$", ".(scss|css)$"], + }, +}; diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..97ce882 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,18 @@ +{ + "files.autoSave": "onFocusChange", + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + "editor.formatOnPaste": true, + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], + "eslint.format.enable": true, + "[javascript]": { + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + }, + "[javascriptreact]": { + "editor.defaultFormatter": "dbaeumer.vscode-eslint", + }, +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 00274de..0de69a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3070,6 +3070,11 @@ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, "array.prototype.flat": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz", @@ -3100,6 +3105,11 @@ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=" }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + }, "async": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", @@ -3664,6 +3674,48 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-truncate": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz", + "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==", + "requires": { + "slice-ansi": "^5.0.0", + "string-width": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==" + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz", + "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==", + "requires": { + "ansi-regex": "^6.0.1" + } + } + } + }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -4484,6 +4536,11 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -4502,6 +4559,11 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.92.tgz", "integrity": "sha512-YAVbvQIcDE/IJ/vzDMjD484/hsRbFPW2qXJPaYTfOhtligmfYEYOep+5QojpaEU9kq6bMvNeC2aG7arYvTHYsA==" }, + "email-addresses": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/email-addresses/-/email-addresses-3.1.0.tgz", + "integrity": "sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg==" + }, "emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -4777,6 +4839,43 @@ } } }, + "eslint-config-airbnb": { + "version": "19.0.4", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb/-/eslint-config-airbnb-19.0.4.tgz", + "integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==", + "dev": true, + "requires": { + "eslint-config-airbnb-base": "^15.0.0", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5" + } + }, + "eslint-config-airbnb-base": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz", + "integrity": "sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.10", + "object.assign": "^4.1.2", + "object.entries": "^1.1.5", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true + }, "eslint-config-react-app": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.0.tgz", @@ -4879,6 +4978,16 @@ } } }, + "eslint-plugin-eslint-plugin": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-plugin/-/eslint-plugin-eslint-plugin-4.1.0.tgz", + "integrity": "sha512-QJVw+WYXJuG2469gx5G929bz7crfxySDlK1i569FkuT6dpeHDeP7MmDrKaswCx17snG25LRFD6wmVX+AO5x7Qg==", + "dev": true, + "requires": { + "eslint-utils": "^3.0.0", + "estraverse": "^5.2.0" + } + }, "eslint-plugin-flowtype": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/eslint-plugin-flowtype/-/eslint-plugin-flowtype-8.0.3.tgz", @@ -4958,6 +5067,15 @@ "minimatch": "^3.0.4" } }, + "eslint-plugin-prettier": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.0.0.tgz", + "integrity": "sha512-98MqmCJ7vJodoQK359bqQWaxOE0CS8paAz/GgjaZLyex4TTk3g9HugoO89EqWCrFiOqn9EVvcoo7gZzONCWVwQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, "eslint-plugin-react": { "version": "7.29.4", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.29.4.tgz", @@ -5217,6 +5335,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, "fast-glob": { "version": "3.2.11", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", @@ -5298,6 +5422,21 @@ "minimatch": "^3.0.4" } }, + "filename-reserved-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz", + "integrity": "sha1-q/c9+rc10EVECr/qLZHzieu/oik=" + }, + "filenamify": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz", + "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==", + "requires": { + "filename-reserved-regex": "^2.0.0", + "strip-outer": "^1.0.1", + "trim-repeated": "^1.0.0" + } + }, "filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -5586,6 +5725,70 @@ "get-intrinsic": "^1.1.1" } }, + "gh-pages": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/gh-pages/-/gh-pages-3.2.3.tgz", + "integrity": "sha512-jA1PbapQ1jqzacECfjUaO9gV8uBgU6XNMV0oXLtfCX3haGLe5Atq8BxlrADhbD6/UdG9j6tZLWAkAybndOXTJg==", + "requires": { + "async": "^2.6.1", + "commander": "^2.18.0", + "email-addresses": "^3.0.1", + "filenamify": "^4.3.0", + "find-cache-dir": "^3.3.1", + "fs-extra": "^8.1.0", + "globby": "^6.1.0" + }, + "dependencies": { + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + } + } + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -5906,6 +6109,11 @@ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" }, + "husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==" + }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -5942,6 +6150,11 @@ "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.12.tgz", "integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==" }, + "immutable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", + "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==" + }, "import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", @@ -7731,6 +7944,101 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "lint-staged": { + "version": "12.3.7", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-12.3.7.tgz", + "integrity": "sha512-/S4D726e2GIsDVWIk1XGvheCaDm1SJRQp8efamZFWJxQMVEbOwSysp7xb49Oo73KYCdy97mIWinhlxcoNqIfIQ==", + "requires": { + "cli-truncate": "^3.1.0", + "colorette": "^2.0.16", + "commander": "^8.3.0", + "debug": "^4.3.3", + "execa": "^5.1.1", + "lilconfig": "2.0.4", + "listr2": "^4.0.1", + "micromatch": "^4.0.4", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.0", + "pidtree": "^0.5.0", + "string-argv": "^0.3.1", + "supports-color": "^9.2.1", + "yaml": "^1.10.2" + }, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, + "lilconfig": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==" + }, + "supports-color": { + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.2.1.tgz", + "integrity": "sha512-Obv7ycoCTG51N7y175StI9BlAXrmgZrFhZOb0/PyjHBher/NmsdBgbbQ1Inhq+gIhz6+7Gb+jWF2Vqi7Mf1xnQ==" + } + } + }, + "listr2": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", + "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.16", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.5.5", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + } + } + }, "loader-runner": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.2.0.tgz", @@ -7784,6 +8092,60 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -8393,6 +8755,29 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "pidtree": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.5.0.tgz", + "integrity": "sha512-9nxspIM7OpZuhBxPg73Zvyq7j1QMPMPsGKTqRc2XOaFQauDvoNz9fM1Wdkjmeo7l9GXOZiRs97sPkuayl39wjA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, "pirates": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", @@ -9173,6 +9558,21 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" }, + "prettier": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.0.tgz", + "integrity": "sha512-m2FgJibYrBGGgQXNzfd0PuDGShJgRavjUoRCw1mZERIWVSXF0iLzLm+aOqTAbLnC3n6JzUhAA8uZnFVghHJ86A==", + "dev": true + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", @@ -9789,6 +10189,15 @@ "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==" }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -9799,6 +10208,11 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" }, + "rfdc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz", + "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==" + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -9867,6 +10281,21 @@ "queue-microtask": "^1.2.2" } }, + "rxjs": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "requires": { + "tslib": "^2.1.0" + }, + "dependencies": { + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + } + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -9882,6 +10311,16 @@ "resolved": "https://registry.npmjs.org/sanitize.css/-/sanitize.css-13.0.0.tgz", "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, + "sass": { + "version": "1.49.9", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz", + "integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==", + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + } + }, "sass-loader": { "version": "12.6.0", "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz", @@ -10103,6 +10542,27 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, + "slice-ansi": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", + "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", + "requires": { + "ansi-styles": "^6.0.0", + "is-fullwidth-code-point": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz", + "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==" + }, + "is-fullwidth-code-point": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", + "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==" + } + } + }, "sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -10238,6 +10698,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==" + }, "string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -10356,6 +10821,14 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "strip-outer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz", + "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", @@ -10609,6 +11082,11 @@ "resolved": "https://registry.npmjs.org/throat/-/throat-6.0.1.tgz", "integrity": "sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==" }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, "thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -10667,6 +11145,14 @@ "punycode": "^2.1.1" } }, + "trim-repeated": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz", + "integrity": "sha1-42RqLqTokTEr9+rObPsFOAvAHCE=", + "requires": { + "escape-string-regexp": "^1.0.2" + } + }, "tryer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/tryer/-/tryer-1.0.1.tgz", diff --git a/package.json b/package.json index 8f99129..3a7a535 100644 --- a/package.json +++ b/package.json @@ -6,12 +6,23 @@ "@testing-library/jest-dom": "^5.16.3", "@testing-library/react": "^12.1.4", "@testing-library/user-event": "^13.5.0", + "gh-pages": "^3.2.3", + "husky": "^7.0.4", + "lint-staged": "^12.3.7", "react": "^17.0.2", "react-dom": "^17.0.2", "react-scripts": "5.0.0", + "sass": "^1.49.9", "web-vitals": "^2.1.4" }, + "lint-staged": { + "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [ + "prettier --write" + ] + }, "scripts": { + "predeploy": "npm run build", + "deploy": "gh-pages -d build", "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", @@ -34,5 +45,23 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "homepage": "https://ameygohil.github.io/slither-link", + "devDependencies": { + "eslint": "^8.11.0", + "eslint-config-airbnb": "^19.0.4", + "eslint-config-prettier": "^8.5.0", + "eslint-plugin-eslint-plugin": "^4.1.0", + "eslint-plugin-import": "^2.25.4", + "eslint-plugin-jsx-a11y": "^6.5.1", + "eslint-plugin-prettier": "^4.0.0", + "eslint-plugin-react": "^7.29.4", + "eslint-plugin-react-hooks": "^4.3.0", + "prettier": "^2.6.0" } } diff --git a/public/favicon.ico b/public/favicon.ico index a11777cc471a4344702741ab1c8a588998b1311a..643ac3063b4be8062bcafce7ccdfb4e318781b20 100644 GIT binary patch literal 15406 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 baj7?x%VD!5Cxk2YbYLNVeiXvvpMCWYo=by@ diff --git a/public/index.html b/public/index.html index aa069f2..05a8ed4 100644 --- a/public/index.html +++ b/public/index.html @@ -7,7 +7,7 @@ - React App + SlitherLink diff --git a/public/logo192.png b/public/logo192.png index fc44b0a3796c0e0a64c3d858ca038bd4570465d9..c175075e0d6d7627964dd5c7e32c76d1f6412a8f 100644 GIT binary patch literal 10432 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 b{|{}U^kRn2wo}j7|0ATu<;8xA7zX}7|B6mN diff --git a/public/manifest.json b/public/manifest.json index 080d6c7..b063c6e 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,6 +1,6 @@ { - "short_name": "React App", - "name": "Create React App Sample", + "short_name": "SlitherLink", + "name": "SlitherLink React App", "icons": [ { "src": "favicon.ico", diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 74b5e05..0000000 --- a/src/App.css +++ /dev/null @@ -1,38 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} diff --git a/src/App.js b/src/App.js index 3784575..a766fa1 100644 --- a/src/App.js +++ b/src/App.js @@ -1,25 +1,35 @@ -import logo from './logo.svg'; -import './App.css'; +import * as React from "react"; -function App() { - return ( - +import styles from "./App.styles.module.scss"; +import CheckSolution from "./components/CheckSolution"; +import Grid from "./components/Grid"; +import Info from "./components/Info"; +import {examples} from "./examples"; + +const App = () => { + const [matrix, setMatirx] = React.useState([]); + const [{dim, numbers, solution}] = React.useState( + examples[Math.floor(Math.random() * examples.length)] ); -} + const [check, setCheck] = React.useState("none"); + + return ( +
+

+ Slither + Link +

+ + + +
+ ); +}; export default App; diff --git a/src/App.styles.module.scss b/src/App.styles.module.scss new file mode 100644 index 0000000..802b7e5 --- /dev/null +++ b/src/App.styles.module.scss @@ -0,0 +1,32 @@ +main { + font-size: 24px; + font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background-color: #23262f; + height: 100%; + width: 100%; + color: #fafafa; + display: flex; + align-items: center; + justify-content: center; + gap: 20px; + flex-direction: column; + box-sizing: border-box; + padding: 0 22px; +} + +.heading { + font-size: 25px; + text-align: center; + + span { + color: #7986cb; + + &:nth-of-type(even) { + color: #8c9eff; + } + + &:nth-of-type(odd) { + color: #64b5f6; + } + } +} diff --git a/src/App.test.js b/src/App.test.js index 1f03afe..ed977f8 100644 --- a/src/App.test.js +++ b/src/App.test.js @@ -1,7 +1,9 @@ -import { render, screen } from '@testing-library/react'; -import App from './App'; +import * as React from "react"; -test('renders learn react link', () => { +import {render, screen} from "@testing-library/react"; +import App from "./App"; + +test("renders learn react link", () => { render(); const linkElement = screen.getByText(/learn react/i); expect(linkElement).toBeInTheDocument(); diff --git a/src/components/AddPuzzleScreen/index.js b/src/components/AddPuzzleScreen/index.js new file mode 100644 index 0000000..bea4d52 --- /dev/null +++ b/src/components/AddPuzzleScreen/index.js @@ -0,0 +1,116 @@ +import * as React from "react"; +import {getNumbers, multiStyles} from "../../utils"; +import Grid from "../Grid"; +import N from "../Grid/components/N"; + +import styles from "./styles.module.scss"; + +const AddPuzzleScreen = ({open, onClose}) => { + const [dim, setDim] = React.useState([3, 3]); + const [gridMat, setGridMat] = React.useState([]); + + const [gridNumbers, setGridNumbers] = React.useState([]); + React.useEffect(() => { + setGridNumbers(getNumbers(gridMat)); + }, [JSON.stringify(gridMat)]); + + const [gridReset, setGridReset] = React.useState(false); + React.useEffect(() => { + if (gridReset) { + setGridReset(false); + } + }, [gridReset]); + + const [submitState, setSubmitState] = React.useState("none"); + const [submitText, setSubmitText] = React.useState("Submit"); + const onSubmit = () => { + setSubmitState("passed"); + setSubmitText("Thanks for submitting! 😀"); + }; + React.useEffect(() => { + let timeout; + if (submitState === "passed") { + timeout = setTimeout(() => { + setSubmitState("none"); + setSubmitText("Submit"); + }, 5000); + } + + return () => !isNaN(timeout) && clearTimeout(timeout); + }, [submitState]); + + const shouldShowGrid = + !isNaN(dim[0]) && dim[0] > 0 && !isNaN(dim[1]) && dim[1] > 0 && !gridReset; + + return ( + <> + {open && ( +
+ + +
+
Set dimensions
+
+
+ { + setGridReset(true); + setDim([n, dim[1]]); + }} + editorMode + className={styles.input} + /> + X + { + setGridReset(true); + setDim([dim[0], n]); + }} + editorMode + className={styles.input} + /> +
+
+
+
+
Set matrix
+
+
+ {shouldShowGrid ? ( + { + setGridNumbers([]); + setGridReset(true); + }} + editorMode + /> + ) : ( + "Please set valid dimensions above" + )} +
+
+ +
+
+ )} + + ); +}; + +export default AddPuzzleScreen; diff --git a/src/components/AddPuzzleScreen/styles.module.scss b/src/components/AddPuzzleScreen/styles.module.scss new file mode 100644 index 0000000..d04b397 --- /dev/null +++ b/src/components/AddPuzzleScreen/styles.module.scss @@ -0,0 +1,159 @@ +* { + box-sizing: border-box; +} + +.screen { + position: fixed; + height: 100vh; + width: 100vw; + background-color: #212121; + z-index: 3; + font-family: monospace; + display: flex; + padding: 50px 40px; + align-items: flex-start; + justify-content: flex-start; + overflow-y: auto; + flex-direction: column; + gap: 50px; + + .cross { + $size: 20px; + height: $size; + width: $size; + position: fixed; + top: 40px; + right: 40px; + background-color: inherit; + padding: 3 * $size / 4; + z-index: 3; + cursor: pointer; + + &:after, &:before { + content: ""; + height: $size / 8; + width: $size; + background-color: #b0bec5; + position: absolute; + top: 50%; + left: 50%; + border-radius: $size / 8; + } + + &:after { + transform: translate(-50%, -50%) rotate(45deg); + } + + &:before { + transform: translate(-50%, -50%) rotate(-45deg); + } + } + + section { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + gap: 20px; + font-size: 17px; + color: #e0e0e0; + width: 100%; + + & > span { + padding-left: 20px; + } + + a { + color: #00b0ff; + text-decoration: none; + } + } + + .title { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + margin-bottom: 10px; + font-size: 26px; + text-transform: uppercase; + font-weight: 600; + color: #f5f5f5; + } + + .content { + line-height: inherit; + font-size: inherit; + padding-left: 20px; + color: inherit; + position: relative; + display: flex; + flex-direction: column; + gap: 20px; + width: 100%; + + .matrixGrid { + display: flex; + align-self: center; + justify-self: center; + gap: 20px; + align-items: center; + justify-content: center; + width: 100%; + } + + .submitButton { + display: flex; + align-items: center; + justify-content: center; + background-color: #263238; + height: auto; + width: auto; + padding: 10px 15px; + font-size: 20px; + border: 2px solid #455a64; + margin-top: 20px; + color: #fafafa; + + align-self: center; + justify-self: center; + border-radius: 10px; + cursor: pointer; + transition: all 0.2s; + + &:hover { + border-color: #78909c; + transform: scale(1.01); + color: white; + } + + &.failed { + color: #ff5722; + border-color: #ff5722; + } + + &.passed { + color: #00e676; + border-color: #00e676; + } + } + } + + .dimInputWrapper { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + + .input { + background-color: #282c34; + border-radius: 6px; + display: inline; + + input { + border-radius: 6px; + font-family: monospace; + } + } + } +} diff --git a/src/components/CheckSolution/index.js b/src/components/CheckSolution/index.js new file mode 100644 index 0000000..70baedf --- /dev/null +++ b/src/components/CheckSolution/index.js @@ -0,0 +1,44 @@ +import * as React from "react"; +import {getActiveEdges, multiStyles} from "../../utils"; + +import styles from "./styles.module.scss"; + +const CheckSolution = ({matrix, solution, setCheck}) => { + const [text, setText] = React.useState("Check Solution"); + const [state, setState] = React.useState("none"); + + React.useEffect(() => { + let timeout; + if (state !== "none") { + timeout = setTimeout(() => { + setState("none"); + setCheck("none"); + setText("Check Solution"); + }, 4000); + } + + return () => !isNaN(timeout) && clearTimeout(timeout); + }, [state]); + return ( +
{ + let check = + JSON.stringify(getActiveEdges(matrix)) === JSON.stringify(solution); + if (check) { + setText("That is correct! 😀"); + setState("passed"); + setCheck("passed"); + } else { + setText("Wrong Solution 😔"); + setState("failed"); + setCheck("failed"); + } + }} + > + {text} +
+ ); +}; + +export default CheckSolution; diff --git a/src/components/CheckSolution/styles.module.scss b/src/components/CheckSolution/styles.module.scss new file mode 100644 index 0000000..c2a9cb5 --- /dev/null +++ b/src/components/CheckSolution/styles.module.scss @@ -0,0 +1,38 @@ +* { + box-sizing: border-box; +} + +.button { + display: flex; + align-items: center; + justify-content: center; + background-color: #263238; + height: 50px; + width: auto; + padding: 10px 15px; + font-size: 20px; + border: 2px solid #455a64; + margin-top: 20px; + color: #fafafa; + + border-radius: 10px; + cursor: pointer; + transition: all 0.2s; + + + &:hover { + border-color: #78909c; + transform: scale(1.01); + color: white; + } + + &.failed { + color: #ff5722; + border-color: #ff5722; + } + + &.passed { + color: #00e676; + border-color: #00e676; + } +} diff --git a/src/components/Controls/index.js b/src/components/Controls/index.js new file mode 100644 index 0000000..b679069 --- /dev/null +++ b/src/components/Controls/index.js @@ -0,0 +1,37 @@ +import * as React from "react"; +import AddPuzzleScreen from "../AddPuzzleScreen"; +import {Plus, Refresh, Reset} from "../Icons"; + +import styles from "./styles.module.scss"; + +const Controls = ({onRefresh, onReset, editorMode}) => { + const [showAddScreen, setShowAddScreen] = React.useState(false); + return ( + <> +
+ {!editorMode && ( +
+ + Refresh +
+ )} +
+ + Reset +
+ {!editorMode && ( +
setShowAddScreen(true)}> + + Add +
+ )} +
+ setShowAddScreen(false)} + /> + + ); +}; + +export default Controls; diff --git a/src/components/Controls/styles.module.scss b/src/components/Controls/styles.module.scss new file mode 100644 index 0000000..4106fa1 --- /dev/null +++ b/src/components/Controls/styles.module.scss @@ -0,0 +1,48 @@ +* { + box-sizing: border-box; +} + +.controls { + position: fixed; + bottom: 10px; + left: 10px; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + + & > div { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + font-size: 12px; + color: #90a4ae; + gap: 2px; + + & > svg { + background-color: #263238; + height: 40px; + width: 40px; + padding: 9px; + border: 2px solid #455a64; + font-family: Georgia, 'Times New Roman', Times, serif; + color: #fafafa; + + border-radius: 50%; + cursor: pointer; + transition: all 0.2s; + font-style: italic; + + &:hover { + border-color: #78909c; + font-size: 22px; + color: white; + } + } + + &:hover { + transform: scale(1.1); + } + } +} diff --git a/src/components/Grid/components/Cross.js b/src/components/Grid/components/Cross.js new file mode 100644 index 0000000..209c0eb --- /dev/null +++ b/src/components/Grid/components/Cross.js @@ -0,0 +1,37 @@ +import * as React from "react"; +import {multiStyles} from "../../../utils"; +import styles from "../styles.module.scss"; + +const Cross = ({ + orientation = "h", + className, + onRightClick, + onMiddleClick, + ...props +}) => { + return ( +
{ + e.preventDefault(); + onRightClick(); + }} + onMouseDown={(e) => { + if (e.button === 1) { + onMiddleClick(); + } + }} + > + + +
+ ); +}; + +export default Cross; diff --git a/src/components/Grid/components/Edge.js b/src/components/Grid/components/Edge.js new file mode 100644 index 0000000..0694b5a --- /dev/null +++ b/src/components/Grid/components/Edge.js @@ -0,0 +1,9 @@ +import * as React from "react"; +import Cross from "./Cross"; +import Line from "./Line"; + +const Edge = ({notAllowed, ...props}) => { + return notAllowed ? : ; +}; + +export default Edge; diff --git a/src/components/Grid/components/Line.js b/src/components/Grid/components/Line.js new file mode 100644 index 0000000..57dfebc --- /dev/null +++ b/src/components/Grid/components/Line.js @@ -0,0 +1,36 @@ +import * as React from "react"; +import {multiStyles} from "../../../utils"; +import styles from "../styles.module.scss"; + +const Line = ({ + orientation = "h", + hovered, + active, + className, + onRightClick, + onMiddleClick, + ...props +}) => { + return ( +
{ + e.preventDefault(); + onRightClick(); + }} + onMouseDown={(e) => { + if (e.button === 1) { + onMiddleClick(); + } + }} + /> + ); +}; + +export default Line; diff --git a/src/components/Grid/components/LineRow.js b/src/components/Grid/components/LineRow.js new file mode 100644 index 0000000..05e99a5 --- /dev/null +++ b/src/components/Grid/components/LineRow.js @@ -0,0 +1,58 @@ +import * as React from "react"; +import {EDGE_STATE} from "../../../constants"; +import {findNeighbor, isNodeActive} from "../../../utils"; +import Edge from "./Edge"; +import Node from "./Node"; +import Row from "./Row"; + +const LineRow = ({ + matX, + mat = [], + n = 1, + onLineClick, + onLineRightClick, + onLineMiddleClick, +}) => { + return ( + + {mat.length && + Array(n - 1) + .fill(0) + .map((_, i) => { + const edgeIdx = findNeighbor([matX, i], [matX, i + 1], mat); + const showEdge = edgeIdx !== -1; + return [ + , + onLineClick(matX, i, "horizontal")} + onRightClick={() => onLineRightClick(matX, i, "horizontal")} + onMiddleClick={() => onLineMiddleClick(matX, i, "horizontal")} + hovered={ + showEdge && + mat[matX][i].neigh[edgeIdx].state === EDGE_STATE.HOVERED + ? 1 + : 0 + } + key={`edge_${matX + i}`} + />, + ]; + }) + .concat([ + , + ])} + + ); +}; + +export default LineRow; diff --git a/src/components/Grid/components/N.js b/src/components/Grid/components/N.js new file mode 100644 index 0000000..cb48447 --- /dev/null +++ b/src/components/Grid/components/N.js @@ -0,0 +1,17 @@ +import * as React from "react"; +import {multiStyles} from "../../../utils"; +import styles from "../styles.module.scss"; + +const N = ({className, value, setNum, editorMode, ...props}) => { + return ( + + setNum(parseInt(e.target.value))} + value={value >= 0 && !isNaN(value) ? value : undefined} + /> + + ); +}; + +export default N; diff --git a/src/components/Grid/components/Node.js b/src/components/Grid/components/Node.js new file mode 100644 index 0000000..f16be0f --- /dev/null +++ b/src/components/Grid/components/Node.js @@ -0,0 +1,14 @@ +import * as React from "react"; +import {multiStyles} from "../../../utils"; +import styles from "../styles.module.scss"; + +const Node = ({className, active, ...props}) => { + return ( +
+ ); +}; + +export default Node; diff --git a/src/components/Grid/components/NumberRow.js b/src/components/Grid/components/NumberRow.js new file mode 100644 index 0000000..60bbf2c --- /dev/null +++ b/src/components/Grid/components/NumberRow.js @@ -0,0 +1,81 @@ +import * as React from "react"; +import {EDGE_STATE} from "../../../constants"; +import {findNeighbor} from "../../../utils"; +import Edge from "./Edge"; +import N from "./N"; +import Row from "./Row"; + +const NumberRow = ({ + matX, + mat = [], + n = 1, + onLineClick, + onLineRightClick, + onLineMiddleClick, + onNumberChange, + editorMode, +}) => { + const endEdgeIdx = + mat.length && findNeighbor([matX, n - 1], [matX + 1, n - 1], mat); + const showEndEdge = endEdgeIdx !== -1; + return ( + + {mat.length && + Array(n - 1) + .fill(0) + .map((_, i) => { + const edgeIdx = findNeighbor([matX, i], [matX + 1, i], mat); + const showEdge = edgeIdx !== -1; + return [ + onLineClick(matX, i, "vertical")} + onRightClick={() => onLineRightClick(matX, i, "vertical")} + onMiddleClick={() => onLineMiddleClick(matX, i, "vertical")} + hovered={ + showEdge && + mat[matX][i].neigh[edgeIdx].state === EDGE_STATE.HOVERED + ? 1 + : 0 + } + key={`edge_${matX + i}`} + />, + onNumberChange(matX, i, n)} + editorMode={editorMode} + key={`number_${matX + i}`} + />, + ]; + }) + .concat([ + onLineClick(matX, n - 1, "vertical")} + onRightClick={() => onLineRightClick(matX, n - 1, "vertical")} + onMiddleClick={() => onLineMiddleClick(matX, n - 1, "vertical")} + hovered={ + showEndEdge && + mat[matX][n - 1].neigh[endEdgeIdx].state === EDGE_STATE.HOVERED + ? 1 + : 0 + } + key={`edge_${matX + n - 1}`} + />, + ])} + + ); +}; + +export default NumberRow; diff --git a/src/components/Grid/components/Row.js b/src/components/Grid/components/Row.js new file mode 100644 index 0000000..e7381f9 --- /dev/null +++ b/src/components/Grid/components/Row.js @@ -0,0 +1,6 @@ +import * as React from "react"; +import styles from "../styles.module.scss"; + +const Row = ({children}) =>
{children}
; + +export default Row; diff --git a/src/components/Grid/index.js b/src/components/Grid/index.js new file mode 100644 index 0000000..11cd724 --- /dev/null +++ b/src/components/Grid/index.js @@ -0,0 +1,291 @@ +import * as React from "react"; +import {EDGE_STATE} from "../../constants"; +import {areValid, findNeighbor, getNeighbors, multiStyles} from "../../utils"; +import Controls from "../Controls"; +import LineRow from "./components/LineRow"; +import NumberRow from "./components/NumberRow"; +import styles from "./styles.module.scss"; + +/* + Grid traversal + (0, 0) -- (0, 1) -- (0, 2) --> + | | | + | | | + (1, 0) -- (1, 1) -- (1, 2) --> + | | | + | | | + (2, 0) -- (2, 1) -- (2, 2) --> + | | | + | | | + V V V +*/ + +const DEFAULT_NODE = {neigh: [], n: -1}; +/* + structure for neigh elements: + {state: , loc: [x, y]} +*/ + +const Grid = ({ + dim = [3, 3], + edges = [], + numbers = [], + matrix: propMatrix = [], + updateMatrix: updatePropMatrix = () => {}, + readOnly, + onReset: propOnReset, + editorMode, + check, + className, +}) => { + const nRows = dim[0]; + const nCols = dim[1]; + const nHorLine = nRows + 1; + const nVerLine = nCols + 1; + const [matrix, setMatrix] = React.useState([]); + + const setPropMatrix = (matrix) => { + if (Array.isArray(propMatrix)) { + updatePropMatrix(matrix); + } + }; + + // initializing matrix with prop edges and numbers + React.useEffect(() => { + let temp = new Array(nHorLine) + .fill(0) + .map(() => new Array(nVerLine).fill(DEFAULT_NODE)); + numbers.forEach(({r, c, n}) => { + if (r <= nRows && c <= nCols) { + temp[r - 1][c - 1] = {...temp[r - 1][c - 1], n}; + } + }); + edges.forEach(({a: [sx, sy], b: [ex, ey], notAllowed, hovered}) => { + const edgeState = notAllowed + ? EDGE_STATE.NOT_ALLOWED + : hovered + ? EDGE_STATE.HOVERED + : EDGE_STATE.ACTIVE; + let startX, startY, endX, endY, hor, ver; + hor = sx === ex; + ver = sy === ey; + + // excluding cases where both points are same + // or where edge is not hor | ver + // Assumption: all edges are between two adjacent nodes + if ((hor && ver) || (!hor && !ver)) { + return; + } + + // horizonal line + if (hor) { + startX = endX = sx; + if (sy > ey) { + startY = ey; + endY = sy; + } + if (sy < ey) { + startY = sy; + endY = ey; + } + } + // vertical line + if (ver) { + startY = endY = sy; + if (sx > ex) { + startX = ex; + endX = sx; + } + if (sx < ex) { + startX = sx; + endX = ex; + } + } + + // check if all nodes are valid nodes + if (areValid([startX, endX], nRows) && areValid([startY, endY], nCols)) { + temp[startX][startY] = { + ...temp[startX][startY], + neigh: [ + ...temp[startX][startY].neigh, + {state: edgeState, loc: [endX, endY]}, + ], + }; + temp[endX][endY] = { + ...temp[endX][endY], + neigh: [ + ...temp[endX][endY].neigh, + {state: edgeState, loc: [startX, startY]}, + ], + }; + } + }); + + setMatrix(temp); + setPropMatrix(temp); + }, []); + + const updateMatrix = (dataArray) => { + let temp = matrix.slice(); + dataArray.forEach(({node: [x, y], data}) => { + temp[x][y] = {...temp[x][y], ...data}; + }); + setMatrix(temp); + setPropMatrix(temp); + }; + + const onLineClick = (x, y, direction, click) => { + const isMiddleClick = click === "middle"; + const node = [x, y]; + const otherNode = direction === "horizontal" ? [x, y + 1] : [x + 1, y]; + let nodeUpdateData = {}; + let otherNodeUpdateData = {}; + const neighborIndex = findNeighbor(node, otherNode, matrix); + const otherNeighborIndex = findNeighbor(otherNode, node, matrix); + + // if there was already an edge (ACTIVE | NOT_ALLOWED) + if (neighborIndex !== -1) { + // if the edge was NOT_ALLOWED + const isNewEdgeStateEmpty = + matrix[x][y].neigh[neighborIndex].state === EDGE_STATE.NOT_ALLOWED; + let newNeighbors = getNeighbors(node, matrix).slice(); + newNeighbors.splice(neighborIndex, 1); + nodeUpdateData = { + neigh: [ + ...newNeighbors, + ...(isNewEdgeStateEmpty || isMiddleClick + ? [] + : [{state: EDGE_STATE.NOT_ALLOWED, loc: otherNode}]), + ], + }; + let newOtherNeighbors = getNeighbors(otherNode, matrix).slice(); + newOtherNeighbors.splice(otherNeighborIndex, 1); + otherNodeUpdateData = { + neigh: [ + ...newOtherNeighbors, + ...(isNewEdgeStateEmpty || isMiddleClick + ? [] + : [{state: EDGE_STATE.NOT_ALLOWED, loc: node}]), + ], + }; + } else if (!isMiddleClick) { + nodeUpdateData = { + neigh: [ + ...matrix[x][y].neigh, + { + state: + click === "left" ? EDGE_STATE.ACTIVE : EDGE_STATE.NOT_ALLOWED, + loc: otherNode, + }, + ], + }; + otherNodeUpdateData = { + neigh: [ + ...matrix[otherNode[0]][otherNode[1]].neigh, + { + state: + click === "left" ? EDGE_STATE.ACTIVE : EDGE_STATE.NOT_ALLOWED, + loc: node, + }, + ], + }; + } + updateMatrix([ + {node, data: nodeUpdateData}, + {node: otherNode, data: otherNodeUpdateData}, + ]); + }; + + const onLineLeftClick = (x, y, direction) => + onLineClick(x, y, direction, "left"); + const onLineRightClick = (x, y, direction) => + onLineClick(x, y, direction, "right"); + const onLineMiddleClick = (x, y, direction) => + onLineClick(x, y, direction, "middle"); + + const onNumberChange = (x, y, num) => { + updateMatrix([{node: [x, y], data: {n: num}}]); + }; + + const onReset = () => { + let temp = new Array(nHorLine) + .fill(0) + .map(() => new Array(nVerLine).fill(DEFAULT_NODE)); + numbers.forEach(({r, c, n}) => { + if (r <= nRows && c <= nCols) { + temp[r - 1][c - 1] = {...temp[r - 1][c - 1], n}; + } + }); + if (editorMode) { + propOnReset(); + } + setMatrix(temp); + setPropMatrix(temp); + }; + + const onRefresh = () => { + window.location.reload(); + }; + + return ( + <> + {matrix.length > 0 && ( +
+ {Array(nHorLine - 1) + .fill(0) + .map((_, i) => { + return [ + , + , + ]; + }) + .concat([ + , + ])} +
+ )} + {!readOnly && ( + + )} + + ); +}; + +export default Grid; diff --git a/src/components/Grid/styles.module.scss b/src/components/Grid/styles.module.scss new file mode 100644 index 0000000..3c1c03d --- /dev/null +++ b/src/components/Grid/styles.module.scss @@ -0,0 +1,296 @@ +$gridColor: #607d8b; +$gridActiveColor: #90a4ae; +$gridHoverColor: #eceff1; +$invalidColor: #ff8a80; +$validColor: #00e676; + +$transitionDuration: 0.2s; + +$gridScale: 1; + +$nodeSize: $gridScale * 6px; +@media (max-width: 500px) { + $gridScale: 0.5; +} +$nodeActiveSize: $gridScale * 7px; + +$edgeLength: $gridScale * 50px; +$edgeBreadth: $gridScale * 2px; + +$fontSize: $gridScale * 24px; + +$animationSeperation: 0.075s; +$animationScale: 1.5; +$animationDuration: 0.25s; +$animationTimingFunction: linear; +$animationFillMode: forwards; + +// canvas +.row, .canvas { + display: flex; + justify-content: center; + align-items: center; +} + +.canvas { + align-items: flex-start; + flex-direction: column; + overflow-x: auto; + box-sizing: border-box; + max-width: 100%; + padding: 50px; + border-radius: 10px; + background-color: #282c34; + + &.readOnly, &.passed { + * { + pointer-events: none; + cursor: default; + } + } +} + +// grid elements +.node { + display: flex; + height: $nodeSize; + width: $nodeSize; + background-color: $gridColor; + border-radius: 50%; + z-index: 1; + transition: background-color $transitionDuration; + + &.active { + background-color: $gridActiveColor; + width: $nodeActiveSize; + height: $nodeActiveSize; + margin: - ($nodeActiveSize - $nodeSize) / 2; + } +} + +.line { + display: flex; + $lineColor: $gridColor; + background-repeat: repeat; + opacity: 0; + position: relative; + cursor: pointer; + + &:after, &:before { + content: ""; + background-color: transparent; + position: absolute; + } + + &.horizontal { + background-size: 4*$edgeBreadth 100%; + background-image: linear-gradient(90deg, $lineColor 50%, transparent 50%); + height: $edgeBreadth; + width: $edgeLength; + + &:after, &:before { + height: ($nodeSize - $edgeBreadth); + width: 100%; + left: 0; + } + + &:after { + top: 100%; + } + + &:before { + bottom: 100%; + } + } + + &.vertical { + background-size: 100% 4*$edgeBreadth; + background-image: linear-gradient(180deg, $lineColor 50%, transparent 50%); + height: $edgeLength; + width: $edgeBreadth; + margin: 0 ($nodeSize - $edgeBreadth) / 2; + + &:after, &:before { + width: ($nodeSize - $edgeBreadth); + height: 100%; + top: 0; + } + + &:after { + right: 100%; + } + + &:before { + left: 100%; + } + } + + &:hover, &.hovered { + transition: opacity $transitionDuration; + opacity: 1; + } + + &.active { + opacity: 1; + background: $gridActiveColor; + transition: background-color $transitionDuration; + + &:hover { + background: $gridHoverColor; + + &.horizontal { + height: 1.5 * $edgeBreadth; + } + + &.vertical { + width: 1.5 * $edgeBreadth; + margin: 0 (($nodeSize - 1.5 * $edgeBreadth) / 2); + } + } + } + + &.cross { + opacity: 1; + background: none; + display: flex; + align-items: center; + justify-content: center; + + & > span { + position: absolute; + width: 6 * $edgeBreadth; + height: 3 * $edgeBreadth / 4; + background-color: $invalidColor; + border-radius: 2 * $edgeBreadth; + transition: all $transitionDuration; + + &:first-child { + transform: rotate(45deg); + } + + &:last-child { + transform: rotate(-45deg); + } + } + + &:hover { + & > span { + width: 7 * $edgeBreadth; + height: $edgeBreadth; + background-color: $gridHoverColor; + } + } + } +} + +.number { + font-size: $fontSize; + display: flex; + align-items: center; + justify-content: center; + height: $edgeLength; + width: $edgeLength; + + .invalid { + color: $invalidColor; + } + + input { + text-align: center; + height: inherit; + width: inherit; + background: inherit; + outline: none; + box-shadow: none; + border: inherit; + color: inherit; + font-size: inherit; + display: inherit; + align-items: inherit; + justify-content: inherit; + cursor: inherit; + } +} + +.canvas.failed { + .node.active { + background-color: $invalidColor; + } + + .line { + &:not(.cross){ + &:not(:hover) { + background-color: $invalidColor; + } + } + } +} + +.canvas.passed { + + .node.active { + animation: node $animationDuration $animationTimingFunction $animationFillMode; + } + + .line:not(.cross) { + &.horizontal { + animation: lineX $animationDuration $animationTimingFunction $animationFillMode; + } + + &.vertical { + animation: lineY $animationDuration $animationTimingFunction $animationFillMode; + } + } + + .node.active, .line:not(.cross) { + @for $i from 1 through 50 { + &:nth-child(#{$i}) { + animation-delay: ($i - 1) * $animationSeperation; + } + } + } + +} + +@keyframes node { + 0% { + background-color: $validColor; + transform: scale(1); + } + 50% { + transform: scale($animationScale); + } + 100% { + background-color: $validColor; + transform: scale(1); + } +} + +@keyframes lineX { + 0% { + background-color: $validColor; + transform: scaleY(1); + } + 50% { + transform: scaleY(1.5 * $animationScale); + } + 100% { + background-color: $validColor; + transform: scaleY(1); + } +} + + +@keyframes lineY { + 0% { + background-color: $validColor; + transform: scaleX(1); + } + 50% { + transform: scaleX(1.5 * $animationScale); + } + 100% { + background-color: $validColor; + transform: scaleX(1); + } +} diff --git a/src/components/Icons.js b/src/components/Icons.js new file mode 100644 index 0000000..fc9a769 --- /dev/null +++ b/src/components/Icons.js @@ -0,0 +1,89 @@ +import * as React from "react"; + +export const Undo = ({className, onClick}) => { + return ( + + + + ); +}; + +export const Redo = ({className, onClick}) => { + return ( + + + + + ); +}; + +export const Refresh = ({className, onClick}) => { + return ( + + + + + ); +}; + +export const Reset = ({className, onClick}) => { + return ( + + + + + ); +}; + +export const Plus = ({className, onClick}) => { + return ( + + + + + ); +}; diff --git a/src/components/Info/index.js b/src/components/Info/index.js new file mode 100644 index 0000000..64d77ac --- /dev/null +++ b/src/components/Info/index.js @@ -0,0 +1,127 @@ +import * as React from "react"; +import { + EXAMPLE_GRID_PROPS, + NOT_ALLOWED_1_GRID_PROPS, + NOT_ALLOWED_2_GRID_PROPS, +} from "../../constants"; +import Grid from "../Grid"; + +import styles from "./styles.module.scss"; + +const Info = () => { + const [open, setOpen] = React.useState(false); + + return ( + <> +
setOpen(true)}> + i +
+ {open && ( +
+ setOpen(false)} /> + +
+
Rules
+
+ Connect adjacent dots with vertical or horizontal lines, creating + a single loop. +
+ +
+
+
+ Crossovers or branches are not allowed (as shown by dotted lines + below). +
+ + +
+
+
+ Numbers in the puzzle indicate the number of lines that should + surround it. +
+
+ You can't draw lines around zeroes. +
+
+ Each puzzle has just one uniques solution. +
+
+
+
How to begin
+
+ Begin with the zero next to 3. If no such case is present, then + use one of the rules in the{" "} + + wikipedia link + {" "} + to start off. +
+
+ Since no lines can be drawn around zero, mark crosses around it as + shown. +
+
+ Now there's a cross in one space around 3. So we know the three + lines of 3 can only be drawn in the remaining three spaces. +
+
+ Next, these lines can only be extended in one direction each. +
+
Continue using the same logic.
+
+
+
Hints
+ + Keep eliminating possibilities by marking crosses in spaces + between dots where a line isn't possible, i.e., if you have + already completed required lines or where a line creation may + create a branch or a deadend. Visit the{" "} + + wikipedia link + {" "} + for more hints. + +
+
+
Controls
+ + + + + + + + + + + + + + + +
+ Left Mouse Button + Toggle line
+ Right Mouse Button + Mark line as crossed/not allowed
+ Middle Mouse Button + Reset line state
+
+
+ )} + + ); +}; + +export default Info; diff --git a/src/components/Info/styles.module.scss b/src/components/Info/styles.module.scss new file mode 100644 index 0000000..fe869bd --- /dev/null +++ b/src/components/Info/styles.module.scss @@ -0,0 +1,171 @@ +* { + box-sizing: border-box; +} + +.button { + position: fixed; + bottom: 10px; + right: 10px; + display: flex; + align-items: center; + justify-content: center; + background-color: #263238; + height: 40px; + width: 40px; + padding: 10px; + font-size: 20px; + border: 2px solid #455a64; + font-family: Georgia, 'Times New Roman', Times, serif; + + border-radius: 50%; + cursor: pointer; + transition: all 0.2s; + font-style: italic; + + &:hover { + border-color: #78909c; + font-size: 22px; + transform: scale(1.1); + color: white; + } +} + +.info { + position: fixed; + height: 100vh; + width: 100vw; + background-color: #212121; + z-index: 2; + font-family: monospace; + display: flex; + padding: 50px 40px; + align-items: flex-start; + justify-content: flex-start; + overflow-y: auto; + flex-direction: column; + gap: 50px; + + .cross { + $size: 20px; + height: $size; + width: $size; + position: fixed; + top: 40px; + right: 40px; + background-color: inherit; + padding: 3 * $size / 4; + z-index: 3; + cursor: pointer; + + &:after, &:before { + content: ""; + height: $size / 8; + width: $size; + background-color: #b0bec5; + position: absolute; + top: 50%; + left: 50%; + border-radius: $size / 8; + } + + &:after { + transform: translate(-50%, -50%) rotate(45deg); + } + + &:before { + transform: translate(-50%, -50%) rotate(-45deg); + } + } + + section { + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: flex-start; + gap: 20px; + font-size: 17px; + color: #e0e0e0; + width: 100%; + + & > span { + padding-left: 20px; + } + + a { + color: #00b0ff; + text-decoration: none; + } + } + + .title { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + margin-bottom: 10px; + font-size: 26px; + text-transform: uppercase; + font-weight: 600; + color: #f5f5f5; + } + + .item { + line-height: inherit; + font-size: inherit; + padding-left: 20px; + color: inherit; + position: relative; + display: flex; + flex-direction: column; + gap: 20px; + width: 100%; + + &:before { + content: ""; + position: absolute; + width: 7px; + height: 7px; + top: 7px; + left: 0; + box-sizing: border-box; + border-radius: 50%; + background-color: #424242; + border: 1.5px solid #9e9e9e; + } + + .eg { + display: flex; + flex-wrap: wrap; + align-self: center; + justify-self: center; + gap: 20px; + align-items: center; + justify-content: center; + width: 100%; + } + } + + table { + width: 100%; + padding-left: 20px; + + tr { + width: 100%; + + td { + width: 50%; + padding-bottom: 10px; + + &:last-child { + padding-left: 10px; + } + } + + &:last-child { + td { + padding-bottom: 0; + } + } + } + } +} diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..2b1ae2c --- /dev/null +++ b/src/constants.js @@ -0,0 +1,162 @@ +export const EDGE_STATE = { + ACTIVE: "ACTIVE", + NOT_ALLOWED: "NOT_ALLOWED", + HOVERED: "HOOVERED", +}; + +export const EXAMPLE_GRID_PROPS = { + readOnly: true, + dim: [7, 7], + edges: [ + {a: [0, 0], b: [0, 1]}, + {a: [0, 0], b: [1, 0]}, + {a: [0, 1], b: [0, 2], notAllowed: true}, + {a: [0, 1], b: [1, 1]}, + {a: [0, 2], b: [0, 3], notAllowed: true}, + {a: [0, 2], b: [1, 2], notAllowed: true}, + {a: [0, 3], b: [0, 4]}, + {a: [0, 3], b: [1, 3]}, + {a: [0, 4], b: [0, 5]}, + {a: [0, 4], b: [1, 4], notAllowed: true}, + {a: [0, 5], b: [0, 6]}, + {a: [0, 5], b: [1, 5], notAllowed: true}, + {a: [0, 6], b: [0, 7]}, + {a: [0, 6], b: [1, 6], notAllowed: true}, + {a: [0, 7], b: [1, 7]}, + {a: [1, 0], b: [1, 1], notAllowed: true}, + {a: [1, 0], b: [2, 0]}, + {a: [1, 1], b: [1, 2]}, + {a: [1, 2], b: [1, 3], notAllowed: true}, + {a: [1, 2], b: [2, 2]}, + {a: [1, 3], b: [1, 4]}, + {a: [1, 3], b: [2, 3], notAllowed: true}, + {a: [1, 4], b: [1, 5]}, + {a: [1, 4], b: [2, 4], notAllowed: true}, + {a: [1, 5], b: [1, 6]}, + {a: [1, 5], b: [2, 5], notAllowed: true}, + {a: [1, 6], b: [1, 7], notAllowed: true}, + {a: [1, 6], b: [2, 6]}, + {a: [1, 7], b: [2, 7]}, + {a: [2, 0], b: [2, 1]}, + {a: [2, 1], b: [3, 1]}, + {a: [2, 2], b: [2, 3]}, + {a: [2, 2], b: [3, 2], notAllowed: true}, + {a: [2, 3], b: [2, 4], notAllowed: true}, + {a: [2, 3], b: [3, 3]}, + {a: [2, 4], b: [2, 5], notAllowed: true}, + {a: [2, 4], b: [3, 4], notAllowed: true}, + {a: [2, 5], b: [2, 6]}, + {a: [2, 5], b: [3, 5]}, + {a: [2, 6], b: [2, 7], notAllowed: true}, + {a: [2, 6], b: [3, 6], notAllowed: true}, + {a: [2, 7], b: [3, 7]}, + {a: [3, 0], b: [3, 1], notAllowed: true}, + {a: [3, 1], b: [3, 2]}, + {a: [3, 1], b: [4, 1], notAllowed: true}, + {a: [3, 2], b: [4, 2]}, + {a: [3, 3], b: [3, 4]}, + {a: [3, 4], b: [3, 5], notAllowed: true}, + {a: [3, 4], b: [4, 4]}, + {a: [3, 5], b: [3, 6], notAllowed: true}, + {a: [3, 5], b: [4, 5]}, + {a: [3, 6], b: [3, 7]}, + {a: [3, 6], b: [4, 6]}, + {a: [4, 0], b: [4, 1], notAllowed: true}, + {a: [4, 1], b: [4, 2], notAllowed: true}, + {a: [4, 1], b: [5, 1], notAllowed: true}, + {a: [4, 2], b: [4, 3]}, + {a: [4, 2], b: [5, 2], notAllowed: true}, + {a: [4, 3], b: [4, 4], notAllowed: true}, + {a: [4, 3], b: [5, 3]}, + {a: [4, 4], b: [4, 5]}, + {a: [4, 4], b: [5, 4], notAllowed: true}, + {a: [4, 5], b: [4, 6], notAllowed: true}, + {a: [4, 5], b: [5, 5], notAllowed: true}, + {a: [4, 6], b: [4, 7]}, + {a: [4, 6], b: [5, 6], notAllowed: true}, + {a: [4, 7], b: [5, 7]}, + {a: [5, 0], b: [5, 1], notAllowed: true}, + {a: [5, 1], b: [5, 2]}, + {a: [5, 1], b: [6, 1]}, + {a: [5, 2], b: [5, 3]}, + {a: [5, 2], b: [6, 2], notAllowed: true}, + {a: [5, 3], b: [5, 4], notAllowed: true}, + {a: [5, 3], b: [6, 3], notAllowed: true}, + {a: [5, 4], b: [5, 5]}, + {a: [5, 4], b: [6, 4]}, + {a: [5, 5], b: [5, 6], notAllowed: true}, + {a: [5, 5], b: [6, 5]}, + {a: [5, 6], b: [5, 7]}, + {a: [5, 6], b: [6, 6]}, + {a: [6, 0], b: [6, 1]}, + {a: [6, 0], b: [7, 0]}, + {a: [6, 1], b: [7, 1], notAllowed: true}, + {a: [6, 2], b: [6, 3]}, + {a: [6, 2], b: [7, 2]}, + {a: [6, 3], b: [6, 4], notAllowed: true}, + {a: [6, 3], b: [7, 3]}, + {a: [6, 4], b: [6, 5], notAllowed: true}, + {a: [6, 4], b: [7, 4]}, + {a: [6, 5], b: [6, 6], notAllowed: true}, + {a: [6, 5], b: [7, 5]}, + {a: [6, 6], b: [6, 7]}, + {a: [6, 6], b: [7, 6], notAllowed: true}, + {a: [6, 7], b: [7, 7]}, + {a: [7, 0], b: [7, 1]}, + {a: [7, 1], b: [7, 2]}, + {a: [7, 3], b: [7, 4]}, + {a: [7, 5], b: [7, 6]}, + {a: [7, 6], b: [7, 7]}, + ], + numbers: [ + {r: 1, c: 4, n: 3}, + {r: 1, c: 5, n: 2}, + {r: 1, c: 6, n: 2}, + {r: 2, c: 1, n: 2}, + {r: 2, c: 2, n: 2}, + {r: 2, c: 3, n: 2}, + {r: 2, c: 6, n: 3}, + {r: 3, c: 1, n: 2}, + {r: 3, c: 2, n: 2}, + {r: 3, c: 5, n: 1}, + {r: 3, c: 6, n: 2}, + {r: 4, c: 3, n: 2}, + {r: 4, c: 5, n: 3}, + {r: 4, c: 7, n: 3}, + {r: 5, c: 1, n: 0}, + {r: 5, c: 6, n: 0}, + {r: 5, c: 7, n: 3}, + {r: 6, c: 1, n: 2}, + {r: 6, c: 7, n: 3}, + {r: 7, c: 2, n: 2}, + {r: 7, c: 3, n: 3}, + {r: 7, c: 5, n: 2}, + ], +}; + +export const NOT_ALLOWED_1_GRID_PROPS = { + readOnly: true, + dim: [2, 2], + edges: [ + {a: [0, 1], b: [1, 1], hovered: true}, + {a: [1, 0], b: [1, 1], hovered: true}, + {a: [1, 1], b: [1, 2]}, + {a: [1, 1], b: [2, 1]}, + ], + numbers: [{r: 2, c: 2, n: 2}], +}; + +export const NOT_ALLOWED_2_GRID_PROPS = { + readOnly: true, + dim: [2, 3], + edges: [ + {a: [0, 1], b: [1, 1]}, + {a: [0, 2], b: [1, 2]}, + {a: [1, 0], b: [1, 1], hovered: true}, + {a: [1, 1], b: [1, 2]}, + {a: [1, 1], b: [2, 1], hovered: true}, + {a: [1, 2], b: [1, 3], hovered: true}, + {a: [1, 2], b: [2, 2], hovered: true}, + ], + numbers: [{r: 1, c: 2, n: 3}], +}; diff --git a/src/examples.js b/src/examples.js new file mode 100644 index 0000000..7b93bf0 --- /dev/null +++ b/src/examples.js @@ -0,0 +1,608 @@ +export const examples = [ + { + dim: [7, 7], + numbers: [ + {r: 1, c: 2, n: 2}, + {r: 1, c: 7, n: 3}, + {r: 2, c: 2, n: 3}, + {r: 2, c: 4, n: 2}, + {r: 2, c: 5, n: 2}, + {r: 2, c: 7, n: 1}, + {r: 3, c: 2, n: 3}, + {r: 3, c: 3, n: 2}, + {r: 3, c: 4, n: 2}, + {r: 3, c: 6, n: 2}, + {r: 3, c: 7, n: 2}, + {r: 4, c: 3, n: 2}, + {r: 4, c: 4, n: 0}, + {r: 4, c: 5, n: 3}, + {r: 4, c: 6, n: 2}, + {r: 5, c: 3, n: 2}, + {r: 5, c: 5, n: 3}, + {r: 5, c: 6, n: 2}, + {r: 6, c: 2, n: 2}, + {r: 6, c: 3, n: 1}, + {r: 6, c: 5, n: 1}, + {r: 7, c: 5, n: 3}, + {r: 7, c: 7, n: 3}, + ], + solution: [ + {a: [0, 0], b: [0, 1]}, + {a: [0, 0], b: [1, 0]}, + {a: [0, 1], b: [1, 1]}, + {a: [0, 3], b: [0, 4]}, + {a: [0, 3], b: [1, 3]}, + {a: [0, 4], b: [1, 4]}, + {a: [0, 6], b: [0, 7]}, + {a: [0, 6], b: [1, 6]}, + {a: [0, 7], b: [1, 7]}, + {a: [1, 0], b: [2, 0]}, + {a: [1, 1], b: [1, 2]}, + {a: [1, 2], b: [2, 2]}, + {a: [1, 3], b: [2, 3]}, + {a: [1, 4], b: [2, 4]}, + {a: [1, 5], b: [1, 6]}, + {a: [1, 5], b: [2, 5]}, + {a: [1, 7], b: [2, 7]}, + {a: [2, 0], b: [3, 0]}, + {a: [2, 1], b: [2, 2]}, + {a: [2, 1], b: [3, 1]}, + {a: [2, 3], b: [3, 3]}, + {a: [2, 4], b: [3, 4]}, + {a: [2, 5], b: [2, 6]}, + {a: [2, 6], b: [3, 6]}, + {a: [2, 7], b: [3, 7]}, + {a: [3, 0], b: [4, 0]}, + {a: [3, 1], b: [3, 2]}, + {a: [3, 2], b: [3, 3]}, + {a: [3, 4], b: [3, 5]}, + {a: [3, 5], b: [4, 5]}, + {a: [3, 6], b: [4, 6]}, + {a: [3, 7], b: [4, 7]}, + {a: [4, 0], b: [5, 0]}, + {a: [4, 1], b: [4, 2]}, + {a: [4, 1], b: [5, 1]}, + {a: [4, 2], b: [4, 3]}, + {a: [4, 3], b: [5, 3]}, + {a: [4, 4], b: [4, 5]}, + {a: [4, 4], b: [5, 4]}, + {a: [4, 6], b: [5, 6]}, + {a: [4, 7], b: [5, 7]}, + {a: [5, 0], b: [6, 0]}, + {a: [5, 1], b: [6, 1]}, + {a: [5, 3], b: [6, 3]}, + {a: [5, 4], b: [5, 5]}, + {a: [5, 5], b: [5, 6]}, + {a: [5, 7], b: [6, 7]}, + {a: [6, 0], b: [7, 0]}, + {a: [6, 1], b: [6, 2]}, + {a: [6, 2], b: [7, 2]}, + {a: [6, 3], b: [6, 4]}, + {a: [6, 4], b: [7, 4]}, + {a: [6, 5], b: [6, 6]}, + {a: [6, 5], b: [7, 5]}, + {a: [6, 6], b: [7, 6]}, + {a: [6, 7], b: [7, 7]}, + {a: [7, 0], b: [7, 1]}, + {a: [7, 1], b: [7, 2]}, + {a: [7, 4], b: [7, 5]}, + {a: [7, 6], b: [7, 7]}, + ], + }, + { + dim: [7, 7], + numbers: [ + {r: 1, c: 4, n: 1}, + {r: 1, c: 7, n: 3}, + {r: 2, c: 1, n: 2}, + {r: 2, c: 2, n: 2}, + {r: 2, c: 3, n: 3}, + {r: 2, c: 4, n: 3}, + {r: 2, c: 5, n: 2}, + {r: 3, c: 1, n: 2}, + {r: 3, c: 2, n: 2}, + {r: 3, c: 5, n: 2}, + {r: 3, c: 6, n: 2}, + {r: 3, c: 7, n: 3}, + {r: 4, c: 1, n: 3}, + {r: 4, c: 2, n: 0}, + {r: 4, c: 3, n: 2}, + {r: 4, c: 7, n: 3}, + {r: 5, c: 1, n: 3}, + {r: 5, c: 5, n: 3}, + {r: 5, c: 6, n: 2}, + {r: 6, c: 2, n: 1}, + {r: 6, c: 4, n: 2}, + {r: 6, c: 6, n: 3}, + {r: 7, c: 2, n: 2}, + {r: 7, c: 6, n: 2}, + ], + solution: [ + {a: [0, 0], b: [0, 1]}, + {a: [0, 0], b: [1, 0]}, + {a: [0, 1], b: [0, 2]}, + {a: [0, 2], b: [0, 3]}, + {a: [0, 3], b: [0, 4]}, + {a: [0, 4], b: [0, 5]}, + {a: [0, 5], b: [0, 6]}, + {a: [0, 6], b: [0, 7]}, + {a: [0, 7], b: [1, 7]}, + {a: [1, 0], b: [1, 1]}, + {a: [1, 1], b: [2, 1]}, + {a: [1, 2], b: [1, 3]}, + {a: [1, 2], b: [2, 2]}, + {a: [1, 3], b: [2, 3]}, + {a: [1, 4], b: [1, 5]}, + {a: [1, 4], b: [2, 4]}, + {a: [1, 5], b: [1, 6]}, + {a: [1, 6], b: [1, 7]}, + {a: [2, 1], b: [3, 1]}, + {a: [2, 2], b: [3, 2]}, + {a: [2, 3], b: [2, 4]}, + {a: [2, 5], b: [2, 6]}, + {a: [2, 5], b: [3, 5]}, + {a: [2, 6], b: [2, 7]}, + {a: [2, 7], b: [3, 7]}, + {a: [3, 0], b: [3, 1]}, + {a: [3, 0], b: [4, 0]}, + {a: [3, 2], b: [3, 3]}, + {a: [3, 3], b: [3, 4]}, + {a: [3, 4], b: [3, 5]}, + {a: [3, 6], b: [3, 7]}, + {a: [3, 6], b: [4, 6]}, + {a: [4, 0], b: [4, 1]}, + {a: [4, 1], b: [5, 1]}, + {a: [4, 2], b: [4, 3]}, + {a: [4, 2], b: [5, 2]}, + {a: [4, 3], b: [5, 3]}, + {a: [4, 4], b: [4, 5]}, + {a: [4, 4], b: [5, 4]}, + {a: [4, 5], b: [5, 5]}, + {a: [4, 6], b: [4, 7]}, + {a: [4, 7], b: [5, 7]}, + {a: [5, 0], b: [5, 1]}, + {a: [5, 0], b: [6, 0]}, + {a: [5, 2], b: [6, 2]}, + {a: [5, 3], b: [5, 4]}, + {a: [5, 5], b: [5, 6]}, + {a: [5, 6], b: [6, 6]}, + {a: [5, 7], b: [6, 7]}, + {a: [6, 0], b: [7, 0]}, + {a: [6, 2], b: [7, 2]}, + {a: [6, 3], b: [6, 4]}, + {a: [6, 3], b: [7, 3]}, + {a: [6, 4], b: [6, 5]}, + {a: [6, 5], b: [6, 6]}, + {a: [6, 7], b: [7, 7]}, + {a: [7, 0], b: [7, 1]}, + {a: [7, 1], b: [7, 2]}, + {a: [7, 3], b: [7, 4]}, + {a: [7, 4], b: [7, 5]}, + {a: [7, 5], b: [7, 6]}, + {a: [7, 6], b: [7, 7]}, + ], + }, + { + dim: [7, 7], + numbers: [ + {r: 1, c: 2, n: 2}, + {r: 1, c: 4, n: 3}, + {r: 1, c: 5, n: 2}, + {r: 1, c: 6, n: 3}, + {r: 2, c: 2, n: 1}, + {r: 2, c: 3, n: 3}, + {r: 2, c: 4, n: 2}, + {r: 2, c: 6, n: 2}, + {r: 3, c: 2, n: 2}, + {r: 3, c: 4, n: 0}, + {r: 3, c: 5, n: 3}, + {r: 4, c: 1, n: 1}, + {r: 4, c: 2, n: 2}, + {r: 4, c: 3, n: 3}, + {r: 4, c: 5, n: 3}, + {r: 5, c: 1, n: 2}, + {r: 5, c: 2, n: 2}, + {r: 5, c: 3, n: 1}, + {r: 5, c: 4, n: 1}, + {r: 5, c: 5, n: 1}, + {r: 5, c: 6, n: 2}, + {r: 6, c: 1, n: 2}, + {r: 6, c: 3, n: 2}, + {r: 7, c: 4, n: 2}, + {r: 7, c: 5, n: 3}, + {r: 7, c: 7, n: 3}, + ], + solution: [ + {a: [0, 0], b: [0, 1]}, + {a: [0, 0], b: [1, 0]}, + {a: [0, 1], b: [0, 2]}, + {a: [0, 2], b: [1, 2]}, + {a: [0, 3], b: [0, 4]}, + {a: [0, 3], b: [1, 3]}, + {a: [0, 4], b: [1, 4]}, + {a: [0, 5], b: [0, 6]}, + {a: [0, 5], b: [1, 5]}, + {a: [0, 6], b: [0, 7]}, + {a: [0, 7], b: [1, 7]}, + {a: [1, 0], b: [2, 0]}, + {a: [1, 2], b: [2, 2]}, + {a: [1, 3], b: [2, 3]}, + {a: [1, 4], b: [2, 4]}, + {a: [1, 5], b: [1, 6]}, + {a: [1, 6], b: [2, 6]}, + {a: [1, 7], b: [2, 7]}, + {a: [2, 0], b: [2, 1]}, + {a: [2, 1], b: [3, 1]}, + {a: [2, 2], b: [2, 3]}, + {a: [2, 4], b: [2, 5]}, + {a: [2, 5], b: [3, 5]}, + {a: [2, 6], b: [3, 6]}, + {a: [2, 7], b: [3, 7]}, + {a: [3, 1], b: [3, 2]}, + {a: [3, 2], b: [3, 3]}, + {a: [3, 3], b: [4, 3]}, + {a: [3, 4], b: [3, 5]}, + {a: [3, 4], b: [4, 4]}, + {a: [3, 6], b: [4, 6]}, + {a: [3, 7], b: [4, 7]}, + {a: [4, 0], b: [4, 1]}, + {a: [4, 0], b: [5, 0]}, + {a: [4, 1], b: [4, 2]}, + {a: [4, 2], b: [4, 3]}, + {a: [4, 4], b: [4, 5]}, + {a: [4, 5], b: [4, 6]}, + {a: [4, 7], b: [5, 7]}, + {a: [5, 0], b: [6, 0]}, + {a: [5, 1], b: [5, 2]}, + {a: [5, 1], b: [6, 1]}, + {a: [5, 2], b: [6, 2]}, + {a: [5, 3], b: [5, 4]}, + {a: [5, 3], b: [6, 3]}, + {a: [5, 4], b: [6, 4]}, + {a: [5, 5], b: [5, 6]}, + {a: [5, 5], b: [6, 5]}, + {a: [5, 6], b: [6, 6]}, + {a: [5, 7], b: [6, 7]}, + {a: [6, 0], b: [7, 0]}, + {a: [6, 1], b: [7, 1]}, + {a: [6, 2], b: [7, 2]}, + {a: [6, 3], b: [7, 3]}, + {a: [6, 4], b: [7, 4]}, + {a: [6, 5], b: [7, 5]}, + {a: [6, 6], b: [7, 6]}, + {a: [6, 7], b: [7, 7]}, + {a: [7, 0], b: [7, 1]}, + {a: [7, 2], b: [7, 3]}, + {a: [7, 4], b: [7, 5]}, + {a: [7, 6], b: [7, 7]}, + ], + }, + { + dim: [7, 7], + numbers: [ + {r: 1, c: 2, n: 2}, + {r: 1, c: 3, n: 2}, + {r: 1, c: 4, n: 2}, + {r: 1, c: 5, n: 2}, + {r: 1, c: 6, n: 2}, + {r: 1, c: 7, n: 2}, + {r: 2, c: 1, n: 2}, + {r: 2, c: 7, n: 2}, + {r: 3, c: 1, n: 2}, + {r: 3, c: 3, n: 2}, + {r: 3, c: 4, n: 2}, + {r: 3, c: 5, n: 2}, + {r: 3, c: 7, n: 2}, + {r: 4, c: 1, n: 3}, + {r: 4, c: 5, n: 2}, + {r: 4, c: 7, n: 2}, + {r: 5, c: 2, n: 1}, + {r: 5, c: 4, n: 3}, + {r: 5, c: 5, n: 0}, + {r: 5, c: 6, n: 2}, + {r: 5, c: 7, n: 1}, + {r: 6, c: 1, n: 2}, + {r: 6, c: 2, n: 3}, + {r: 6, c: 4, n: 3}, + {r: 6, c: 5, n: 2}, + {r: 7, c: 5, n: 2}, + {r: 7, c: 6, n: 2}, + ], + solution: [ + {a: [0, 0], b: [0, 1]}, + {a: [0, 0], b: [1, 0]}, + {a: [0, 1], b: [0, 2]}, + {a: [0, 2], b: [0, 3]}, + {a: [0, 3], b: [0, 4]}, + {a: [0, 4], b: [0, 5]}, + {a: [0, 5], b: [0, 6]}, + {a: [0, 6], b: [0, 7]}, + {a: [0, 7], b: [1, 7]}, + {a: [1, 0], b: [1, 1]}, + {a: [1, 1], b: [1, 2]}, + {a: [1, 2], b: [1, 3]}, + {a: [1, 3], b: [1, 4]}, + {a: [1, 4], b: [1, 5]}, + {a: [1, 5], b: [1, 6]}, + {a: [1, 6], b: [2, 6]}, + {a: [1, 7], b: [2, 7]}, + {a: [2, 0], b: [2, 1]}, + {a: [2, 0], b: [3, 0]}, + {a: [2, 1], b: [2, 2]}, + {a: [2, 2], b: [2, 3]}, + {a: [2, 3], b: [2, 4]}, + {a: [2, 4], b: [2, 5]}, + {a: [2, 5], b: [3, 5]}, + {a: [2, 6], b: [3, 6]}, + {a: [2, 7], b: [3, 7]}, + {a: [3, 0], b: [4, 0]}, + {a: [3, 1], b: [3, 2]}, + {a: [3, 1], b: [4, 1]}, + {a: [3, 2], b: [3, 3]}, + {a: [3, 3], b: [3, 4]}, + {a: [3, 4], b: [4, 4]}, + {a: [3, 5], b: [4, 5]}, + {a: [3, 6], b: [4, 6]}, + {a: [3, 7], b: [4, 7]}, + {a: [4, 0], b: [4, 1]}, + {a: [4, 3], b: [4, 4]}, + {a: [4, 3], b: [5, 3]}, + {a: [4, 5], b: [4, 6]}, + {a: [4, 7], b: [5, 7]}, + {a: [5, 1], b: [5, 2]}, + {a: [5, 1], b: [6, 1]}, + {a: [5, 2], b: [6, 2]}, + {a: [5, 3], b: [5, 4]}, + {a: [5, 4], b: [6, 4]}, + {a: [5, 5], b: [5, 6]}, + {a: [5, 5], b: [6, 5]}, + {a: [5, 6], b: [6, 6]}, + {a: [5, 7], b: [6, 7]}, + {a: [6, 0], b: [6, 1]}, + {a: [6, 0], b: [7, 0]}, + {a: [6, 2], b: [6, 3]}, + {a: [6, 3], b: [6, 4]}, + {a: [6, 5], b: [7, 5]}, + {a: [6, 6], b: [7, 6]}, + {a: [6, 7], b: [7, 7]}, + {a: [7, 0], b: [7, 1]}, + {a: [7, 1], b: [7, 2]}, + {a: [7, 2], b: [7, 3]}, + {a: [7, 3], b: [7, 4]}, + {a: [7, 4], b: [7, 5]}, + {a: [7, 6], b: [7, 7]}, + ], + }, + { + dim: [5, 5], + numbers: [ + {r: 1, c: 2, n: 3}, + {r: 1, c: 3, n: 1}, + {r: 1, c: 4, n: 2}, + {r: 2, c: 2, n: 3}, + {r: 2, c: 5, n: 2}, + {r: 3, c: 2, n: 2}, + {r: 3, c: 5, n: 3}, + {r: 4, c: 1, n: 2}, + {r: 4, c: 3, n: 1}, + {r: 4, c: 5, n: 2}, + {r: 5, c: 1, n: 0}, + {r: 5, c: 4, n: 3}, + ], + solution: [ + {a: [0, 0], b: [0, 1]}, + {a: [0, 0], b: [1, 0]}, + {a: [0, 1], b: [0, 2]}, + {a: [0, 2], b: [1, 2]}, + {a: [0, 4], b: [0, 5]}, + {a: [0, 4], b: [1, 4]}, + {a: [0, 5], b: [1, 5]}, + {a: [1, 0], b: [2, 0]}, + {a: [1, 1], b: [1, 2]}, + {a: [1, 1], b: [2, 1]}, + {a: [1, 3], b: [1, 4]}, + {a: [1, 3], b: [2, 3]}, + {a: [1, 5], b: [2, 5]}, + {a: [2, 0], b: [3, 0]}, + {a: [2, 1], b: [2, 2]}, + {a: [2, 2], b: [3, 2]}, + {a: [2, 3], b: [3, 3]}, + {a: [2, 4], b: [2, 5]}, + {a: [2, 4], b: [3, 4]}, + {a: [3, 0], b: [3, 1]}, + {a: [3, 1], b: [4, 1]}, + {a: [3, 2], b: [3, 3]}, + {a: [3, 4], b: [3, 5]}, + {a: [3, 5], b: [4, 5]}, + {a: [4, 1], b: [4, 2]}, + {a: [4, 2], b: [5, 2]}, + {a: [4, 3], b: [4, 4]}, + {a: [4, 3], b: [5, 3]}, + {a: [4, 4], b: [5, 4]}, + {a: [4, 5], b: [5, 5]}, + {a: [5, 2], b: [5, 3]}, + {a: [5, 4], b: [5, 5]}, + ], + }, + { + dim: [7, 7], + numbers: [ + {r: 1, c: 3, n: 2}, + {r: 2, c: 1, n: 2}, + {r: 2, c: 2, n: 3}, + {r: 2, c: 4, n: 2}, + {r: 2, c: 6, n: 2}, + {r: 3, c: 3, n: 1}, + {r: 3, c: 5, n: 3}, + {r: 4, c: 3, n: 2}, + {r: 4, c: 4, n: 3}, + {r: 4, c: 6, n: 3}, + {r: 4, c: 7, n: 2}, + {r: 5, c: 1, n: 3}, + {r: 5, c: 3, n: 0}, + {r: 5, c: 4, n: 2}, + {r: 6, c: 1, n: 2}, + {r: 6, c: 2, n: 3}, + {r: 6, c: 6, n: 2}, + {r: 7, c: 3, n: 1}, + {r: 7, c: 5, n: 3}, + {r: 7, c: 7, n: 3}, + ], + solution: [ + {a: [0, 0], b: [0, 1]}, + {a: [0, 0], b: [1, 0]}, + {a: [0, 1], b: [1, 1]}, + {a: [0, 2], b: [0, 3]}, + {a: [0, 2], b: [1, 2]}, + {a: [0, 3], b: [0, 4]}, + {a: [0, 4], b: [1, 4]}, + {a: [0, 5], b: [0, 6]}, + {a: [0, 5], b: [1, 5]}, + {a: [0, 6], b: [0, 7]}, + {a: [0, 7], b: [1, 7]}, + {a: [1, 0], b: [2, 0]}, + {a: [1, 1], b: [2, 1]}, + {a: [1, 2], b: [2, 2]}, + {a: [1, 3], b: [1, 4]}, + {a: [1, 3], b: [2, 3]}, + {a: [1, 5], b: [1, 6]}, + {a: [1, 6], b: [2, 6]}, + {a: [1, 7], b: [2, 7]}, + {a: [2, 0], b: [3, 0]}, + {a: [2, 1], b: [2, 2]}, + {a: [2, 3], b: [3, 3]}, + {a: [2, 4], b: [2, 5]}, + {a: [2, 4], b: [3, 4]}, + {a: [2, 5], b: [3, 5]}, + {a: [2, 6], b: [3, 6]}, + {a: [2, 7], b: [3, 7]}, + {a: [3, 0], b: [3, 1]}, + {a: [3, 1], b: [3, 2]}, + {a: [3, 2], b: [4, 2]}, + {a: [3, 3], b: [4, 3]}, + {a: [3, 4], b: [4, 4]}, + {a: [3, 5], b: [4, 5]}, + {a: [3, 6], b: [4, 6]}, + {a: [3, 7], b: [4, 7]}, + {a: [4, 0], b: [4, 1]}, + {a: [4, 0], b: [5, 0]}, + {a: [4, 1], b: [4, 2]}, + {a: [4, 3], b: [4, 4]}, + {a: [4, 5], b: [4, 6]}, + {a: [4, 7], b: [5, 7]}, + {a: [5, 0], b: [5, 1]}, + {a: [5, 1], b: [5, 2]}, + {a: [5, 2], b: [6, 2]}, + {a: [5, 3], b: [5, 4]}, + {a: [5, 3], b: [6, 3]}, + {a: [5, 4], b: [5, 5]}, + {a: [5, 5], b: [5, 6]}, + {a: [5, 6], b: [6, 6]}, + {a: [5, 7], b: [6, 7]}, + {a: [6, 0], b: [6, 1]}, + {a: [6, 0], b: [7, 0]}, + {a: [6, 1], b: [6, 2]}, + {a: [6, 3], b: [6, 4]}, + {a: [6, 4], b: [6, 5]}, + {a: [6, 5], b: [7, 5]}, + {a: [6, 6], b: [7, 6]}, + {a: [6, 7], b: [7, 7]}, + {a: [7, 0], b: [7, 1]}, + {a: [7, 1], b: [7, 2]}, + {a: [7, 2], b: [7, 3]}, + {a: [7, 3], b: [7, 4]}, + {a: [7, 4], b: [7, 5]}, + {a: [7, 6], b: [7, 7]}, + ], + }, + { + dim: [7, 7], + numbers: [ + {r: 1, c: 3, n: 1}, + {r: 1, c: 6, n: 3}, + {r: 1, c: 7, n: 2}, + {r: 2, c: 1, n: 2}, + {r: 2, c: 4, n: 2}, + {r: 2, c: 7, n: 2}, + {r: 3, c: 2, n: 2}, + {r: 3, c: 3, n: 1}, + {r: 3, c: 4, n: 2}, + {r: 3, c: 5, n: 0}, + {r: 3, c: 6, n: 3}, + {r: 4, c: 1, n: 2}, + {r: 4, c: 4, n: 2}, + {r: 4, c: 5, n: 3}, + {r: 5, c: 1, n: 3}, + {r: 5, c: 4, n: 2}, + {r: 5, c: 6, n: 2}, + {r: 6, c: 1, n: 2}, + {r: 6, c: 2, n: 2}, + {r: 6, c: 3, n: 2}, + {r: 6, c: 7, n: 1}, + {r: 7, c: 2, n: 2}, + {r: 7, c: 5, n: 1}, + ], + solution: [ + {a: [0, 0], b: [0, 1]}, + {a: [0, 0], b: [1, 0]}, + {a: [0, 1], b: [0, 2]}, + {a: [0, 2], b: [0, 3]}, + {a: [0, 3], b: [0, 4]}, + {a: [0, 4], b: [1, 4]}, + {a: [0, 5], b: [0, 6]}, + {a: [0, 5], b: [1, 5]}, + {a: [0, 6], b: [1, 6]}, + {a: [1, 0], b: [1, 1]}, + {a: [1, 1], b: [1, 2]}, + {a: [1, 2], b: [2, 2]}, + {a: [1, 3], b: [1, 4]}, + {a: [1, 3], b: [2, 3]}, + {a: [1, 5], b: [2, 5]}, + {a: [1, 6], b: [1, 7]}, + {a: [1, 7], b: [2, 7]}, + {a: [2, 0], b: [2, 1]}, + {a: [2, 0], b: [3, 0]}, + {a: [2, 1], b: [2, 2]}, + {a: [2, 3], b: [3, 3]}, + {a: [2, 5], b: [2, 6]}, + {a: [2, 6], b: [3, 6]}, + {a: [2, 7], b: [3, 7]}, + {a: [3, 0], b: [3, 1]}, + {a: [3, 1], b: [3, 2]}, + {a: [3, 2], b: [4, 2]}, + {a: [3, 3], b: [3, 4]}, + {a: [3, 4], b: [4, 4]}, + {a: [3, 5], b: [3, 6]}, + {a: [3, 5], b: [4, 5]}, + {a: [3, 7], b: [4, 7]}, + {a: [4, 0], b: [4, 1]}, + {a: [4, 0], b: [5, 0]}, + {a: [4, 1], b: [5, 1]}, + {a: [4, 2], b: [4, 3]}, + {a: [4, 3], b: [5, 3]}, + {a: [4, 4], b: [4, 5]}, + {a: [4, 6], b: [4, 7]}, + {a: [4, 6], b: [5, 6]}, + {a: [5, 0], b: [6, 0]}, + {a: [5, 1], b: [5, 2]}, + {a: [5, 2], b: [6, 2]}, + {a: [5, 3], b: [5, 4]}, + {a: [5, 4], b: [6, 4]}, + {a: [5, 5], b: [5, 6]}, + {a: [5, 5], b: [6, 5]}, + {a: [6, 0], b: [6, 1]}, + {a: [6, 1], b: [7, 1]}, + {a: [6, 2], b: [6, 3]}, + {a: [6, 3], b: [6, 4]}, + {a: [6, 5], b: [6, 6]}, + {a: [6, 6], b: [6, 7]}, + {a: [6, 7], b: [7, 7]}, + {a: [7, 1], b: [7, 2]}, + {a: [7, 2], b: [7, 3]}, + {a: [7, 3], b: [7, 4]}, + {a: [7, 4], b: [7, 5]}, + {a: [7, 5], b: [7, 6]}, + {a: [7, 6], b: [7, 7]}, + ], + }, +]; diff --git a/src/index.css b/src/index.css index ec2585e..8684abe 100644 --- a/src/index.css +++ b/src/index.css @@ -11,3 +11,9 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } + +body, html, #root { + height: 100%; + width: 100%; + box-sizing: border-box; +} diff --git a/src/index.js b/src/index.js index ef2edf8..26419cd 100644 --- a/src/index.js +++ b/src/index.js @@ -1,14 +1,14 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './index.css'; -import App from './App'; -import reportWebVitals from './reportWebVitals'; +import * as React from "react"; +import ReactDOM from "react-dom"; +import "./index.css"; +import App from "./App"; +import reportWebVitals from "./reportWebVitals"; ReactDOM.render( , - document.getElementById('root') + document.getElementById("root") ); // If you want to start measuring performance in your app, pass a function diff --git a/src/reportWebVitals.js b/src/reportWebVitals.js index 5253d3a..81d1046 100644 --- a/src/reportWebVitals.js +++ b/src/reportWebVitals.js @@ -1,6 +1,6 @@ -const reportWebVitals = onPerfEntry => { +const reportWebVitals = (onPerfEntry) => { if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { + import("web-vitals").then(({getCLS, getFID, getFCP, getLCP, getTTFB}) => { getCLS(onPerfEntry); getFID(onPerfEntry); getFCP(onPerfEntry); diff --git a/src/setupTests.js b/src/setupTests.js index 8f2609b..1dd407a 100644 --- a/src/setupTests.js +++ b/src/setupTests.js @@ -2,4 +2,4 @@ // allows you to do things like: // expect(element).toHaveTextContent(/react/i) // learn more: https://github.com/testing-library/jest-dom -import '@testing-library/jest-dom'; +import "@testing-library/jest-dom"; diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 0000000..d4c44d1 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,105 @@ +import {EDGE_STATE} from "./constants"; + +// to get multiple styles in space-seperated format +export const multiStyles = (styles, classNames) => + classNames + .reduce( + (classNames, className) => [ + ...classNames, + ...(!className ? [] : [styles?.[className] || className]), + ], + [] + ) + .join(" "); + +// to check if number is valid number +export const isValid = (n, lim) => n >= 0 && n <= lim; + +// to check all elements are valid numbers +export const areValid = (n, lim) => n.every((n) => isValid(n, lim)); + +// to check if a node has an active edge +export const isNodeActive = (n) => + Array.isArray(n?.neigh) && + n.neigh.some((neighbor) => neighbor.state === EDGE_STATE.ACTIVE); + +// to get neighbors from matrix +export const getNeighbors = ([ax, ay], mat) => { + return mat[ax][ay]?.neigh || []; +}; + +// to check if two nodes are neighbor in a matrix +export const findNeighbor = ([ax, ay], [bx, by], mat) => { + const index = mat?.[ax]?.[ay]?.neigh.findIndex( + ({loc}) => loc[0] === bx && loc[1] === by + ); + return isNaN(index) ? -1 : index; +}; + +// get an array of all numbers present in the matrix +export const getNumbers = (mat) => { + let numbers = []; + let nRow = mat?.length; + let nCol = mat?.[0]?.length; + if (!nRow || !nCol) { + return; + } + + mat.forEach((row, r) => { + if (r >= nRow - 1) return; + row.forEach((node, c) => { + if (c >= nCol - 1) return; + numbers = [ + ...numbers, + ...(node.n > -1 ? [{r: r + 1, c: c + 1, n: node.n}] : []), + ]; + }); + }); + return numbers; +}; + +// get an array of all edges present in the matrix +export const getEdges = (mat) => { + let edges = []; + mat.forEach((row, sX) => { + row.forEach((node, sY) => { + let horEdges = []; + let verEdges = []; + node.neigh.forEach((edge) => { + const [eX, eY] = edge.loc; + if (eX > sX && eY === sY) { + verEdges = [ + ...verEdges, + { + a: [sX, sY], + b: [eX, eY], + ...(edge?.state === EDGE_STATE.NOT_ALLOWED + ? {notAllowed: true} + : {}), + ...(edge?.state === EDGE_STATE.HOVERED ? {hovered: true} : {}), + }, + ]; + } else if (eY > sY && eX === sX) { + horEdges = [ + ...horEdges, + { + a: [sX, sY], + b: [eX, eY], + ...(edge?.state === EDGE_STATE.NOT_ALLOWED + ? {notAllowed: true} + : {}), + ...(edge?.state === EDGE_STATE.HOVERED ? {hovered: true} : {}), + }, + ]; + } + }); + edges = [...edges, ...horEdges, ...verEdges]; + }); + }); + return edges; +}; + +// get an array of all active edges present in the matrix +export const getActiveEdges = (mat) => { + return getEdges(mat).filter((edge) => !edge?.notAllowed && !edge?.hovered); +};