imwithye commited on
Commit
1afe868
·
1 Parent(s): 20ba9a1

format codes

Browse files
.prettierrc DELETED
@@ -1 +0,0 @@
1
- {}
 
 
.prettierrc.js ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ module.exports = {
2
+ printWidth: 120,
3
+ tabWidth: 2,
4
+ trailingComma: 'all',
5
+ singleQuote: true,
6
+ semi: true,
7
+ importOrder: ['^@/(.*)$', '^[./]'],
8
+ importOrderSeparation: true,
9
+ importOrderSortSpecifiers: true,
10
+ plugins: ['@trivago/prettier-plugin-sort-imports'],
11
+ };
bun.lock CHANGED
@@ -14,6 +14,7 @@
14
  "devDependencies": {
15
  "@eslint/eslintrc": "^3",
16
  "@tailwindcss/postcss": "^4",
 
17
  "@types/node": "^20",
18
  "@types/react": "^19",
19
  "@types/react-dom": "^19",
@@ -28,8 +29,26 @@
28
  "packages": {
29
  "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
30
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="],
32
 
 
 
 
 
 
 
33
  "@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.12.0", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="],
34
 
35
  "@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
@@ -242,6 +261,8 @@
242
 
243
  "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.13", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.13", "@tailwindcss/oxide": "4.1.13", "postcss": "^8.4.41", "tailwindcss": "4.1.13" } }, "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ=="],
244
 
 
 
245
  "@tweenjs/tween.js": ["@tweenjs/tween.js@23.1.3", "", {}, "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA=="],
246
 
247
  "@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="],
@@ -666,12 +687,16 @@
666
 
667
  "its-fine": ["its-fine@2.0.0", "", { "dependencies": { "@types/react-reconciler": "^0.28.9" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng=="],
668
 
 
 
669
  "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
670
 
671
  "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
672
 
673
  "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
674
 
 
 
675
  "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
676
 
677
  "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
@@ -718,6 +743,8 @@
718
 
719
  "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
720
 
 
 
721
  "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
722
 
723
  "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
 
14
  "devDependencies": {
15
  "@eslint/eslintrc": "^3",
16
  "@tailwindcss/postcss": "^4",
17
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
18
  "@types/node": "^20",
19
  "@types/react": "^19",
20
  "@types/react-dom": "^19",
 
29
  "packages": {
30
  "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
31
 
32
+ "@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
33
+
34
+ "@babel/generator": ["@babel/generator@7.28.3", "", { "dependencies": { "@babel/parser": "^7.28.3", "@babel/types": "^7.28.2", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw=="],
35
+
36
+ "@babel/helper-globals": ["@babel/helper-globals@7.28.0", "", {}, "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw=="],
37
+
38
+ "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="],
39
+
40
+ "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="],
41
+
42
+ "@babel/parser": ["@babel/parser@7.28.4", "", { "dependencies": { "@babel/types": "^7.28.4" }, "bin": "./bin/babel-parser.js" }, "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg=="],
43
+
44
  "@babel/runtime": ["@babel/runtime@7.28.4", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="],
45
 
46
+ "@babel/template": ["@babel/template@7.27.2", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="],
47
+
48
+ "@babel/traverse": ["@babel/traverse@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/types": "^7.28.4", "debug": "^4.3.1" } }, "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ=="],
49
+
50
+ "@babel/types": ["@babel/types@7.28.4", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q=="],
51
+
52
  "@dimforge/rapier3d-compat": ["@dimforge/rapier3d-compat@0.12.0", "", {}, "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow=="],
53
 
54
  "@emnapi/core": ["@emnapi/core@1.5.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" } }, "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg=="],
 
261
 
262
  "@tailwindcss/postcss": ["@tailwindcss/postcss@4.1.13", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "@tailwindcss/node": "4.1.13", "@tailwindcss/oxide": "4.1.13", "postcss": "^8.4.41", "tailwindcss": "4.1.13" } }, "sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ=="],
263
 
264
+ "@trivago/prettier-plugin-sort-imports": ["@trivago/prettier-plugin-sort-imports@5.2.2", "", { "dependencies": { "@babel/generator": "^7.26.5", "@babel/parser": "^7.26.7", "@babel/traverse": "^7.26.7", "@babel/types": "^7.26.7", "javascript-natural-sort": "^0.7.1", "lodash": "^4.17.21" }, "peerDependencies": { "@vue/compiler-sfc": "3.x", "prettier": "2.x - 3.x", "prettier-plugin-svelte": "3.x", "svelte": "4.x || 5.x" }, "optionalPeers": ["@vue/compiler-sfc", "prettier-plugin-svelte", "svelte"] }, "sha512-fYDQA9e6yTNmA13TLVSA+WMQRc5Bn/c0EUBditUHNfMMxN7M82c38b1kEggVE3pLpZ0FwkwJkUEKMiOi52JXFA=="],
265
+
266
  "@tweenjs/tween.js": ["@tweenjs/tween.js@23.1.3", "", {}, "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA=="],
267
 
268
  "@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="],
 
687
 
688
  "its-fine": ["its-fine@2.0.0", "", { "dependencies": { "@types/react-reconciler": "^0.28.9" }, "peerDependencies": { "react": "^19.0.0" } }, "sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng=="],
689
 
690
+ "javascript-natural-sort": ["javascript-natural-sort@0.7.1", "", {}, "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw=="],
691
+
692
  "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="],
693
 
694
  "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
695
 
696
  "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="],
697
 
698
+ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="],
699
+
700
  "json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
701
 
702
  "json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
 
743
 
744
  "locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
745
 
746
+ "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
747
+
748
  "lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
749
 
750
  "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
eslint.config.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { dirname } from "path";
2
- import { fileURLToPath } from "url";
3
- import { FlatCompat } from "@eslint/eslintrc";
4
 
5
  const __filename = fileURLToPath(import.meta.url);
6
  const __dirname = dirname(__filename);
@@ -10,15 +10,9 @@ const compat = new FlatCompat({
10
  });
11
 
12
  const eslintConfig = [
13
- ...compat.extends("next/core-web-vitals", "next/typescript"),
14
  {
15
- ignores: [
16
- "node_modules/**",
17
- ".next/**",
18
- "out/**",
19
- "build/**",
20
- "next-env.d.ts",
21
- ],
22
  },
23
  ];
24
 
 
1
+ import { FlatCompat } from '@eslint/eslintrc';
2
+ import { dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
 
5
  const __filename = fileURLToPath(import.meta.url);
6
  const __dirname = dirname(__filename);
 
10
  });
