Move to Typescript. #4
					 16 changed files with 482 additions and 119 deletions
				
			
		
							
								
								
									
										344
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										344
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							|  | @ -8,8 +8,13 @@ | |||
|       "name": "a-maze-r", | ||||
|       "version": "0.0.0", | ||||
|       "dependencies": { | ||||
|         "@types/jest": "^29.5.0", | ||||
|         "@types/node": "^18.15.11", | ||||
|         "@types/react": "^18.0.35", | ||||
|         "@types/react-dom": "^18.0.11", | ||||
|         "react": "^18.0.0", | ||||
|         "react-dom": "^18.0.0" | ||||
|         "react-dom": "^18.0.0", | ||||
|         "typescript": "^5.0.4" | ||||
|       }, | ||||
|       "devDependencies": { | ||||
|         "react-scripts": "^5.0.1" | ||||
|  | @ -32,7 +37,6 @@ | |||
|       "version": "7.21.4", | ||||
|       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz", | ||||
|       "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "@babel/highlight": "^7.18.6" | ||||
|       }, | ||||
|  | @ -414,7 +418,6 @@ | |||
|       "version": "7.19.1", | ||||
|       "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", | ||||
|       "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=6.9.0" | ||||
|       } | ||||
|  | @ -461,7 +464,6 @@ | |||
|       "version": "7.18.6", | ||||
|       "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", | ||||
|       "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "@babel/helper-validator-identifier": "^7.18.6", | ||||
|         "chalk": "^2.0.0", | ||||
|  | @ -2642,6 +2644,25 @@ | |||
|         "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@jest/expect-utils": { | ||||
|       "version": "29.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.5.0.tgz", | ||||
|       "integrity": "sha512-fmKzsidoXQT2KwnrwE0SQq3uj8Z763vzR8LnLBwC2qYWEFpjX8daRsk6rHUM1QvNlEW/UJXNXm59ztmJJWs2Mg==", | ||||
|       "dependencies": { | ||||
|         "jest-get-type": "^29.4.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@jest/expect-utils/node_modules/jest-get-type": { | ||||
|       "version": "29.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", | ||||
|       "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@jest/fake-timers": { | ||||
|       "version": "27.5.1", | ||||
|       "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz", | ||||
|  | @ -3753,14 +3774,12 @@ | |||
|     "node_modules/@types/istanbul-lib-coverage": { | ||||
|       "version": "2.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", | ||||
|       "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==" | ||||
|     }, | ||||
|     "node_modules/@types/istanbul-lib-report": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", | ||||
|       "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "@types/istanbul-lib-coverage": "*" | ||||
|       } | ||||
|  | @ -3769,11 +3788,246 @@ | |||
|       "version": "3.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", | ||||
|       "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "@types/istanbul-lib-report": "*" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest": { | ||||
|       "version": "29.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.0.tgz", | ||||
|       "integrity": "sha512-3Emr5VOl/aoBwnWcH/EFQvlSAmjV+XtV9GGu5mwdYew5vhQh0IUZx/60x0TzHDu09Bi7HMx10t/namdJw5QIcg==", | ||||
|       "dependencies": { | ||||
|         "expect": "^29.0.0", | ||||
|         "pretty-format": "^29.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/@jest/schemas": { | ||||
|       "version": "29.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.4.3.tgz", | ||||
|       "integrity": "sha512-VLYKXQmtmuEz6IxJsrZwzG9NvtkQsWNnWMsKxqWNu3+CnfzJQhp0WDDKWLVV9hLKr0l3SLLFRqcYHjhtyuDVxg==", | ||||
|       "dependencies": { | ||||
|         "@sinclair/typebox": "^0.25.16" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/@jest/types": { | ||||
|       "version": "29.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.5.0.tgz", | ||||
|       "integrity": "sha512-qbu7kN6czmVRc3xWFQcAN03RAUamgppVUdXrvl1Wr3jlNF93o9mJbGcDWrwGB6ht44u7efB1qCFgVQmca24Uog==", | ||||
|       "dependencies": { | ||||
|         "@jest/schemas": "^29.4.3", | ||||
|         "@types/istanbul-lib-coverage": "^2.0.0", | ||||
|         "@types/istanbul-reports": "^3.0.0", | ||||
|         "@types/node": "*", | ||||
|         "@types/yargs": "^17.0.8", | ||||
|         "chalk": "^4.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/@sinclair/typebox": { | ||||
|       "version": "0.25.24", | ||||
|       "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz", | ||||
|       "integrity": "sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ==" | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/@types/yargs": { | ||||
|       "version": "17.0.24", | ||||
|       "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", | ||||
|       "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", | ||||
|       "dependencies": { | ||||
|         "@types/yargs-parser": "*" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/ansi-styles": { | ||||
|       "version": "4.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", | ||||
|       "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", | ||||
|       "dependencies": { | ||||
|         "color-convert": "^2.0.1" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/ansi-styles?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/chalk": { | ||||
|       "version": "4.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", | ||||
|       "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", | ||||
|       "dependencies": { | ||||
|         "ansi-styles": "^4.1.0", | ||||
|         "supports-color": "^7.1.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/chalk?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/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==", | ||||
|       "dependencies": { | ||||
|         "color-name": "~1.1.4" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=7.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/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==" | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/diff-sequences": { | ||||
|       "version": "29.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", | ||||
|       "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/expect": { | ||||
|       "version": "29.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/expect/-/expect-29.5.0.tgz", | ||||
|       "integrity": "sha512-yM7xqUrCO2JdpFo4XpM82t+PJBFybdqoQuJLDGeDX2ij8NZzqRHyu3Hp188/JX7SWqud+7t4MUdvcgGBICMHZg==", | ||||
|       "dependencies": { | ||||
|         "@jest/expect-utils": "^29.5.0", | ||||
|         "jest-get-type": "^29.4.3", | ||||
|         "jest-matcher-utils": "^29.5.0", | ||||
|         "jest-message-util": "^29.5.0", | ||||
|         "jest-util": "^29.5.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/has-flag": { | ||||
|       "version": "4.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", | ||||
|       "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/jest-diff": { | ||||
|       "version": "29.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz", | ||||
|       "integrity": "sha512-LtxijLLZBduXnHSniy0WMdaHjmQnt3g5sa16W4p0HqukYTTsyTW3GD1q41TyGl5YFXj/5B2U6dlh5FM1LIMgxw==", | ||||
|       "dependencies": { | ||||
|         "chalk": "^4.0.0", | ||||
|         "diff-sequences": "^29.4.3", | ||||
|         "jest-get-type": "^29.4.3", | ||||
|         "pretty-format": "^29.5.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/jest-get-type": { | ||||
|       "version": "29.4.3", | ||||
|       "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz", | ||||
|       "integrity": "sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg==", | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/jest-matcher-utils": { | ||||
|       "version": "29.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.5.0.tgz", | ||||
|       "integrity": "sha512-lecRtgm/rjIK0CQ7LPQwzCs2VwW6WAahA55YBuI+xqmhm7LAaxokSB8C97yJeYyT+HvQkH741StzpU41wohhWw==", | ||||
|       "dependencies": { | ||||
|         "chalk": "^4.0.0", | ||||
|         "jest-diff": "^29.5.0", | ||||
|         "jest-get-type": "^29.4.3", | ||||
|         "pretty-format": "^29.5.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/jest-message-util": { | ||||
|       "version": "29.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.5.0.tgz", | ||||
|       "integrity": "sha512-Kijeg9Dag6CKtIDA7O21zNTACqD5MD/8HfIV8pdD94vFyFuer52SigdC3IQMhab3vACxXMiFk+yMHNdbqtyTGA==", | ||||
|       "dependencies": { | ||||
|         "@babel/code-frame": "^7.12.13", | ||||
|         "@jest/types": "^29.5.0", | ||||
|         "@types/stack-utils": "^2.0.0", | ||||
|         "chalk": "^4.0.0", | ||||
|         "graceful-fs": "^4.2.9", | ||||
|         "micromatch": "^4.0.4", | ||||
|         "pretty-format": "^29.5.0", | ||||
|         "slash": "^3.0.0", | ||||
|         "stack-utils": "^2.0.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/jest-util": { | ||||
|       "version": "29.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.5.0.tgz", | ||||
|       "integrity": "sha512-RYMgG/MTadOr5t8KdhejfvUU82MxsCu5MF6KuDUHl+NuwzUt+Sm6jJWxTJVrDR1j5M/gJVCPKQEpWXY+yIQ6lQ==", | ||||
|       "dependencies": { | ||||
|         "@jest/types": "^29.5.0", | ||||
|         "@types/node": "*", | ||||
|         "chalk": "^4.0.0", | ||||
|         "ci-info": "^3.2.0", | ||||
|         "graceful-fs": "^4.2.9", | ||||
|         "picomatch": "^2.2.3" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/pretty-format": { | ||||
|       "version": "29.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.5.0.tgz", | ||||
|       "integrity": "sha512-V2mGkI31qdttvTFX7Mt4efOqHXqJWMu4/r66Xh3Z3BwZaPfPJgp6/gbwoujRpPUtfEF6AUUWx3Jim3GCw5g/Qw==", | ||||
|       "dependencies": { | ||||
|         "@jest/schemas": "^29.4.3", | ||||
|         "ansi-styles": "^5.0.0", | ||||
|         "react-is": "^18.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": "^14.15.0 || ^16.10.0 || >=18.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/pretty-format/node_modules/ansi-styles": { | ||||
|       "version": "5.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", | ||||
|       "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", | ||||
|       "engines": { | ||||
|         "node": ">=10" | ||||
|       }, | ||||
|       "funding": { | ||||
|         "url": "https://github.com/chalk/ansi-styles?sponsor=1" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/react-is": { | ||||
|       "version": "18.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", | ||||
|       "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" | ||||
|     }, | ||||
|     "node_modules/@types/jest/node_modules/supports-color": { | ||||
|       "version": "7.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", | ||||
|       "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", | ||||
|       "dependencies": { | ||||
|         "has-flag": "^4.0.0" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/json-schema": { | ||||
|       "version": "7.0.11", | ||||
|       "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", | ||||
|  | @ -3795,8 +4049,7 @@ | |||
|     "node_modules/@types/node": { | ||||
|       "version": "18.15.11", | ||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", | ||||
|       "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==" | ||||
|     }, | ||||
|     "node_modules/@types/parse-json": { | ||||
|       "version": "4.0.0", | ||||
|  | @ -3810,6 +4063,11 @@ | |||
|       "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/@types/prop-types": { | ||||
|       "version": "15.7.5", | ||||
|       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", | ||||
|       "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" | ||||
|     }, | ||||
|     "node_modules/@types/q": { | ||||
|       "version": "1.5.5", | ||||
|       "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.5.tgz", | ||||
|  | @ -3828,6 +4086,24 @@ | |||
|       "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/@types/react": { | ||||
|       "version": "18.0.35", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.35.tgz", | ||||
|       "integrity": "sha512-6Laome31HpetaIUGFWl1VQ3mdSImwxtFZ39rh059a1MNnKGqBpC88J6NJ8n/Is3Qx7CefDGLgf/KhN/sYCf7ag==", | ||||
|       "dependencies": { | ||||
|         "@types/prop-types": "*", | ||||
|         "@types/scheduler": "*", | ||||
|         "csstype": "^3.0.2" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/react-dom": { | ||||
|       "version": "18.0.11", | ||||
|       "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.11.tgz", | ||||
|       "integrity": "sha512-O38bPbI2CWtgw/OoQoY+BRelw7uysmXbWvw3nLWO21H1HSh+GOlqPuXshJfjmpNlKiiSDG9cc1JZAaMmVdcTlw==", | ||||
|       "dependencies": { | ||||
|         "@types/react": "*" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/@types/resolve": { | ||||
|       "version": "1.17.1", | ||||
|       "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", | ||||
|  | @ -3843,6 +4119,11 @@ | |||
|       "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/@types/scheduler": { | ||||
|       "version": "0.16.3", | ||||
|       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", | ||||
|       "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" | ||||
|     }, | ||||
|     "node_modules/@types/semver": { | ||||
|       "version": "7.3.13", | ||||
|       "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", | ||||
|  | @ -3880,8 +4161,7 @@ | |||
|     "node_modules/@types/stack-utils": { | ||||
|       "version": "2.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", | ||||
|       "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==" | ||||
|     }, | ||||
|     "node_modules/@types/trusted-types": { | ||||
|       "version": "2.0.3", | ||||
|  | @ -3910,8 +4190,7 @@ | |||
|     "node_modules/@types/yargs-parser": { | ||||
|       "version": "21.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", | ||||
|       "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==" | ||||
|     }, | ||||
|     "node_modules/@typescript-eslint/eslint-plugin": { | ||||
|       "version": "5.58.0", | ||||
|  | @ -4617,7 +4896,6 @@ | |||
|       "version": "3.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", | ||||
|       "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "color-convert": "^1.9.0" | ||||
|       }, | ||||
|  | @ -5314,7 +5592,6 @@ | |||
|       "version": "3.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", | ||||
|       "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "fill-range": "^7.0.1" | ||||
|       }, | ||||
|  | @ -5490,7 +5767,6 @@ | |||
|       "version": "2.4.2", | ||||
|       "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", | ||||
|       "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "ansi-styles": "^3.2.1", | ||||
|         "escape-string-regexp": "^1.0.5", | ||||
|  | @ -5567,7 +5843,6 @@ | |||
|       "version": "3.8.0", | ||||
|       "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", | ||||
|       "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", | ||||
|       "dev": true, | ||||
|       "funding": [ | ||||
|         { | ||||
|           "type": "github", | ||||
|  | @ -5650,7 +5925,6 @@ | |||
|       "version": "1.9.3", | ||||
|       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", | ||||
|       "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "color-name": "1.1.3" | ||||
|       } | ||||
|  | @ -5658,8 +5932,7 @@ | |||
|     "node_modules/color-name": { | ||||
|       "version": "1.1.3", | ||||
|       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", | ||||
|       "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" | ||||
|     }, | ||||
|     "node_modules/colord": { | ||||
|       "version": "2.9.3", | ||||
|  | @ -6349,6 +6622,11 @@ | |||
|       "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "node_modules/csstype": { | ||||
|       "version": "3.1.2", | ||||
|       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", | ||||
|       "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" | ||||
|     }, | ||||
|     "node_modules/damerau-levenshtein": { | ||||
|       "version": "1.0.8", | ||||
|       "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", | ||||
|  | @ -6974,7 +7252,6 @@ | |||
|       "version": "1.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", | ||||
|       "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=0.8.0" | ||||
|       } | ||||
|  | @ -8034,7 +8311,6 @@ | |||
|       "version": "7.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", | ||||
|       "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "to-regex-range": "^5.0.1" | ||||
|       }, | ||||
|  | @ -8684,8 +8960,7 @@ | |||
|     "node_modules/graceful-fs": { | ||||
|       "version": "4.2.11", | ||||
|       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", | ||||
|       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", | ||||
|       "dev": true | ||||
|       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" | ||||
|     }, | ||||
|     "node_modules/grapheme-splitter": { | ||||
|       "version": "1.0.4", | ||||
|  | @ -8745,7 +9020,6 @@ | |||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", | ||||
|       "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=4" | ||||
|       } | ||||
|  | @ -9415,7 +9689,6 @@ | |||
|       "version": "7.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", | ||||
|       "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=0.12.0" | ||||
|       } | ||||
|  | @ -12345,7 +12618,6 @@ | |||
|       "version": "4.0.5", | ||||
|       "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", | ||||
|       "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "braces": "^3.0.2", | ||||
|         "picomatch": "^2.3.1" | ||||
|  | @ -13088,7 +13360,6 @@ | |||
|       "version": "2.3.1", | ||||
|       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", | ||||
|       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=8.6" | ||||
|       }, | ||||
|  | @ -15879,7 +16150,6 @@ | |||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", | ||||
|       "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|  | @ -16013,7 +16283,6 @@ | |||
|       "version": "2.0.6", | ||||
|       "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", | ||||
|       "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "escape-string-regexp": "^2.0.0" | ||||
|       }, | ||||
|  | @ -16025,7 +16294,6 @@ | |||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", | ||||
|       "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", | ||||
|       "dev": true, | ||||
|       "engines": { | ||||
|         "node": ">=8" | ||||
|       } | ||||
|  | @ -16321,7 +16589,6 @@ | |||
|       "version": "5.5.0", | ||||
|       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", | ||||
|       "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "has-flag": "^3.0.0" | ||||
|       }, | ||||
|  | @ -16716,7 +16983,6 @@ | |||
|       "version": "5.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", | ||||
|       "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", | ||||
|       "dev": true, | ||||
|       "dependencies": { | ||||
|         "is-number": "^7.0.0" | ||||
|       }, | ||||
|  | @ -16911,17 +17177,15 @@ | |||
|       } | ||||
|     }, | ||||
|     "node_modules/typescript": { | ||||
|       "version": "4.9.5", | ||||
|       "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", | ||||
|       "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", | ||||
|       "dev": true, | ||||
|       "peer": true, | ||||
|       "version": "5.0.4", | ||||
|       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", | ||||
|       "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", | ||||
|       "bin": { | ||||
|         "tsc": "bin/tsc", | ||||
|         "tsserver": "bin/tsserver" | ||||
|       }, | ||||
|       "engines": { | ||||
|         "node": ">=4.2.0" | ||||
|         "node": ">=12.20" | ||||
|       } | ||||
|     }, | ||||
|     "node_modules/unbox-primitive": { | ||||
|  |  | |||
|  | @ -1,7 +1,12 @@ | |||
| { | ||||
|   "dependencies": { | ||||
|     "@types/jest": "^29.5.0", | ||||
|     "@types/node": "^18.15.11", | ||||
|     "@types/react": "^18.0.35", | ||||
|     "@types/react-dom": "^18.0.11", | ||||
|     "react": "^18.0.0", | ||||
|     "react-dom": "^18.0.0" | ||||
|     "react-dom": "^18.0.0", | ||||
|     "typescript": "^5.0.4" | ||||
|   }, | ||||
|   "main": "/index.js", | ||||
|   "homepage": ".", | ||||
|  |  | |||
|  | @ -1,18 +1,13 @@ | |||
| import React, {useReducer} from 'react'; | ||||
| import Maze from "./Maze"; | ||||
| import InputForm from "./InputForm"; | ||||
| import reduce from "./reducer"; | ||||
| import MessageBanner from "./MessageBanner"; | ||||
| import {useReducer} from 'react'; | ||||
| import Maze from "./Maze.tsx"; | ||||
| import InputForm from "./InputForm.tsx"; | ||||
| import reduce from "./state/reducer.ts"; | ||||
| import MessageBanner from "./MessageBanner.tsx"; | ||||
| import {INITIAL_STATE} from "./state/state.ts"; | ||||
| import {actionToggledShowSolution} from "./state/action.ts"; | ||||
| 
 | ||||
| export default function App() { | ||||
|     const [state, dispatch] = useReducer(reduce, { | ||||
|             maze: null, | ||||
|             loading: false, | ||||
|             errorMessage: null, | ||||
|             showSolution: false, | ||||
|             userPath: [] | ||||
|         }, | ||||
|         undefined); | ||||
|     const [state, dispatch] = useReducer(reduce, INITIAL_STATE); | ||||
|     const hasValidMaze = !!state.maze; | ||||
|     return ( | ||||
|         <> | ||||
|  | @ -25,10 +20,7 @@ export default function App() { | |||
|                     <h1>The Maze ({state.maze.width}x{state.maze.height}, ID: {state.maze.id})</h1> | ||||
|                     <input type={"checkbox"} | ||||
|                            onChange={(e) => { | ||||
|                                dispatch({ | ||||
|                                    type: 'toggled_show_solution', | ||||
|                                    value: e.target.checked | ||||
|                                }); | ||||
|                                dispatch(actionToggledShowSolution(e.target.checked)); | ||||
|                            }} | ||||
|                            id={"showSolution"}/><label htmlFor="showSolution">Show Solution</label> | ||||
|                     <Maze state={state} | ||||
|  | @ -1,11 +1,13 @@ | |||
| import React from 'react'; | ||||
| import {MazeCell} from "./model/Maze"; | ||||
| import Coordinates from "./model/Coordinates"; | ||||
| import {actionClickedCell} from "./state/action.ts"; | ||||
| 
 | ||||
| function isMarked(x, y, marked) { | ||||
| function isMarked(x: number, y: number, marked: Coordinates[]): boolean { | ||||
|     return !!marked.find(e => e.x === x && e.y === y); | ||||
| } | ||||
| 
 | ||||
| export default function Cell({x, y, state, dispatch}) { | ||||
|     const cell = state.maze.grid[y][x]; | ||||
|     const cell: MazeCell = state.maze.grid[y][x]; | ||||
|     let classes = "cell r" + y + " c" + x; | ||||
|     if (cell.top) classes += " top"; | ||||
|     if (cell.right) classes += " right"; | ||||
|  | @ -19,19 +21,11 @@ export default function Cell({x, y, state, dispatch}) { | |||
|              onMouseEnter={(e) => { | ||||
|                  const leftPressed = e.buttons & 0x1; | ||||
|                  if (leftPressed) { | ||||
|                      dispatch({ | ||||
|                          type: 'clicked_cell', | ||||
|                          x, | ||||
|                          y | ||||
|                      }); | ||||
|                      dispatch(actionClickedCell(x, y)); | ||||
|                  } | ||||
|              }} | ||||
|              onClick={(e) => { | ||||
|                  dispatch({ | ||||
|                      type: 'clicked_cell', | ||||
|                      x, | ||||
|                      y | ||||
|                  }); | ||||
|                  dispatch(actionClickedCell(x, y)); | ||||
|              }}> | ||||
|         </div> | ||||
|     ); | ||||
|  | @ -1,32 +1,25 @@ | |||
| import React, {useState} from 'react'; | ||||
| import ValidatingInputNumberField from "./ValidatingInputNumberField"; | ||||
| import {useState} from 'react'; | ||||
| import ValidatingInputNumberField from "./ValidatingInputNumberField.tsx"; | ||||
| import {actionLoadedMaze, actionLoadingFailed, actionStartedLoading} from "./state/action.ts"; | ||||
| 
 | ||||
| export default function InputForm({state, dispatch}) { | ||||
|     const [width, setWidth] = useState(10); | ||||
|     const [height, setHeight] = useState(10); | ||||
|     const [id, setId] = useState(null); | ||||
|     const [id, setId] = useState(null as number); | ||||
| 
 | ||||
|     const handleSubmit = (e) => { | ||||
|         e.preventDefault(); | ||||
|         dispatch({ | ||||
|             type: 'started_loading' | ||||
|         }); | ||||
|         dispatch(actionStartedLoading()); | ||||
|         const url = `https://manuel.friedli.info/labyrinth/create/json?w=${width}&h=${height}&id=${id || ''}`; | ||||
|         fetch(url) | ||||
|             .then(response => response.json()) | ||||
|             // .then(result => new Promise(resolve => setTimeout(resolve, 600, result)))
 | ||||
|             .then(result => { | ||||
|                 dispatch({ | ||||
|                     type: 'loaded_maze', | ||||
|                     maze: result | ||||
|                 }); | ||||
|                 dispatch(actionLoadedMaze(result)); | ||||
|             }) | ||||
|             .catch(reason => { | ||||
|                 console.error("Failed to fetch maze data.", reason); | ||||
|                 dispatch({ | ||||
|                     type: 'loading_failed', | ||||
|                     reason | ||||
|                 }); | ||||
|                 dispatch(actionLoadingFailed(reason)); | ||||
|             }); | ||||
|     }; | ||||
|     const validateWidthHeightInput = value => { | ||||
|  | @ -1,5 +1,4 @@ | |||
| import React from 'react'; | ||||
| import Cell from "./Cell"; | ||||
| import Cell from "./Cell.tsx"; | ||||
| 
 | ||||
| 
 | ||||
| export default function Maze({state, dispatch}) { | ||||
|  | @ -7,9 +6,9 @@ export default function Maze({state, dispatch}) { | |||
|         return <div>No valid maze.</div> | ||||
|     } | ||||
| 
 | ||||
|     let maze = []; | ||||
|     let maze: JSX.Element[] = []; | ||||
|     for (let y = 0; y < state.maze.height; y++) { | ||||
|         let row = []; | ||||
|         let row: JSX.Element[] = []; | ||||
|         for (let x = 0; x < state.maze.width; x++) { | ||||
|             row.push(<Cell key={`${x}x${y}`} x={x} y={y} state={state} dispatch={dispatch}/>) | ||||
|         } | ||||
|  | @ -1,10 +1,8 @@ | |||
| import React from "react"; | ||||
| import {actionClosedMessageBanner} from "./state/action.ts"; | ||||
| 
 | ||||
| export default function MessageBanner({state, dispatch}) { | ||||
|     function handleClose() { | ||||
|         dispatch({ | ||||
|             type: 'closed_message_banner' | ||||
|         }) | ||||
|         dispatch(actionClosedMessageBanner()); | ||||
|     } | ||||
| 
 | ||||
|     if (!!state.errorMessage) { | ||||
							
								
								
									
										12
									
								
								src/index.js
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								src/index.js
									
										
									
									
									
								
							|  | @ -1,12 +0,0 @@ | |||
| import React, { StrictMode } from "react"; | ||||
| import { createRoot } from "react-dom/client"; | ||||
| import "./styles.css"; | ||||
| 
 | ||||
| import App from "./App"; | ||||
| 
 | ||||
| const root = createRoot(document.getElementById("root")); | ||||
| root.render( | ||||
|   <StrictMode> | ||||
|     <App /> | ||||
|   </StrictMode> | ||||
| ); | ||||
							
								
								
									
										12
									
								
								src/index.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/index.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,12 @@ | |||
| import {StrictMode} from "react"; | ||||
| import {createRoot, Root} from "react-dom/client"; | ||||
| import "./styles.css"; | ||||
| import App from "./App.tsx"; | ||||
| 
 | ||||
| 
 | ||||
| const root: Root = createRoot(document.getElementById("root")); | ||||
| root.render( | ||||
|     <StrictMode> | ||||
|         <App/> | ||||
|     </StrictMode> | ||||
| ); | ||||
							
								
								
									
										4
									
								
								src/model/Coordinates.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/model/Coordinates.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | |||
| export default interface Coordinates { | ||||
|     x: number, | ||||
|     y: number | ||||
| } | ||||
							
								
								
									
										18
									
								
								src/model/Maze.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/model/Maze.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| import Coordinates from "./Coordinates"; | ||||
| 
 | ||||
| export default interface Maze { | ||||
|     id: string, | ||||
|     width: number, | ||||
|     height: number, | ||||
|     start: Coordinates, | ||||
|     end: Coordinates, | ||||
|     grid: MazeCell[][] | ||||
| } | ||||
| 
 | ||||
| export interface MazeCell { | ||||
|     top: boolean, | ||||
|     right: boolean, | ||||
|     bottom: boolean, | ||||
|     left: boolean, | ||||
|     solution: boolean | ||||
| } | ||||
							
								
								
									
										60
									
								
								src/state/action.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/state/action.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| import Maze from "../model/Maze"; | ||||
| 
 | ||||
| export interface Action { | ||||
|     type: string, | ||||
| 
 | ||||
|     [key: string]: any | ||||
| } | ||||
| 
 | ||||
| export const ID_ACTION_STARTED_LOADING = 'started_loading'; | ||||
| 
 | ||||
| export function actionStartedLoading(): Action { | ||||
|     return { | ||||
|         type: ID_ACTION_STARTED_LOADING | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const ID_ACTION_LOADED_MAZE = 'loaded_maze'; | ||||
| 
 | ||||
| export function actionLoadedMaze(maze: Maze): Action { | ||||
|     return { | ||||
|         type: ID_ACTION_LOADED_MAZE, | ||||
|         maze | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const ID_ACTION_LOADING_FAILED = 'loading_failed'; | ||||
| 
 | ||||
| export function actionLoadingFailed(reason: string): Action { | ||||
|     return { | ||||
|         type: ID_ACTION_LOADING_FAILED, | ||||
|         reason | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| export const ID_ACTION_TOGGLED_SHOW_SOLUTION = 'toggled_show_solution'; | ||||
| 
 | ||||
| export function actionToggledShowSolution(value: boolean): Action { | ||||
|     return { | ||||
|         type: ID_ACTION_TOGGLED_SHOW_SOLUTION, | ||||
|         value | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const ID_ACTION_CLOSED_MESSAGE_BANNER = 'closed_message_banner'; | ||||
| 
 | ||||
| export function actionClosedMessageBanner(): Action { | ||||
|     return { | ||||
|         type: ID_ACTION_CLOSED_MESSAGE_BANNER | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const ID_ACTION_CLICKED_CELL = 'clicked_cell'; | ||||
| 
 | ||||
| export function actionClickedCell(x: number, y: number): Action { | ||||
|     return { | ||||
|         type: ID_ACTION_CLICKED_CELL, | ||||
|         x, | ||||
|         y | ||||
|     } | ||||
| } | ||||
|  | @ -1,8 +1,18 @@ | |||
| import handleUserClicked from "./userpathhandler"; | ||||
| import handleUserClicked from "./userpathhandler.ts"; | ||||
| import {State} from "./state"; | ||||
| import { | ||||
|     Action, | ||||
|     ID_ACTION_CLICKED_CELL, | ||||
|     ID_ACTION_CLOSED_MESSAGE_BANNER, | ||||
|     ID_ACTION_LOADED_MAZE, | ||||
|     ID_ACTION_LOADING_FAILED, | ||||
|     ID_ACTION_STARTED_LOADING, | ||||
|     ID_ACTION_TOGGLED_SHOW_SOLUTION | ||||
| } from "./action.ts"; | ||||
| 
 | ||||
| export default function reduce(state, action) { | ||||
| export default function reduce(state: State, action: Action) { | ||||
|     switch (action.type) { | ||||
|         case 'started_loading': { | ||||
|         case ID_ACTION_STARTED_LOADING: { | ||||
|             return { | ||||
|                 ...state, | ||||
|                 maze: null, | ||||
|  | @ -10,7 +20,7 @@ export default function reduce(state, action) { | |||
|                 errorMessage: null | ||||
|             } | ||||
|         } | ||||
|         case 'loaded_maze': { | ||||
|         case ID_ACTION_LOADED_MAZE: { | ||||
|             return { | ||||
|                 ...state, | ||||
|                 loading: false, | ||||
|  | @ -18,26 +28,26 @@ export default function reduce(state, action) { | |||
|                 userPath: [] | ||||
|             } | ||||
|         } | ||||
|         case 'loading_failed': { | ||||
|         case ID_ACTION_LOADING_FAILED: { | ||||
|             return { | ||||
|                 ...state, | ||||
|                 loading: false, | ||||
|                 errorMessage: `Failed to load maze. Reason: ${action.reason}` | ||||
|             } | ||||
|         } | ||||
|         case 'toggled_show_solution': { | ||||
|         case ID_ACTION_TOGGLED_SHOW_SOLUTION: { | ||||
|             return { | ||||
|                 ...state, | ||||
|                 showSolution: action.value | ||||
|             } | ||||
|         } | ||||
|         case 'closed_message_banner': { | ||||
|         case ID_ACTION_CLOSED_MESSAGE_BANNER: { | ||||
|             return { | ||||
|                 ...state, | ||||
|                 errorMessage: null | ||||
|             } | ||||
|         } | ||||
|         case 'clicked_cell': { | ||||
|         case ID_ACTION_CLICKED_CELL: { | ||||
|             // There's so much logic involved, externalize that into its own file.
 | ||||
|             return handleUserClicked(state, action.x, action.y); | ||||
|         } | ||||
							
								
								
									
										18
									
								
								src/state/state.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/state/state.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | |||
| import Coordinates from "../model/Coordinates"; | ||||
| import Maze from "../model/Maze"; | ||||
| 
 | ||||
| export interface State { | ||||
|     errorMessage: string | null, | ||||
|     loading: boolean, | ||||
|     maze: Maze | null, | ||||
|     showSolution: boolean, | ||||
|     userPath: Coordinates[] | ||||
| } | ||||
| 
 | ||||
| export const INITIAL_STATE: State = { | ||||
|     errorMessage: null, | ||||
|     loading: false, | ||||
|     maze: null, | ||||
|     showSolution: false, | ||||
|     userPath: [] | ||||
| }; | ||||
|  | @ -1,12 +1,19 @@ | |||
| export default function handleUserClicked(state, x, y) { | ||||
| import {State} from "./state"; | ||||
| import Coordinates from "../model/Coordinates"; | ||||
| import {MazeCell} from "../model/Maze"; | ||||
| 
 | ||||
| export default function handleUserClicked(state: State, x: number, y: number): State { | ||||
|     if (isClickAllowed(x, y, state)) { | ||||
|         // Okay, we clicked a cell that's adjacent to the end of the userpath (or which IS the end of the userpath)
 | ||||
|         // and that's not blocked by a wall. Now let's see.
 | ||||
|         if (-1 === state.userPath.findIndex(step => step.x === x && step.y === y)) { | ||||
|             // The clicked cell is not yet part of the userpath --> add it.
 | ||||
|             // If it's the end tile, also show a congratulation message
 | ||||
|             const showMessage = x === state.maze.end.x && y === state.maze.end.y; | ||||
|             return { | ||||
|                 ...state, | ||||
|                 userPath: [...state.userPath, {x: x, y: y}] | ||||
|                 userPath: [...state.userPath, {x, y}], | ||||
|                 errorMessage: showMessage ? "Congratulations! You won!" : state.errorMessage | ||||
|             }; | ||||
|         } else { | ||||
|             // The clicked cell IS part of the userpath. Is it the last cell of it?
 | ||||
|  | @ -15,7 +22,8 @@ export default function handleUserClicked(state, x, y) { | |||
|                 // Yes, it's the last cell of the userpath --> remove it.
 | ||||
|                 return { | ||||
|                     ...state, | ||||
|                     userPath: state.userPath.filter(step => step.x !== x || step.y !== y) | ||||
|                     userPath: state.userPath.filter(step => step.x !== x || step.y !== y), | ||||
|                     errorMessage: null | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -24,7 +32,7 @@ export default function handleUserClicked(state, x, y) { | |||
|     return state; | ||||
| } | ||||
| 
 | ||||
| function isClickAllowed(x, y, state) { | ||||
| function isClickAllowed(x: number, y: number, state: State): boolean { | ||||
|     const lastCoordsFromUserPath = getLastCoordsFromUserPath(state); | ||||
|     if (!lastCoordsFromUserPath) { | ||||
|         // when nothing has been marked yet, we can only toggle the starting position
 | ||||
|  | @ -61,11 +69,11 @@ function isClickAllowed(x, y, state) { | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| function getCellAt(coords, state) { | ||||
| function getCellAt(coords: Coordinates, state: State): MazeCell { | ||||
|     return state.maze.grid[coords.y][coords.x]; | ||||
| } | ||||
| 
 | ||||
| function getLastCoordsFromUserPath(state) { | ||||
| function getLastCoordsFromUserPath(state: State): Coordinates | null { | ||||
|     if (state.userPath.length > 0) { | ||||
|         return state.userPath[state.userPath.length - 1]; | ||||
|     } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue