• Welcome to SpeedSolving.com — the world’s largest puzzle community!

    You’re currently browsing as a guest, which means you have limited access to discussions, resources, member profiles, and community features.

    Join 50,000+ cubers and puzzle enthusiasts from around the world to ask questions, share solves, improve faster, and be part of the community.

    Registration is free, fast, and easy — create your account today.

    Already a member? Log in here to hide this message and start participating.

Coding a cube solver

CubingKid

Regular
Joined
Jan 19, 2025
Messages
37
Location
Portugal
Today I started coding my secound cube simulator/solver. I decided to restart from scratch because in my previous iteration I used an array of stickers to represent the cube, and that's a really ugly way to do it. This time I did seperate arrays for permutation and orientation of the pieces. This still doesn't seem to be the way most people do it, instead using bitmapping, but it's already much better.

I am using python, and for simplicity I started off with a 2x2 simulator and solver. I will eventually upgrade it to a 3x3 solver.

I am using a brute force (IDA*) solver with two kinds of pruning:

- Do not do moves in the same layer twice in a row
- Quit exploring if it is definilly impossible to solve in x moves

For the secound pruning, I was forced to use a heuristic function. More specifically, I need a way to look at the cube and say "it MUST take at least x moves". I decided to use a simple heuristic function, at least for now, by simply counting the ammount of oriented corners (note that for my simulator DBL does not exist):
3 oriented corners: 1 move
1 oriented corner: 2 moves
7 oriented corners: if solved, then 0 moves, otherwise 1 move
Any other ammount: 3 moves

I conjecture 2 oriented corners require 4 moves.

It is currently solving random scrambles (25 random moves) in about 10 secounds.

Updates soon
 
Last edited:
Update: better pruning/heuristic.