11
 
12
  const eslintConfig = [
13
+ ...compat.extends('next/core-web-vitals', 'next/typescript'),
14
  {
15
+ ignores: ['node_modules/**', '.next/**', 'out/**', 'build/**', 'next-env.d.ts'],
 
 
 
 
 
 
16
  },
17
  ];
18
 
next.config.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { NextConfig } from "next";
2
 
3
  const nextConfig: NextConfig = {
4
  /* config options here */
 
1
+ import type { NextConfig } from 'next';
2
 
3
  const nextConfig: NextConfig = {
4
  /* config options here */
package.json CHANGED
@@ -20,6 +20,7 @@
20
  "devDependencies": {
21
  "@eslint/eslintrc": "^3",
22
  "@tailwindcss/postcss": "^4",
 
23
  "@types/node": "^20",
24
  "@types/react": "^19",
25
  "@types/react-dom": "^19",
 
20
  "devDependencies": {
21
  "@eslint/eslintrc": "^3",
22
  "@tailwindcss/postcss": "^4",
23
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
24
  "@types/node": "^20",
25
  "@types/react": "^19",
26
  "@types/react-dom": "^19",
postcss.config.mjs CHANGED
@@ -1,5 +1,5 @@
1
  const config = {
2
- plugins: ["@tailwindcss/postcss"],
3
  };
4
 
5
  export default config;
 
1
  const config = {
2
+ plugins: ['@tailwindcss/postcss'],
3
  };
4
 
5
  export default config;
public/file.svg DELETED
public/globe.svg DELETED
public/next.svg DELETED
public/vercel.svg DELETED
public/window.svg DELETED
src/app/globals.css CHANGED
@@ -1,4 +1,4 @@
1
- @import "tailwindcss";
2
 
3
  ::-webkit-scrollbar {
4
  display: none;
 
1
+ @import 'tailwindcss';
2
 
3
  ::-webkit-scrollbar {
4
  display: none;
src/app/layout.tsx CHANGED
@@ -1,10 +1,10 @@
1
- import type { Metadata } from "next";
2
- import "./globals.css";
 
3
 
4
  export const metadata: Metadata = {
5
  title: "Rubik's Cube Solver - with Reinforcement Learning",
6
- description:
7
- "A web application for solving Rubik's Cube using Reinforcement Learning techniques.",
8
  };
9
 
10
  export default function RootLayout({
 
1
+ import type { Metadata } from 'next';
2
+
3
+ import './globals.css';
4
 
5
  export const metadata: Metadata = {
6
  title: "Rubik's Cube Solver - with Reinforcement Learning",
7
+ description: "A web application for solving Rubik's Cube using Reinforcement Learning techniques.",
 
8
  };
9
 
10
  export default function RootLayout({
src/app/page.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import { Canvas } from "@/components/canvas";
2
 
3
  export default function Home() {
4
  return (
@@ -6,9 +6,7 @@ export default function Home() {
6
  <div className="absolute inset-0 z-10 pointer-events-none">
7
  <div className="w-full h-full p-6">
8
  <div className="text-6xl font-bold">Rubik&apos;s Cube Solver</div>
9
- <div className="text-2xl text-gray-800">
10
- with Reinforcement Learning
11
- </div>
12
  <div className="text-gray-700">
13
  <a href="https://cross-entropy.ai">https://cross-entropy.ai</a>
14
  </div>
 
1
+ import { Canvas } from '@/components/canvas';
2
 
3
  export default function Home() {
4
  return (
 
6
  <div className="absolute inset-0 z-10 pointer-events-none">
7
  <div className="w-full h-full p-6">
8
  <div className="text-6xl font-bold">Rubik&apos;s Cube Solver</div>
9
+ <div className="text-2xl text-gray-800">with Reinforcement Learning</div>
 
 
10
  <div className="text-gray-700">
11
  <a href="https://cross-entropy.ai">https://cross-entropy.ai</a>
12
  </div>
src/components/canvas.tsx CHANGED
@@ -1,34 +1,31 @@
1
- "use client";
2
 
3
- import { useControls, Leva, button } from "leva";
4
- import { Canvas as ThreeCanvas } from "@react-three/fiber";
 
 
5
 
6
- import { Env } from "../components/env";
7
- import { RubiksCube, RubiksCubeRef } from "./rubiks-cube";
8
- import { useRef, useState, useTransition } from "react";
9
- import { PresetsType } from "@react-three/drei/helpers/environment-assets";
10
- import { Actions } from "./consts";
11
 
12
  export const Canvas = () => {
13
  const rubiksCubeRef = useRef<RubiksCubeRef>(null);
14
 
15
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
  const [_, startTransition] = useTransition();
17
- const [background, setBackground] = useState<PresetsType>("sunset");
18
 
19
  const { cubeRoughness, cubeSpeed } = useControls({
20
  cubeRoughness: { value: 0.5, min: 0.2, max: 0.8 },
21
  cubeSpeed: { value: 2, min: 1, max: 10 },
22
  background: {
23
  value: background,
24
- options: ["sunset", "dawn", "forest"],
25
  onChange: (value) => startTransition(() => setBackground(value)),
26
  },
27
  Scramble: button(() => {
28
- const scrambleSteps = Array.from(
29
- { length: 20 },
30
- () => Actions[Math.floor(Math.random() * Actions.length)],
31
- );
32
  rubiksCubeRef.current?.rotate(scrambleSteps);
33
  }),
34
  });
@@ -37,11 +34,7 @@ export const Canvas = () => {
37
  <>
38
  <Leva />
39
  <ThreeCanvas shadows camera={{ position: [0, 0, 4.5], fov: 50 }}>
40
- <RubiksCube
41
- ref={rubiksCubeRef}
42
- cubeRoughness={cubeRoughness}
43
- cubeSpeed={cubeSpeed}
44
- />
45
  <Env background={background} />
46
  </ThreeCanvas>
47
  </>
 
1
+ 'use client';
2
 
3
+ import { PresetsType } from '@react-three/drei/helpers/environment-assets';
4
+ import { Canvas as ThreeCanvas } from '@react-three/fiber';
5
+ import { Leva, button, useControls } from 'leva';
6
+ import { useRef, useState, useTransition } from 'react';
7
 
8
+ import { Env } from '../components/env';
9
+ import { Actions } from './consts';
10
+ import { RubiksCube, RubiksCubeRef } from './rubiks-cube';
 
 
11
 
12
  export const Canvas = () => {
13
  const rubiksCubeRef = useRef<RubiksCubeRef>(null);
14
 
15
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
  const [_, startTransition] = useTransition();
17
+ const [background, setBackground] = useState<PresetsType>('sunset');
18
 
19
  const { cubeRoughness, cubeSpeed } = useControls({
20
  cubeRoughness: { value: 0.5, min: 0.2, max: 0.8 },
21
  cubeSpeed: { value: 2, min: 1, max: 10 },
22
  background: {
23
  value: background,
24
+ options: ['sunset', 'dawn', 'forest'],
25
  onChange: (value) => startTransition(() => setBackground(value)),
26
  },
27
  Scramble: button(() => {
28
+ const scrambleSteps = Array.from({ length: 20 }, () => Actions[Math.floor(Math.random() * Actions.length)]);
 
 
 
29
  rubiksCubeRef.current?.rotate(scrambleSteps);
30
  }),
31
  });
 
34
  <>
35
  <Leva />
36
  <ThreeCanvas shadows camera={{ position: [0, 0, 4.5], fov: 50 }}>
37
+ <RubiksCube ref={rubiksCubeRef} cubeRoughness={cubeRoughness} cubeSpeed={cubeSpeed} />
 
 
 
 
38
  <Env background={background} />
39
  </ThreeCanvas>
40
  </>
src/components/consts.tsx CHANGED
@@ -1,12 +1,6 @@
1
- export type FacingDirection =
2
- | "front"
3
- | "back"
4
- | "left"
5
- | "right"
6
- | "top"
7
- | "bottom";
8
 
9
- export type RotationDirection = "clockwise" | "counter-clockwise";
10
 
11
  export const Rotations: Record<FacingDirection, [number, number, number]> = {
12
  front: [0, 0, 0],
@@ -23,16 +17,16 @@ export type RotationStep = {
23
  };
24
 
25
  export const Actions: Array<RotationStep> = [
26
- { faceDirection: "front", direction: "clockwise" },
27
- { faceDirection: "front", direction: "counter-clockwise" },
28
- { faceDirection: "right", direction: "clockwise" },
29
- { faceDirection: "right", direction: "counter-clockwise" },
30
- { faceDirection: "left", direction: "clockwise" },
31
- { faceDirection: "left", direction: "counter-clockwise" },
32
- { faceDirection: "back", direction: "clockwise" },
33
- { faceDirection: "back", direction: "counter-clockwise" },
34
- { faceDirection: "top", direction: "clockwise" },
35
- { faceDirection: "top", direction: "counter-clockwise" },
36
- { faceDirection: "bottom", direction: "clockwise" },
37
- { faceDirection: "bottom", direction: "counter-clockwise" },
38
  ];
 
1
+ export type FacingDirection = 'front' | 'back' | 'left' | 'right' | 'top' | 'bottom';
 
 
 
 
 
 
2
 
3
+ export type RotationDirection = 'clockwise' | 'counter-clockwise';
4
 
5
  export const Rotations: Record<FacingDirection, [number, number, number]> = {
6
  front: [0, 0, 0],
 
17
  };
18
 
19
  export const Actions: Array<RotationStep> = [
20
+ { faceDirection: 'front', direction: 'clockwise' },
21
+ { faceDirection: 'front', direction: 'counter-clockwise' },
22
+ { faceDirection: 'right', direction: 'clockwise' },
23
+ { faceDirection: 'right', direction: 'counter-clockwise' },
24
+ { faceDirection: 'left', direction: 'clockwise' },
25
+ { faceDirection: 'left', direction: 'counter-clockwise' },
26
+ { faceDirection: 'back', direction: 'clockwise' },
27
+ { faceDirection: 'back', direction: 'counter-clockwise' },
28
+ { faceDirection: 'top', direction: 'clockwise' },
29
+ { faceDirection: 'top', direction: 'counter-clockwise' },
30
+ { faceDirection: 'bottom', direction: 'clockwise' },
31
+ { faceDirection: 'bottom', direction: 'counter-clockwise' },
32
  ];
src/components/cube-piece.tsx CHANGED
@@ -1,19 +1,20 @@
1
- "use client";
2
 
3
- import { RoundedBox } from "@react-three/drei";
4
- import { useEffect, useRef, useState } from "react";
5
- import { FacingDirection, Rotations } from "./consts";
6
- import { Mesh } from "three";
7
- import { rotationController } from "./rotation-controller";
 
8
 
9
  // Standard Rubik's cube colors
10
  const CUBE_COLORS = {
11
- front: "#ff0000", // Red
12
- back: "#ff00ff", // Purple
13
- left: "#00ff00", // Green
14
- right: "#0000ff", // Blue
15
- top: "#ffff00", // Yellow
16
- bottom: "#ffffff", // White
17
  };
18
 
19
  type CubePieceProps = {
@@ -52,28 +53,16 @@ export const CubePiece = ({ roughness, initialPosition }: CubePieceProps) => {
52
  return (
53
  <mesh position={position} ref={meshRef}>
54
  <RoundedBox args={[0.95, 0.95, 0.95]} radius={0.05} smoothness={4}>
55
- <meshStandardMaterial
56
- color="#2a2a2a"
57
- metalness={1}
58
- roughness={roughness}
59
- />
60
  </RoundedBox>
61
 
62
  {Object.entries(visibleFaces).map(([face, isVisible]) => {
63
  if (!isVisible) return null;
64
  const color = CUBE_COLORS[face as keyof typeof CUBE_COLORS];
65
  return (
66
- <mesh
67
- key={face}
68
- position={positions[face as FacingDirection]}
69
- rotation={Rotations[face as FacingDirection]}
70
- >
71
  <planeGeometry args={[0.8, 0.8]} />
72
- <meshStandardMaterial
73
- color={color}
74
- metalness={1}
75
- roughness={roughness}
76
- />
77
  </mesh>
78
  );
79
  })}
 
1
+ 'use client';
2
 
3
+ import { RoundedBox } from '@react-three/drei';
4
+ import { useEffect, useRef, useState } from 'react';
5
+ import { Mesh } from 'three';
6
+
7
+ import { FacingDirection, Rotations } from './consts';
8
+ import { rotationController } from './rotation-controller';
9
 
10
  // Standard Rubik's cube colors
11
  const CUBE_COLORS = {
12
+ front: '#ff0000', // Red
13
+ back: '#ff00ff', // Purple
14
+ left: '#00ff00', // Green
15
+ right: '#0000ff', // Blue
16
+ top: '#ffff00', // Yellow
17
+ bottom: '#ffffff', // White
18
  };
19
 
20
  type CubePieceProps = {
 
53
  return (
54
  <mesh position={position} ref={meshRef}>
55
  <RoundedBox args={[0.95, 0.95, 0.95]} radius={0.05} smoothness={4}>
56
+ <meshStandardMaterial color="#2a2a2a" metalness={1} roughness={roughness} />
 
 
 
 
57
  </RoundedBox>
58
 
59
  {Object.entries(visibleFaces).map(([face, isVisible]) => {
60
  if (!isVisible) return null;
61
  const color = CUBE_COLORS[face as keyof typeof CUBE_COLORS];
62
  return (
63
+ <mesh key={face} position={positions[face as FacingDirection]} rotation={Rotations[face as FacingDirection]}>
 
 
 
 
64
  <planeGeometry args={[0.8, 0.8]} />
65
+ <meshStandardMaterial color={color} metalness={1} roughness={roughness} />
 
 
 
 
66
  </mesh>
67
  );
68
  })}
src/components/env.tsx CHANGED
@@ -1,8 +1,9 @@
1
- "ue client";
 
 
 
 
2
 
3
- import { Environment, CameraControls } from "@react-three/drei";
4
- import { PresetsType } from "@react-three/drei/helpers/environment-assets";
5
- import { CameraControlsImpl } from "@react-three/drei";
6
  const { ACTION } = CameraControlsImpl;
7
 
8
  type EnvProps = {
 
1
+ 'ue client';
2
+
3
+ import { CameraControls, Environment } from '@react-three/drei';
4
+ import { CameraControlsImpl } from '@react-three/drei';
5
+ import { PresetsType } from '@react-three/drei/helpers/environment-assets';
6
 
 
 
 
7
  const { ACTION } = CameraControlsImpl;
8
 
9
  type EnvProps = {
src/components/rotation-controller.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { FacingDirection, RotationStep } from "./consts";
2
- import { Group, Mesh } from "three";
 
3
 
4
  export class RotationController {
5
  private static instance: RotationController;
@@ -21,31 +22,31 @@ export class RotationController {
21
 
22
  private rotate(step: RotationStep, group: Group, delta: number) {
23
  let sign = 0;
24
- let axis: "x" | "y" | "z" = "x";
25
  switch (step.faceDirection) {
26
- case "front":
27
- sign = step.direction === "clockwise" ? -1 : 1;
28
- axis = "z";
29
  break;
30
- case "back":
31
- sign = step.direction === "clockwise" ? 1 : -1;
32
- axis = "z";
33
  break;
34
- case "left":
35
- sign = step.direction === "clockwise" ? 1 : -1;
36
- axis = "x";
37
  break;
38
- case "right":
39
- sign = step.direction === "clockwise" ? -1 : 1;
40
- axis = "x";
41
  break;
42
- case "top":
43
- sign = step.direction === "clockwise" ? -1 : 1;
44
- axis = "y";
45
  break;
46
- case "bottom":
47
- sign = step.direction === "clockwise" ? 1 : -1;
48
- axis = "y";
49
  break;
50
  }
51
 
@@ -76,17 +77,17 @@ export class RotationController {
76
 
77
  getCubes(faceDirection: FacingDirection) {
78
  switch (faceDirection) {
79
- case "front":
80
  return this.cubes.filter((m) => m.position.z > 0);
81
- case "back":
82
  return this.cubes.filter((m) => m.position.z < 0);
83
- case "left":
84
  return this.cubes.filter((m) => m.position.x < 0);
85
- case "right":
86
  return this.cubes.filter((m) => m.position.x > 0);
87
- case "top":
88
  return this.cubes.filter((m) => m.position.y > 0);
89
- case "bottom":
90
  return this.cubes.filter((m) => m.position.y < 0);
91
  }
92
  }
@@ -123,3 +124,4 @@ export class RotationController {
123
  }
124
 
125
  export const rotationController = RotationController.getInstance();
 
 
1
+ import { Group, Mesh } from 'three';
2
+
3
+ import { FacingDirection, RotationStep } from './consts';
4
 
5
  export class RotationController {
6
  private static instance: RotationController;
 
22
 
23
  private rotate(step: RotationStep, group: Group, delta: number) {
24
  let sign = 0;
25
+ let axis: 'x' | 'y' | 'z' = 'x';
26
  switch (step.faceDirection) {
27
+ case 'front':
28
+ sign = step.direction === 'clockwise' ? -1 : 1;
29
+ axis = 'z';
30
  break;
31
+ case 'back':
32
+ sign = step.direction === 'clockwise' ? 1 : -1;
33
+ axis = 'z';
34
  break;
35
+ case 'left':
36
+ sign = step.direction === 'clockwise' ? 1 : -1;
37
+ axis = 'x';
38
  break;
39
+ case 'right':
40
+ sign = step.direction === 'clockwise' ? -1 : 1;
41
+ axis = 'x';
42
  break;
43
+ case 'top':
44
+ sign = step.direction === 'clockwise' ? -1 : 1;
45
+ axis = 'y';
46
  break;
47
+ case 'bottom':
48
+ sign = step.direction === 'clockwise' ? 1 : -1;
49
+ axis = 'y';
50
  break;
51
  }
52
 
 
77
 
78
  getCubes(faceDirection: FacingDirection) {
79
  switch (faceDirection) {
80
+ case 'front':
81
  return this.cubes.filter((m) => m.position.z > 0);
82
+ case 'back':
83
  return this.cubes.filter((m) => m.position.z < 0);
84
+ case 'left':
85
  return this.cubes.filter((m) => m.position.x < 0);
86
+ case 'right':
87
  return this.cubes.filter((m) => m.position.x > 0);
88
+ case 'top':
89
  return this.cubes.filter((m) => m.position.y > 0);
90
+ case 'bottom':
91
  return this.cubes.filter((m) => m.position.y < 0);
92
  }
93
  }
 
124
  }
125
 
126
  export const rotationController = RotationController.getInstance();
127
+ export const frameCallback = rotationController.frameCallback.bind(rotationController);
src/components/rotation-panel.tsx CHANGED
@@ -1,14 +1,10 @@
1
- "use client";
2
 
3
- import { useLoader } from "@react-three/fiber";
4
- import { TextureLoader } from "three";
5
- import {
6
- Rotations,
7
- FacingDirection,
8
- RotationStep,
9
- RotationDirection,
10
- } from "./consts";
11
- import { useState } from "react";
12
 
13
  type RotationPanelProps = {
14
  facingDirection: FacingDirection;
@@ -16,12 +12,8 @@ type RotationPanelProps = {
16
  onClick?: (step: RotationStep) => void;
17
  };
18
 
19
- export const RotationPanel = ({
20
- facingDirection,
21
- direction,
22
- onClick,
23
- }: RotationPanelProps) => {
24
- const clockwise = direction === "clockwise";
25
  const texture = useLoader(TextureLoader, `/textures/${direction}.png`);
26
  const [opacity, setOpacity] = useState(0);
27
 
@@ -34,8 +26,7 @@ export const RotationPanel = ({
34
  bottom: clockwise ? [0.5, -1.01, 0] : [-0.5, -1.01, 0],
35
  };
36
 
37
- const handleClick = () =>
38
- onClick?.({ faceDirection: facingDirection, direction });
39
 
40
  return (
41
  <mesh
@@ -43,22 +34,16 @@ export const RotationPanel = ({
43
  rotation={Rotations[facingDirection]}
44
  onPointerEnter={() => {
45
  setOpacity(1);
46
- document.body.style.cursor = "pointer";
47
  }}
48
  onPointerLeave={() => {
49
  setOpacity(0);
50
- document.body.style.cursor = "default";
51
  }}
52
  onClick={handleClick}
53
  >
54
  <planeGeometry args={[0.8, 1.6]} />
55
- <meshStandardMaterial
56
- color={"#aaaaaa"}
57
- roughness={0.5}
58
- opacity={opacity}
59
- map={texture}
60
- transparent
61
- />
62
  </mesh>
63
  );
64
  };
 
1
+ 'use client';
2
 
3
+ import { useLoader } from '@react-three/fiber';
4
+ import { useState } from 'react';
5
+ import { TextureLoader } from 'three';
6
+
7
+ import { FacingDirection, RotationDirection, RotationStep, Rotations } from './consts';
 
 
 
 
8
 
9
  type RotationPanelProps = {
10
  facingDirection: FacingDirection;
 
12
  onClick?: (step: RotationStep) => void;
13
  };
14
 
15
+ export const RotationPanel = ({ facingDirection, direction, onClick }: RotationPanelProps) => {
16
+ const clockwise = direction === 'clockwise';
 
 
 
 
17
  const texture = useLoader(TextureLoader, `/textures/${direction}.png`);
18
  const [opacity, setOpacity] = useState(0);
19
 
 
26
  bottom: clockwise ? [0.5, -1.01, 0] : [-0.5, -1.01, 0],
27
  };
28
 
29
+ const handleClick = () => onClick?.({ faceDirection: facingDirection, direction });
 
30
 
31
  return (
32
  <mesh
 
34
  rotation={Rotations[facingDirection]}
35
  onPointerEnter={() => {
36
  setOpacity(1);
37
+ document.body.style.cursor = 'pointer';
38
  }}
39
  onPointerLeave={() => {
40
  setOpacity(0);
41
+ document.body.style.cursor = 'default';
42
  }}
43
  onClick={handleClick}
44
  >
45
  <planeGeometry args={[0.8, 1.6]} />
46
+ <meshStandardMaterial color={'#aaaaaa'} roughness={0.5} opacity={opacity} map={texture} transparent />
 
 
 
 
 
 
47
  </mesh>
48
  );
49
  };
src/components/rotator.tsx CHANGED
@@ -1,8 +1,9 @@
1
- import { FacingDirection, RotationStep } from "./consts";
2
- import { RotationPanel } from "./rotation-panel";
3
- import { rotationController } from "./rotation-controller";
4
- import { forwardRef, Fragment, useImperativeHandle } from "react";
5
- import { useFrame } from "@react-three/fiber";
 
6
 
7
  export type RotatorRef = {
8
  rotate: (steps: Array<RotationStep>) => void;
@@ -12,38 +13,33 @@ type RotatorProps = {
12
  cubeSpeed: number;
13
  };
14
 
15
- export const Rotator = forwardRef<RotatorRef, RotatorProps>(
16
- ({ cubeSpeed }: RotatorProps, ref) => {
17
- rotationController.setCubeSpeed(cubeSpeed);
18
-
19
- useImperativeHandle(ref, () => ({
20
- rotate: (steps: Array<RotationStep>) =>
21
- rotationController.addRotationStep(...steps),
22
- }));
23
-
24
- useFrame(rotationController.frameCallback.bind(rotationController));
25
-
26
- return (
27
- <>
28
- {["front", "back", "left", "right", "top", "bottom"].map(
29
- (facingDirection) => (
30
- <Fragment key={facingDirection}>
31
- <RotationPanel
32
- direction="clockwise"
33
- facingDirection={facingDirection as FacingDirection}
34
- onClick={(s) => rotationController.addRotationStep(s)}
35
- />
36
- <RotationPanel
37
- direction="counter-clockwise"
38
- facingDirection={facingDirection as FacingDirection}
39
- onClick={(s) => rotationController.addRotationStep(s)}
40
- />
41
- </Fragment>
42
- ),
43
- )}
44
- </>
45
- );
46
- },
47
- );
48
-
49
- Rotator.displayName = "Rotator";
 
1
+ import { useFrame } from '@react-three/fiber';
2
+ import { Fragment, forwardRef, useImperativeHandle } from 'react';
3
+
4
+ import { FacingDirection, RotationStep } from './consts';
5
+ import { frameCallback, rotationController } from './rotation-controller';
6
+ import { RotationPanel } from './rotation-panel';
7
 
8
  export type RotatorRef = {
9
  rotate: (steps: Array<RotationStep>) => void;
 
13
  cubeSpeed: number;
14
  };
15
 
16
+ export const Rotator = forwardRef<RotatorRef, RotatorProps>(({ cubeSpeed }: RotatorProps, ref) => {
17
+ rotationController.setCubeSpeed(cubeSpeed);
18
+
19
+ useImperativeHandle(ref, () => ({
20
+ rotate: (steps: Array<RotationStep>) => rotationController.addRotationStep(...steps),
21
+ }));
22
+
23
+ useFrame(frameCallback);
24
+
25
+ return (
26
+ <>
27
+ {['front', 'back', 'left', 'right', 'top', 'bottom'].map((facingDirection) => (
28
+ <Fragment key={facingDirection}>
29
+ <RotationPanel
30
+ direction="clockwise"
31
+ facingDirection={facingDirection as FacingDirection}
32
+ onClick={(s) => rotationController.addRotationStep(s)}
33
+ />
34
+ <RotationPanel
35
+ direction="counter-clockwise"
36
+ facingDirection={facingDirection as FacingDirection}
37
+ onClick={(s) => rotationController.addRotationStep(s)}
38
+ />
39
+ </Fragment>
40
+ ))}
41
+ </>
42
+ );
43
+ });
44
+
45
+ Rotator.displayName = 'Rotator';
 
 
 
 
 
src/components/rubiks-cube.tsx CHANGED
@@ -1,9 +1,10 @@
1
- import { useRef, forwardRef, useImperativeHandle, useEffect } from "react";
2
- import { CubePiece } from "./cube-piece";
3
- import { Rotator, RotatorRef } from "./rotator";
4
- import { Group } from "three";
5
- import { RotationStep } from "./consts";
6
- import { rotationController } from "./rotation-controller";
 
7
 
8
  const CUBE_POSITIONS: Array<[number, number, number]> = [];
9
  for (let x = -0.5; x <= 0.5; x += 1) {
@@ -23,35 +24,28 @@ type RubiksCubeProps = {
23
  cubeSpeed: number;
24
  };
25
 
26
- export const RubiksCube = forwardRef<RubiksCubeRef, RubiksCubeProps>(
27
- ({ cubeRoughness, cubeSpeed }, ref) => {
28
- const cubeGroupRef = useRef<Group | null>(null);
29
- const rotatorRef = useRef<RotatorRef | null>(null);
30
-
31
- useImperativeHandle(ref, () => ({
32
- rotate: (steps: Array<RotationStep>) => rotatorRef.current?.rotate(steps),
33
- }));
34
-
35
- useEffect(() => {
36
- if (cubeGroupRef.current)
37
- rotationController.setCubeGroup(cubeGroupRef.current);
38
- }, [cubeGroupRef]);
39
-
40
- return (
41
- <>
42
- <group ref={cubeGroupRef}>
43
- {CUBE_POSITIONS.map((position) => (
44
- <CubePiece
45
- key={position.join(",")}
46
- initialPosition={position}
47
- roughness={cubeRoughness}
48
- />
49
- ))}
50
- </group>
51
- <Rotator ref={rotatorRef} cubeSpeed={cubeSpeed} />
52
- </>
53
- );
54
- },
55
- );
56
-
57
- RubiksCube.displayName = "RubiksCube";
 
1
+ import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
2
+ import { Group } from 'three';
3
+
4
+ import { RotationStep } from './consts';
5
+ import { CubePiece } from './cube-piece';
6
+ import { rotationController } from './rotation-controller';
7
+ import { Rotator, RotatorRef } from './rotator';
8
 
9
  const CUBE_POSITIONS: Array<[number, number, number]> = [];
10
  for (let x = -0.5; x <= 0.5; x += 1) {
 
24
  cubeSpeed: number;
25
  };
26
 
27
+ export const RubiksCube = forwardRef<RubiksCubeRef, RubiksCubeProps>(({ cubeRoughness, cubeSpeed }, ref) => {
28
+ const cubeGroupRef = useRef<Group | null>(null);
29
+ const rotatorRef = useRef<RotatorRef | null>(null);
30
+
31
+ useImperativeHandle(ref, () => ({
32
+ rotate: (steps: Array<RotationStep>) => rotatorRef.current?.rotate(steps),
33
+ }));
34
+
35
+ useEffect(() => {
36
+ if (cubeGroupRef.current) rotationController.setCubeGroup(cubeGroupRef.current);
37
+ }, [cubeGroupRef]);
38
+
39
+ return (
40
+ <>
41
+ <group ref={cubeGroupRef}>
42
+ {CUBE_POSITIONS.map((position) => (
43
+ <CubePiece key={position.join(',')} initialPosition={position} roughness={cubeRoughness} />
44
+ ))}
45
+ </group>
46
+ <Rotator ref={rotatorRef} cubeSpeed={cubeSpeed} />
47
+ </>
48
+ );
49
+ });
50
+
51
+ RubiksCube.displayName = 'RubiksCube';