So I got this idea of making a Rubik’s Cube solver in Ruby and I would call it RubyCube. Hilarious, right? But kind of stupid, because nobody really cares what the solver is written in as long as it actually works. Looking back, I think similarly few users would care whether I vibe-coded it with AI or handcrafted each line, especially given that I’m not open-sourcing it. Here, for what matters, I vibe-coded the front entirely (I just told Copilot to use three.js, don’t ask me more) and manually coded the backend solver in Ruby.
It’s live at rubycube.jmarhic.com!

My 2x2x2 Rubik's cube solver
So, while I can’t talk much about the frontend, the backend is quite elegant. I followed part of Can a Rubik’s Cube be brute-forced—in short, I use a “brute-force + meet-in-the-middle” approach. To solve a given “state,” we compute the follow-up states by applying every possible rotation. We get a tree that we traverse in “breadth-first order” (not depth-first) to ensure we get the shortest solution. And to speed things up, I precomputed the first 6 levels (every possible state up to 6 rotations) and stored them in a db, so when solving a cube we only have to solve it “up to one of the stored states,” not “up to the initial state.” Anyway, I think reading the “solve” method below is easier to understand: notice the commented “if state == solved_state”, replaced by a db lookup “find_moves_for_state”.
The cube state is represented as an array of numbers (each sticker from 0 to 23—it’s a 2x2x2 cube, by the way), and a move is just a permutation of this array. It was easy to write the permutation by hand, since I had a cube at hand and just wrote the number on each sticker. The most painful part of the code was converting from a “color state” (what the frontend sends, the color of each cubie for each face) to this “permutation state” (list of numbers).
| |
Notice I can stop the search after 6 moves—that’s because any state can be solved in less than 11 moves (it’s called God’s number), and the first 6 moves are already stored in the db. I really appreciate this “meet-in-the-middle” search where we trade some storage cost (precomputing the states and storing them in a db) for a speed boost at search time. It’s kind of like the famous “Leetcode 4 sum” problem where you have to find 4 numbers that sum up to some target; you “meet in the middle” by generating two lists instead of brute-forcing all combinations of 4 numbers.
Anyway, that marks my first project of 2026 and I’m happy with it!