arjan suri ← writing

teaching a 9B model to solve the rubik’s cube

A scrambled Rubik's cube drawn as an unfolded net, next to the title: teaching a 9B model to solve the rubik's cube. cross, F2L, last layer, with SFT and RL. 0% to 66% of real scrambles solved end to end.

tl;dr

I trained Qwen3.5-9B, a small open model, to solve a Rubik’s cube from a real scramble. It reads the cube as text, turns it with a move tool, and has no solver and no code execution. It started out solving almost nothing. It now solves 66% of real scrambles end to end, and 88% of them within two tries.

→ the model is open source on Hugging Face, with a one‑file script to run it in the same environment it was trained in.

I set it up as a reinforcement learning problem: an environment with the cube and its tools, a reward for progress, and GRPO on top. That was the baseline, and on its own it barely moved. What moved the numbers was post‑training with SFT: writing out the reasoning I wanted the model to do, generated and checked by a cube engine, and rewriting it every time the model got stuck. Every time I turned a search in its head into a lookup, it jumped. RL came back at the end to polish, once the model could already solve most positions some of the time.

Watch it solve. Pick one of the 28 real scrambles it solved, press play, and read what it wrote at every turn:

open the solver full page →

background

The cube looks like a toy, but it’s a brutal test for a language model. You have to track the exact state of 54 stickers across dozens of moves, understand how each turn moves them in 3D, and plan a long way ahead. One wrong move quietly wrecks everything you built.

Frontier models are bad at it. CubeBench tested GPT‑5, Gemini 2.5 Pro, Claude Sonnet 4 and others as cube‑solving agents: on scrambles 8 or more moves from solved, every model scored 0%. Give them a solver tool and GPT‑5 jumps to 95%. So the models know what a move does. What breaks is the long‑horizon planning.

And a small network trained from scratch doesn’t get around this: classic solvers like DeepCubeA lean on a search algorithm to do the planning, CubeBench’s own RL‑trained network solved 0% of long scrambles without one, and my model has to read the cube as text and plan with no search at all.

I wanted to know if a 9B model could learn the planning itself.

I’d never done RL before this. I talked to a couple of friends at different RL‑environment startups, who pointed me at what to read (the sources are at the end), and I learned the rest by running it.

methods & results

The approach had three parts:

  1. An RL environment as the baseline: the cube, three tools, and a reward a program can check exactly, trained with GRPO.
  2. A curriculum: solve the cube in CFOP stages, one at a time, each starting from the model before it.
  3. SFT post‑training: engine‑written reasoning, rewritten wherever the model got stuck, then RL once it could solve a stage some of the time.

The model sees the cube as six 3×3 grids of color letters, one per face, and acts through three tools: move (e.g. "R U R'"), peek and state. Thinking mode is off, so all of its reasoning is visible in its reply. The CFOP stages:

  1. cross: the 4 white edges around the white center.
  2. F2L (first two layers): the 4 corner‑edge pairs under the cross.
  3. OLL (orient the last layer): make the top face yellow.
  4. PLL (permute the last layer): move the top pieces into place.

Training used prime‑rl and verifiers with LoRA. I started on GPUs rented through Prime Intellect, but the deals weren’t stable (offers vanished or machines came up broken), so I moved to Modal.

cross

first attempts

I started on the easiest possible tasks, and spent the first week there: crosses and “undo” puzzles only 1–2 moves from solved. Here’s everything I tried, in order. Hover over a row to preview it, or click to keep it open, and see what happened, with a real excerpt of what Qwen wrote.

approachtrained onresult
base Qwen3.5‑9B, thinking offnothingcross 6/32, undo 9/31
base, thinking onnothingsome solves, huge thoughts
SFT on solver moves1,000 engine traces, no reasoningcross 0/32, undo 5/32
frontier teacher, reasoning off(eval only)cross 1/20
frontier teacher, reasoning on(eval only)cross 296/300
SFT on the teacher’s explanations275 teacher tracescross 0/32, undo 1/32
RL (GRPO) from the base model40 steps, depths 1–2undo 7 → 8/32, cross 0 → 2–3/32
rejection sampling: SFT on its own verified solves264 of its own solvesundo 16% → 34%
RL on cube quizzes60 steps of short questionsreplies −32%, planning 14 → 16%

held‑out evals on 1–2‑move tasks (fractions are solved / episodes), except where noted

hover over or tap a row above

why rejection sampling worked

The one clear win was the simplest: let the RL model attempt thousands of tasks, keep only the attempts the engine verified as solved, and fine‑tune on those. Undo solves went from 16% to 34%, and 2‑move undos went from 0% to 29%, a skill it didn’t have before. Why it beat everything else:

But it had a hard ceiling, exactly the one Will Brown describes: it can only lock in what the model can already sometimes do. Crosses had only 45 kept examples and stayed flat. Depth 3 had zero solves to keep, so it stayed at zero.