I have changed the old oriented corner counter heuristic to a more sufisticated heuristic. Now it will take the exact orientation of all corners and search in a lookup table (array). It has 3^6 = 729 entries, where the largest value is 6. The lookup table is generated at runtime practically instantly. When generating the lookup table 2 kinds of prining can be used: same move pruning (not turning the same side twice in a row), and U pruning (not beggining the search with a U turn, since U turns do not affect orientation on a solved cube.

I also added explicit code to never return an heuritic of 0 if the cube is not solved, instead returning 1 if the table says 0 with a scrambled (domino reducted) cube, since the brute forcer assumes heuristic 0 means solved cube.

With this better pruning, the code is now solving the cube move-optimally in about 1 secound. I think I am now ready to move on to 3x3.

Edit: I was curious so I couldn't help with doing it. I re-ran the table-generating search but using the oriented corner ammount instead. Here are the results:
0: 3
1: 2
2: 4 (as conjectured!)
3: 1
4: 3
5: 4
6: impossible/corner twist
7: 0 (or 1 if unsolved)
 
Last edited:
Today I started coding my secound cube simulator/solver. I decided to restart from scratch because in my previous iteration I used an array of stickers to represent the cube, and that's a really ugly way to do it. This time I did seperate arrays for permutation and orientation of the pieces. This still doesn't seem to be the way most people do it, instead using bitmapping, but it's already much better.

I am using python, and for simplicity I started off with a 2x2 simulator and solver. I will eventually upgrade it to a 3x3 solver.

I am using a brute force (IDA*) solver with two kinds of pruning:

- Do not do moves in the same layer twice in a row
- Quit exploring if it is definilly impossible to solve in x moves

For the secound pruning, I was forced to use a heuristic function. More specifically, I need a way to look at the cube and say "it MUST take at least x moves". I decided to use a simple heuristic function, at least for now, by simply counting the ammount of oriented corners (note that for my simulator DBL does not exist):
3 oriented corners: 1 move
1 oriented corner: 2 moves
7 oriented corners: if solved, then 0 moves, otherwise 1 move
Any other ammount: 3 moves

I conjecture 2 oriented corners require 4 moves.

It is currently solving random scrambles (25 random moves) in about 10 secounds.

Updates soon
I have a unique way to represent a cube state based on the symmetry and geometry of the cube. The cubies are rearranged by 90˚ rotations about the principal axes of the cube. The closure of the group formed by combining 90˚ rotations is the 24 element cubic rotational group. There are 24 ways to place a corner cubie in the cube: 8 corners x 3 orientations. There are 24 ways to place an edge cubie in the cube 12 edges x 2 orientation. These permutations correspond to the 24 elements of the cubic symmetry group. So one may specify the state of the cube with a 20 element array listing the cubic group rotation which will move each cubie from its home position to its present position and orientation. Thus my representation consists of a 20 byte array of integers from 0 to 23.
 
I have a unique way to represent a cube state based on the symmetry and geometry of the cube. The cubies are rearranged by 90˚ rotations about the principal axes of the cube. The closure of the group formed by combining 90˚ rotations is the 24 element cubic rotational group. There are 24 ways to place a corner cubie in the cube: 8 corners x 3 orientations. There are 24 ways to place an edge cubie in the cube 12 edges x 2 orientation. These permutations correspond to the 24 elements of the cubic symmetry group. So one may specify the state of the cube with a 20 element array listing the cubic group rotation which will move each cubie from its home position to its present position and orientation. Thus my representation consists of a 20 byte array of integers from 0 to 23.
Your representation seems more memory efficient and easier to render, but I believe that it would make it harder to read the state of the cube and do something useful with it, like checking wether a specific step (namely domino reduction) has been achieved
 
Last edited:
Update: I made the 3x3 solver (kociemba algorithm).

First, I decided to use a similar IDA* aproach to last time. For this I used seperate tables for EO, CO, EP, CP. This aproach was sufficient to solve the cube, but I decided to change to a more direct aproach using a list of known positions for each phase. For the first phase, a list with all positions solvable in at most 6 moves was used, and for the secound phase, a list with all the positions solvable in at most 7 moves. This way, whenever any position in the table is found, the search emmidiately stops, and the rest of the phase is solved trivially using the table (which, just to clarify, only stores state-distance pairs, not actually the solutions). I then managed to store the table in external files in order to avoid re-computing them every time. Despite this, reading the tables and parsing them using python's eval function still takes about 40 secounds. Solving the cube takes about 10 secounds, most of said time in the secound phase of the solve (a.k.a. DR into solved, or even <U, L2, F2, R2, B2, D> into <>). I attempted to use a depth-8 secound phase table to solve the 10 secound issue, but I got a memory-related crash. Weirdly enough, it was not a standard python exception, the python phone app I'm using actually crashed. Something I'd like to note, tho, is that the 40 secound read time only applies the moment the program in opened. This means that, if you want to solve like 10 cubes, you only have to wait the 40 secounds once and then wait 10 secounds for each cube. I now plan to translate the code to c or c++ in order to make the code as fast as possible. Altho I have not used AI for the project at any point so far, I may use it to help translate the code.
 
  • Like
Reactions: qwr
Your representation seems more memory efficient and easier to render, but I believe that it would make it harder to read the state of the cube and do something useful with it, like checking wether a specific step (namely domino reduction) has been achieved
Actually, routines to convert my cubie based rep to a position based facelet pemutation, or a Kociemba like position/orientation rep are straightforward. Michael Reid who wrote the first (as far as I am aware) optimal 3x3 solver introduced a position based facelet perputation rep as a string: a solved cube is represented as:


@"UF UR UB UL DF DR DB DL FR FL BR BL UFR URB UBL ULF DRF DFL DLB DBR"



a three way swap of the Up-Right, Up-Back and Up-Front cubies would be written as:​

​


Index​
0​
1​
2​
3​
4​
5​
6​
7​
8​
9​
10​
11​
12​
13​
14​
15​
16​
17​
18​
19​
Cubicle​
UF​
UR​
UB​
UL​
DF​
DR​
DB​
DL​
FR​
FL​
BR​
BL​
UFR​
URB​
UBL​
ULF​
DRF​
DFL​
DLB​
DBR​
Cubie​
UR​
UB​
UF​
UL​
DF​
DR​
DB​
DL​
FR​
FL​
BR​
BL​
UFR​
URB​
UBL​
ULF​
DRF​
DFL​
DLB​
DBR​

a tandem flip of the Up-Front and Up-Right cubies is represented as:


Index​
0​
1​
2​
3​
4​
5​
6​
7​
8​
9​
10​
11​
12​
13​
14​
15​
16​
17​
18​
19​
Cubicle​
UF​
UR​
UB​
UL​
DF​
DR​
DB​
DL​
FR​
FL​
BR​
BL​
UFR​
URB​
UBL​
ULF​
DRF​
DFL​
DLB​
DBR​
Cubie​
FU​
RU​
UB​
UL​
DF​
DR​
DB​
DL​
FR​
FL​
BR​
BL​
UFR​
URB​
UBL​
ULF​
DRF​
DFL​
DLB​
DBR​
 
Last edited:
Your representation seems more memory efficient and easier to render, but I believe that it would make it harder to read the state of the cube and do something useful with it, like checking wether a specific step (namely domino reduction) has been achieved
I,ve never tried to index the cosets of the <U,D,L2,R2,F2,B2>. subgroup. My own optimal solver is based on indexing the cosets of the subgroup having the UP face solved, which is easy to do: all members of a coset have the same configuration of the UP face cubies. As part of my implementation I construct look up tables for the position and orientation of a cubie in each rotational state. So I have the info at hand. If Kociemeba can do it I daresay I could as well.
 
Today I started coding my secound cube simulator/solver. I decided to restart from scratch because in my previous iteration I used an array of stickers to represent the cube, and that's a really ugly way to do it. This time I did seperate arrays for permutation and orientation of the pieces. This still doesn't seem to be the way most people do it, instead using bitmapping, but it's already much better.

I am using python, and for simplicity I started off with a 2x2 simulator and solver. I will eventually upgrade it to a 3x3 solver.

I am using a brute force (IDA*) solver with two kinds of pruning:

- Do not do moves in the same layer twice in a row
- Quit exploring if it is definilly impossible to solve in x moves

For the secound pruning, I was forced to use a heuristic function. More specifically, I need a way to look at the cube and say "it MUST take at least x moves". I decided to use a simple heuristic function, at least for now, by simply counting the ammount of oriented corners (note that for my simulator DBL does not exist):
3 oriented corners: 1 move
1 oriented corner: 2 moves
7 oriented corners: if solved, then 0 moves, otherwise 1 move
Any other ammount: 3 moves

I conjecture 2 oriented corners require 4 moves.

It is currently solving random scrambles (25 random moves) in about 10 secounds.

Updates soon
Um where is the URL?
 
do you have a github repo or is it not open source
I just find it difficult to use the mobile file manager. Also the obvious fact that it is still under active development (still a few small ideas to brainstorm) but I may try putting it on github

Edit: I still haven't figured out any intuitive way to enter costum cube positions so the first version will likely just use random 25 move scrambles. I also have to find a better way to read the pruning table files than the eval function for obvious reasons
 
Last edited:
Update: optimal solver by generating multiple solutions until one is proven optimal. Also, reversed back to IDA*, for the first phase, a depth 6 pruning table is beeing used, and for the secound phase, a depth 7 pruning table is beeing used. This means that, for example in the first phase, if said position x in within 6 moves of domino reduction, it must be in the table, and the heuristic for the IDA* brute forcer will just be whatever the table says the distance is. However, if the position is not in the table, then it is not within 6 moves of domino reduction, thus beeing at least 7 moves away, and a static heuristic of 7 is used. Similar concept applies for the secound phase. A new kind of pruning is now also used in the secound phase. Let's say we are using the optimal solver, the best solution we have found so far is 19 moves long and it took 10 moves to reach the domino reduction we are trying to solve. In this condition, there's no point on trying to find a secound phase solution any longer than 8 moves, and if we end up proving that 8 moves to not suffice we can simply give up on the domino reduction entirely as it is simply not good enough to provide a new best solution. Using this pruning, we also have the garantee that all solution that we find are the new best solution. A similar, altho less powerfull, logic can also be used in the first phase in order to know when to stop searching. Let's say we have a best solution of 18 moves. In this case, if we finish checking all phase 1 solutions and find that none of them beat this 18 move solution, we can stop the search as a whole, as we can't beat an 18 move solution when the first phase alone is already 18 moves or more. This condition terminates the search and serves as proof that the best solution found is optimal. Despite that, the code is still written on python running on a phone, and I'm yet to see one single non-trivial search finish within reasonable time. Dispite that, actually finding the optimal solution takes way less time than proving it is optimal. It may take multiple hours to finish the search proving the solution is optimal, but just several minutes to find said solution. It, seperately, also finds multiple technically not optimal but still good solutions along the way, about the same size as you may find the scrambles in cstimer to be, in just a few secounds (not counting the time to load the pruning tables to ram, since you just have to do that once if you want to solve multiple cubes). If you do not care about the intermediate solutions and only care about the final optimal solution, one other small pruning you may use is to initialize the best solution length variable to 21 rather than infinity, since it is proven by exaution that no state of the rubik's cube takes more than 20 moves to solve. Since the ammount of states of the cube that actually requires 20 moves to solve is way bellow 1%, you may want go initialize the variable at 20 rather than 21, but I don't recommend that, since doing so would finish the search before finding any solution at all in 20 move scrambles.

Here's an example of the optimal solver doing the Jb perm scramble.
Reading table #1
Reading table #2
0
999 11 U R2 B2 U B2 U' B2 D B2 D' R2
1
2
3
4
5
6
7
8
11 10 B2 L U L' B2 R D' R D R2

First it reads both tables. Nothing to comment here; takes about 1 minute. Then it explores phase 1 solutions of increasing length. Each time a number is printed out, the next phase 1 solution length starts beeing explored. First, it starts exploring 0-long phase 1 solutions and emmidiately finds an 11 move solution within <U, L2, F2, R2, B2, D> to the Jb perm. However, after several minutes it found an 8 move phase 1 solution that resulted in an overall 10 move long solution, 8 for first phase and 2 for secound phase. Notice that, altho the initial scramble already had the first phase done, it couldn't find the optimal solution the first time because the optimal solution violated the restrictions of phase 2. You may also notice that I didn't let it finish running both because I know the optimal solution is 10 moves because I am into fmc and because it would take hours to finish exploring depth 9 at this pace. Still, I'm quite excited to see how fast this will run once I get it running on a faster programming language (probabilly c, c++ or java) and on my computer.

Edit: I forgot to explain what the numbers in the lines that have the solutions mean. The first number is the length of the previous best solution, and the secound number is the length of the new solution. Notice that I am not using the 21 move pruning.
 
I have a naive question,

What does the 21 move pruning do?
When searching for an optimal solution, it is possible to assume that the cube must not take more than 20 moves by initializing the "best solution length" variable at 21. Assuming this allows you to abort searching phase 2 solutions with no potencial to be optimal
 
Update: translated the code to c and PUBLISHED the code.

If you just want the code, here it is: https://github.com/RodrigoTeixeira01/Rubik-s-cube-solver-in-c

I finally arrived home from vacation, this means I got access to my computer back. I chose not to use AI to translate the code, keeping it fully human made. So far it simply solves one phase and then the other, without searching multiple phase 1 solutions or anything. Really just wanted to get a working code for now. I used IDA* for both phases, but instead of using the simple state list aproach I was using in the python version, I decided to instead use seperate tables for corners and edges, resulting in less accurate lower bounds but making the code simpler, as I wanted to avoid using hashmaps, since c doesn't have native support for them. Instead I used plain arrays with smart indexing for the tables (more detailed information can be found in the project's readme file in github). I found significant performance boosts in both generating the tables (in the previous version, they were read from a file instead, altho the new version generates tables with more depth in less time than the old version read the tables) and solving the cube. I extimate that generating the tables now takes about 1 secound and solving the cube takes another secound, maybe less. Additionally, when writting some pruning-table related code, I decided to slightly change the order of the edges in the array compared to the previous version, such that now the E slice edges are stored last rather than in the middle of the array. This has no significant effect on performance and was done simply for code simplicity sake. I am very happy on how this turned out and look forwards to make it an optimal solver to see how it compares to my previous optimal solver and even to more well-estabilished code such as cubeexplorer. I also plan to make the pruning tables hard coded, rather than generated in runtime, since c seems to allow relatively big hardcoded arrays without much of an issue.
 
Back
Top