The theory was that by "reverse engineering" prompt from already working solution
it may be possible to generate "one shot" solution.
So I again asked Gemini to generate a new, very specific, prompt.
And then I run that new prompt "from scratch" with Gemini, Claude and ChatGPT.
The results are interesting... see below, and play with it.
You can see them working here, and see the generated code by "view source".
So this is a variant of "destilling" solution to a prompt,
and then generating new solution from the new prompt,
passing "AI genes" from one generation to another.
AI solutions evolution!
Please generate a prompt for creating this app in "one shot", with very specific instructions what libs to use and how, what features to include and how. The intent is to make it work on a first try if at all possible!
```
Create a complete, single HTML file web application for an Interactive 3D Rubik's Cube.
**I. Core Objective:**
Display an N x N x N Rubik's Cube (default N=3, user-configurable from N=2 to N=5). Allow users to rotate the cube's view, perform face and slice rotations on the cube itself, scramble it, attempt to solve it (for 3x3x3), and log manual moves.
**II. External Libraries (Use these exact CDN links):**
1. **Three.js (r128):** `https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js`
2. **OrbitControls (for Three.js r128):** `https://unpkg.com/three@0.128.0/examples/js/controls/OrbitControls.js`
3. **TWEEN.js (18.6.4):** `https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.js`
4. **cube.js (1.3.2 - for logical cube state and solving):**
* `https://cdn.jsdelivr.net/npm/cubejs@1.3.2/lib/cube.min.js`
* `https://cdn.jsdelivr.net/npm/cubejs@1.3.2/lib/solve.min.js`
**III. HTML Structure (Key Elements & IDs):**
* A `body` with `flex-direction: column`, `align-items: center`.
* `div#title-bar`: For the main title and a subtitle ("Drag to rotate cube view. Scroll to zoom.").
* `div#container`: This is where the Three.js canvas will be appended. It should have a `min-height` (e.g., 300px) and `max-width` (e.g., 800px) and grow to fill available vertical space.
* `div#ui-panel`: Contains all controls, centered below the canvas.
* `div#cube-config`:
* `label` for "Cube Size (N):" (must not wrap).
* `input type="number" id="cubeSizeInput"` (value=3, min=2, max=5).
* `button id="generateCubeBtn"` text "Generate Cube".
* `div.controls-group.face-rotations`:
* `legend`: "Face Rotations".
* Buttons for U, U', D, D', R, R', L, L', F, F', B, B'. Each button should have a `data-move` attribute with its corresponding move string (e.g., `data-move="U"`). Prime moves (e.g., U') should have a `class="prime"`.
* `div.controls-group.slice-rotations#slice-moves-group`:
* `legend`: "Slice Rotations (3x3x3 only)".
* Buttons for M, M', E, E', S, S'. `data-move` attributes and `class="prime"` as above. This group should be hidden if N is not 3.
* `div.controls-group#manual-log-group`:
* `legend`: "Manual Moves Log".
* `div.manual-log-content-wrapper` (flex container):
* `div#manualMovesLogDisplay.info-display` (for displaying logged moves, `flex-grow: 1`).
* `button#clearManualLogBtn` text "Clear".
* `div.controls-group` (for Scramble):
* `legend`: "Scramble".
* `label` for "Moves:".
* `input type="number" id="scrambleMovesInput"` (value=20, min=1, max=100).
* `button id="scrambleBtn"` text "Scramble".
* `button id="unscrambleBtn"` text "Unscramble" (initially disabled).
* `div` with text "Scramble Sequence: " followed by `span#scrambleSequenceDisplay.info-display`.
* `div.controls-group` (for Actions):
* `legend`: "Actions".
* `button id="solveBtn"` text "Solve with cube.js (3x3x3 only)" (disabled if N is not 3 or cube.js fails).
* `div` with text "Solution Steps: " followed by `span#solveStepsDisplay.info-display`.
* `div#status-message`: For displaying status like "Loading...", "Ready.", "Moving: U...", "Cube solved!".
**IV. CSS Styling (Key Requirements):**
* **General:** `body` background `#f0f0f0`, text `#333`. `ui-panel` background `#fff`.
* **Layout:** Use flexbox for centering and arrangement as described in HTML. `controls-group` should have `max-width: 500px` and be centered.
* **Rotation Buttons (`.face-rotations`, `.slice-rotations`):**
* The `legend` should be on its own line, full width of the group.
* Buttons should be arranged in a single line below the legend, centered. Make them small enough (padding, font-size, min-width) to fit.
* Prime buttons (`.prime`) should have a distinct background color (e.g., red/orange). Normal rotation buttons green/blue.
* **"Cube Size (N):" Label:** Must not wrap (`white-space: nowrap`).
* **Manual Log:**
* `#manualMovesLogDisplay` should take most of the width.
* `#clearManualLogBtn` should be small, next to the log display, with a distinct color (e.g., orange).
* **Info Displays (`.info-display`):** Monospace font, light background, border, padding.
* **Responsiveness:** Basic adjustments for smaller screens (`@media (max-width: 600px)`) to shrink buttons/fonts slightly if needed to maintain layout.
* **Button States:** Standard hover effects. Disabled buttons should have a greyed-out appearance and `cursor: not-allowed`.
**V. JavaScript Logic (Implemented as an ES6 module `<script type="module">`):**
1. **Constants:**
* `CUBIE_SIZE = 1`, `GAP = 0.05`, `CUBIE_UNIT = CUBIE_SIZE + GAP`.
* `ANIMATION_DURATION = 300` (milliseconds).
* `COLORS`: Object mapping W, Y, R, O, G, B, BLACK to hex color codes (e.g., `W: 0xFFFFFF`).
* `MATERIALS`: Object to store `THREE.MeshBasicMaterial` for each color, initialized once.
2. **Global Variables:**
* `scene, camera, renderer, controls, cubeGroup, pivot`.
* `N` (current cube size).
* `allCubies` (array of cubie objects/meshes).
* `animationQueue` (array for queuing moves/functions).
* `isAnimating` (boolean flag).
* `logicalCubeState` (instance of `Cube` from `cube.js` for 3x3x3).
* `currentScrambleSequence` (array of strings).
* References to all relevant DOM elements from section III.
3. **Initialization (`async function init()`):**
* Get DOM elements.
* Initialize `Cube.js` solver: `Cube.initSolver()`. Handle potential errors and disable solve button if it fails.
* Initialize `MATERIALS`.
* Setup Three.js: `scene`, `camera` (PerspectiveCamera, fov 50), `renderer` (WebGL, antialias). Append renderer to `#container`.
* Setup `OrbitControls`. Enable damping.
* Add lighting (ambient and directional).
* Create `pivot` object (`THREE.Object3D`) and add to scene.
* Create `cubeGroup` (`THREE.Group`) and add to scene.
* Call `await resetAndCreateCube()`.
* Add window resize listener (`onWindowResize`).
* Start animation loop (`animateScene`).
* Call `setupUI()`.
* Set initial status message to "Ready.".
4. **Cube Creation (`async function resetAndCreateCube()`):**
* Reset animation state (`isAnimating = false`, `animationQueue = []`).
* Clear `currentScrambleSequence`, `scrambleSequenceDisplay`, `solveStepsDisplay`, `manualMovesLogDisplay`. Disable `unscrambleBtn`.
* Remove all cubies from `cubeGroup` and clear `allCubies` array.
* Read `N` from `#cubeSizeInput` (validate 2-5, default to 3).
* Call `createCubeGeometry(N)`.
* If `N === 3`:
* Initialize `logicalCubeState = new Cube()`.
* Enable `solveBtn` (if `cube.js` is working).
* Show `#slice-moves-group`.
* Else (`N !== 3`):
* Set `logicalCubeState = null`.
* Disable `solveBtn`.
* Hide `#slice-moves-group`.
* Update status message: "Solver & slice moves for 3x3x3 only."
* Set camera position based on `N` to frame the cube well (e.g., `camera.position.set(N * CUBIE_UNIT * 2.0, N * CUBIE_UNIT * 2.0, N * CUBIE_UNIT * 2.7)`). Update controls target.
5. **Cubie Geometry (`function createCubeGeometry(sizeN)`):**
* Loop `i, j, k` from `0` to `sizeN - 1`.
* Skip internal cubies (those not on any face).
* Create `THREE.BoxGeometry(CUBIE_SIZE, CUBIE_SIZE, CUBIE_SIZE)`.
* Determine materials for each of the 6 faces of the cubie:
* Right (+X, `i === sizeN - 1`): Red (R)
* Left (-X, `i === 0`): Orange (O)
* Top (+Y, `j === sizeN - 1`): White (W)
* Bottom (-Y, `j === 0`): Yellow (Y)
* Front (+Z, `k === sizeN - 1`): Green (G) <- **CORRECTION: Standard Western color scheme is Green front, Blue back**
* Back (-Z, `k === 0`): Blue (B) <- **CORRECTION: Standard Western color scheme is Green front, Blue back**
* Non-visible internal faces of cubies should use `MATERIALS.BLACK`.
* Create `THREE.Mesh` with the geometry and materials.
* Position the cubie mesh correctly based on `i, j, k`, `CUBIE_UNIT`, and an offset to center the whole cube at `(0,0,0)`.
* Add mesh to `cubeGroup` and `allCubies`.
6. **Move Configuration (`MOVES_CONFIG` constant object):**
* Define parameters for each move (U, U', D, D', R, R', L, L', F, F', B, B', M, M', E, E', S, S').
* Each move object should have: `axis` ('x', 'y', or 'z'), `layer_coeff` (1 for outer positive face, -1 for outer negative face, 0 for middle slice), `angle_mult` (-1 or 1 to ensure CW/CCW rotation matches standard notation when looking at the face).
* Example for U (clockwise from top, rotation around Y axis): `{ axis: 'y', layer_coeff: 1, angle_mult: -1 }`
* Example for R (clockwise from right, rotation around X axis): `{ axis: 'x', layer_coeff: 1, angle_mult: -1 }`
* Example for F (clockwise from front, rotation around Z axis): `{ axis: 'z', layer_coeff: 1, angle_mult: -1 }`
* Slice moves (M, E, S) should follow standard definitions (M like L, E like D, S like F).
7. **Animation (`function performMove(moveName)`, `function processAnimationQueue()`):**
* `processAnimationQueue`: If `isAnimating` or queue is empty, return. Set `isAnimating = true`. Dequeue task. If task is a function, call it, set `isAnimating = false`, recurse. Else, call `performMove(task)`.
* `performMove(moveName)`:
* Update `statusMessage`.
* Get `moveConfig` from `MOVES_CONFIG`. If unknown, warn and skip.
* If `N !== 3` and it's a slice move (`layer_coeff === 0`), warn and skip.
* Calculate `targetAngle = moveConfig.angle_mult * Math.PI / 2`.
* Select cubies: Iterate `allCubies`. A cubie is part of the move if its position along `moveConfig.axis` is close to `moveConfig.layer_coeff * ((N - 1) / 2 * CUBIE_UNIT)` (for face moves) or close to `0` (for slice moves, `layer_coeff === 0`). Use a small threshold like `CUBIE_UNIT / 2 - GAP`.
* If no cubies selected, warn and skip.
* Clear `pivot` rotation/position. Attach selected cubie meshes to `pivot`.
* Use `TWEEN.Tween` to animate `pivot.rotation[moveConfig.axis]` from `0` to `targetAngle` over `ANIMATION_DURATION` with `TWEEN.Easing.Quadratic.InOut`.
* `onComplete` of tween:
* Update pivot's world matrix.
* Reattach cubie meshes from `pivot` back to `cubeGroup`.
* If `N === 3` and `logicalCubeState` exists, call `logicalCubeState.move(moveName)`. Catch errors, potentially reset `logicalCubeState`.
* Set `isAnimating = false`.
* Call `processAnimationQueue()`.
8. **UI Setup (`function setupUI()`):**
* Add click listeners to all `button[data-move]`:
* Get `move` from `data-move`.
* Append `move` to `manualMovesLogDisplay.textContent` (with a space).
* If `animationQueue` is too long (e.g., >15), show status and return.
* Push `move` to `animationQueue`.
* Call `processAnimationQueue()`.
* `#clearManualLogBtn`: Clear `manualMovesLogDisplay.textContent`.
* `#generateCubeBtn`: If animating, show status. Else, call `await resetAndCreateCube()`.
* `#scrambleBtn`: Call `scrambleCube()`.
* `#unscrambleBtn`: Call `unscrambleCube()`.
* `#solveBtn`: Call `solveCubeWithCubeJS()`.
9. **Scramble/Unscramble:**
* `function scrambleCube()`:
* If animating or no cube, return. If `N===3` and no `logicalCubeState`, show error.
* Clear `manualMovesLogDisplay`.
* Queue a function to set status to "Scrambling...".
* Get `numScrambleMoves` from input.
* Filter `MOVES_CONFIG` keys: if `N !== 3`, exclude slice moves.
* Generate `numScrambleMoves` random moves, avoiding redundant consecutive moves on the same axis.
* Push each random move to `animationQueue` and `currentScrambleSequence`.
* Queue a function to: display `currentScrambleSequence` in `#scrambleSequenceDisplay`, enable `unscrambleBtn`, set status.
* Call `processAnimationQueue()`.
* `function unscrambleCube()`:
* If animating or `currentScrambleSequence` is empty, return.
* Clear `manualMovesLogDisplay`.
* Queue a function to set status to "Unscrambling...".
* Get inverse moves from `currentScrambleSequence` (reverse array, then map `move` to `move'` and `move'` to `move`).
* Push inverse moves to `animationQueue`.
* Queue a function to: clear `currentScrambleSequence` and display, disable `unscrambleBtn`, set status.
* Call `processAnimationQueue()`.
10. **Solve (`async function solveCubeWithCubeJS()`):**
* If animating, `N !== 3`, or `logicalCubeState` missing, or `cube.js` functions missing, show status and return.
* Clear `manualMovesLogDisplay`.
* Queue a function to set status to "Solving...". Call `processAnimationQueue()`.
* `await new Promise(resolve => setTimeout(resolve, 50))` (to allow status update).
* If `logicalCubeState.isSolved()`, show "Already solved!" status/display.
* Else:
* `solutionString = logicalCubeState.solve()`. Catch errors.
* If `solutionString`:
* Display in `#solveStepsDisplay`.
* Split `solutionString` into moves. For `X2` moves, queue `X` twice. Queue all solution moves.
* Else (no solution found), show appropriate status/display.
* Call `processAnimationQueue()`.
11. **Helper: `onWindowResize()`:** Update camera aspect and renderer size.
12. **Helper: `animateScene()`:** `requestAnimationFrame`, `TWEEN.update()`, `controls.update()`, `renderer.render()`.
13. **Load Event:** `window.addEventListener('load', async () => { await init(); });` with error catching.
**VI. Specific Color Scheme for Cube Faces (Western Standard):**
* Top (U face, +Y): White
* Bottom (D face, -Y): Yellow
* Front (F face, +Z): Green
* Back (B face, -Z): Blue
* Right (R face, +X): Red
* Left (L face, -X): Orange
**VII. Final Check:**
* Ensure all DOM element interactions are covered.
* Ensure button disabling/enabling logic is sound (e.g., during animation, for N-specific features).
* Ensure status messages are informative.
* The code should be well-structured and follow these instructions precisely.
```
This prompt is very dense, but aims to leave little to interpretation. Good luck!