That pointed at the real move. Rejection sampling worked because the examples were verified reasoning with the work written out. My engine can produce that for any position, at any depth, in unlimited quantity. So from here on, I wrote the reasoning myself, generated by the engine and checked line by line.

hill‑climbing the reasoning

Each version below is the same model and the same kind of SFT run. The only thing that changed was the shape of the reasoning in the training data. After every run I graded the model’s own working against the real cube to find the step that broke, then rewrote that step.

versionwhat changed in the reasoningshort crossesreal scrambles
stepstates each edge’s position and distance28%–
scancopies the edge squares off the cube first67%–
scan v2 (v3)names edges in the order they were copied66%11.9%
deepadds 5–8‑move crosses to the data38%(9% on 5–8)
v4+ RL with a distance reward67%14.5%
v5lists a move per edge that gets it 1 closer93% / 81%64.2%
v6+ RL with a distance reward97% / 88%75.9%
v7+ 100 more RL stepsflat–

short crosses: 1–4 moves, 16 per depth (v5/v6: 3–4 moves on the position bank). real scrambles: a fixed bank of positions, 8 tries each, weighted like real scrambles (5–7 moves).

Moves:
  W-B (1 move from home): B2 brings it home, keeps the home edges.
  W-G (1 move from home): F2 brings it home, keeps the home edges.
  W-R (2 moves from home): F2 brings it 1 closer, keeps the home edges.
-> B2
B2: W-B to D row 2 col 1 (home).
After: 2 of 4 home.
Bar chart of real-scramble crosses solved: base Qwen3.5-9B about 0%, v3 scan SFT 11.9%, v4 plus RL 14.5%, v5 move-choice SFT 64.2%, v6 plus RL 75.9%.
crosses solved, on a fixed bank of positions (3–8 moves deep, 8 tries each), weighted like real scrambles

F2L

you can’t imitate a search

F2L is where the lesson really landed. Every version here starts from the v6 cross model.

versionthe reasoning1 pair234 (full)
v6, no F2L training–0%0%0%0%
F1the expert solver’s plan, stated13%0%0%0%
F3case lookup, 236 algorithms41%19%9%0%
F4case lookup from one side, 59 algorithms98%98%94%94%

F2L solved, by how many pairs were left to place. Prompting v6 with the method (no training) also stayed at 0.

Step: case. BR corner in its slot (DRB), white on R; BR edge at UL, B on U.
FR view (R>F, B>R, L>B, F>L): corner DFR, white on F; edge UB, R-color on U.
FR algorithm: R U' R' F' U' F
For BR (F>R, R>B, B>L, L>F): B U' B' R' U' R
Grouped bar chart of F2L solved by pairs left. Copying the solver's plan: 13, 0, 0, 0 percent. Case lookup with 236 algorithms: 41, 19, 9, 0. Case lookup with 59 algorithms seen from FR: 98, 98, 94, 94.
F2L solved, by how many pairs were left to place

I’d planned RL on F2L next, and launched it. But its first eval showed F4 already solving 1–2 pairs nearly every time. With every group of 8 a sweep of wins, RL could only shave a few moves off. I stopped it after 11 minutes.

OLL + PLL

mostly memorization, then the full solve

We gave it the beginner two‑look algorithms (16, all face moves) and had it solve whole cubes, cross to last layer, in one conversation.

test (model F5)solved
whole last layer, before last‑layer training (F4)0 / 8
whole last layer, after60 / 64 (94%)
last PLL step alone26 / 32 (81%)
full F2L (4 pairs), unchanged31 / 32 (97%)
6‑move crosses, unchanged25 / 32 (78%)
real scramble → solved, end to end42 / 64 (66%)
… solved in at least 1 of 2 tries28 / 32 (88%)
Stacked bar of 64 full-solve tries: 42 solved, 12 ran out of their 30 turns, 10 hit the 120-move cap.
64 tries on 32 real scrambles. Every miss hit a turn or move limit, not a dead end.

conclusions

what i learned

what’s next

Choosing an unverifiable problem. The cube made everything checkable, and most problems worth solving aren’t like that: writing, analysis, research. The next step is learning how to post‑train when there’s no exact answer to check, where the reward is a rubric or a judge and the training data can’t be checked line by line.

Getting really good at one specific task with LLMs. The recipe here seems general: build an environment with a reliable check, write the reasoning as lookups instead of searches, keep the lookup tables small, SFT first, then RL once the model sometimes succeeds. I want to see how far that goes on a task that matters outside of puzzles.

And for the cube: rerun the full solves with sensible limits to get the true solve rate, fix the weakest stage (about 78% on 6‑move crosses) with DAgger, letting the model play and having the engine label the moves it should have made, then the two PLL cases it fumbles (H and Z perms), and eventually RL on whole solves.

sources