This is an open-source project developed by Samuele Riccardi, a university student passionate about robotics and algorithm development.
RS1 is a Rubik's Cube solving algorithm that implements a hybrid solving method: the classic Layer by Layer approach for the first two layers, combined with OLL/PLL algorithms for the last layer. The algorithm receives a scrambled cube configuration and returns the sequence of moves required to solve it.
This repository contains the algorithm module of a larger robotics project. In the future, additional modules will be published to assemble a complete cube-solving robot, including:
- Vision module (cube scanning and color recognition)
- Hardware control module (robotic arm movements)
- Integration module (orchestrating all components)
The service accepts a Rubik's Cube represented as 6 faces in a specific order. This order was designed to match the scanning sequence of a robotic arm that cannot scan faces consecutively:
| Input Index | Face | Color | Emoji |
|---|---|---|---|
| 0 | Front | Blue | π¦ |
| 1 | Top | Yellow | π¨ |
| 2 | Back | Green | π© |
| 3 | Bottom | White | β¬ |
| 4 | Right | Red | π₯ |
| 5 | Left | Orange | π§ |
Each face is a 3x3 matrix of color indices (0-5), read row by row from top-left to bottom-right.
When building a physical cube scanner, the camera may capture faces with incorrect rotations relative to the cube's standard orientation. Before sending the cube to the solver, you must normalize each face's rotation.
The algorithm expects each face to be oriented so that its bottom neighbor matches the expected color relationship. If your scanner captures faces in an arbitrary rotation, implement a normalization function like this:
void align_face_to_bottom_neighbor(json& cube_json, int face_index, int bottom_face_index) {
int current_face_color = cube_json["cube"][face_index][cube::CENTER_ROW][cube::CENTER_COL];
int current_bottom_face = cube_json["cube"][bottom_face_index][cube::CENTER_ROW][cube::CENTER_COL];
int expected_bottom = FACE_NEIGHBORS[current_face_color][static_cast<int>(Direction::BOTTOM)];
if (expected_bottom != current_bottom_face) {
int rotation_count = 0;
for (; rotation_count < 4; rotation_count++) {
if (current_bottom_face == FACE_NEIGHBORS[current_face_color][rotation_count]) {
break;
}
}
for (int i = 0; i < rotation_count; i++) {
rotate_json_face_clockwise(&cube_json["cube"][face_index]);
}
}
}
void fix_scan_rotation(json& cube_json) {
align_face_to_bottom_neighbor(cube_json, cube::BOTTOM_FACE, cube::FRONT_FACE);
align_face_to_bottom_neighbor(cube_json, cube::FRONT_FACE, cube::RIGHT_FACE);
align_face_to_bottom_neighbor(cube_json, cube::RIGHT_FACE, cube::BACK_FACE);
align_face_to_bottom_neighbor(cube_json, cube::BACK_FACE, cube::BOTTOM_FACE);
align_face_to_bottom_neighbor(cube_json, cube::LEFT_FACE, cube::FRONT_FACE);
align_face_to_bottom_neighbor(cube_json, cube::TOP_FACE, cube::BACK_FACE);
}How it works:
- Each face has a known neighbor relationship (stored in
FACE_NEIGHBORS) - The function checks if the current bottom neighbor matches the expected one
- If not, it calculates how many 90Β° clockwise rotations are needed
- The face is rotated until it aligns with the standard orientation
Once the cube is received, the algorithm:
- Reorders faces - Maps input order to internal representation
- Builds the cube structure - Creates faces with all cell references
- Links adjacent faces - Establishes neighbor relationships (top, bottom, left, right, opposite)
- Links border cells - Connects edge and corner cells to their neighbors on adjacent faces
- Solves using Layer by Layer + OLL/PLL:
- Cross - Solves the white cross on the bottom face
- First Layer Corners - Places all 4 corners of the bottom layer
- Second Layer Edges - Places all 4 edges of the middle layer
- OLL (Orientation of Last Layer) - Orients all yellow pieces on top
- PLL (Permutation of Last Layer) - Permutes the last layer pieces to complete the solve
This project requires ROS2 Humble. Follow the official installation guide:
# Set locale
sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8
# Setup sources
sudo apt install software-properties-common
sudo add-apt-repository universe
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
# Install ROS2 Humble Desktop
sudo apt update
sudo apt install ros-humble-desktopmkdir -p ~/rs1_ws/src
cd ~/rs1_ws/src
git clone https://github.com/ZiRoX7/RS1-Algorithm.git .cd ~/rs1_ws
source /opt/ros/humble/setup.bash
# Install ROS dependencies
rosdep install --from-paths src --ignore-src -r -y
# Install nlohmann-json (JSON library)
sudo apt install nlohmann-json3-devcd ~/rs1_ws
source /opt/ros/humble/setup.bash
colcon build
source install/setup.bashIn one terminal:
source /opt/ros/humble/setup.bash
source ~/rs1_ws/install/setup.bash
ros2 run cube_algorithm cube_algorithm_nodeIn another terminal, call the service with a scrambled cube:
source /opt/ros/humble/setup.bash
source ~/rs1_ws/install/setup.bash
ros2 service call /solve_cube cube_algorithm/srv/SolveCube "{cube_faces: [{cells: [3,1,2,5,1,0,4,3,3]}, {cells: [3,1,5,3,5,4,4,2,3]}, {cells: [2,4,5,0,3,5,1,4,0]}, {cells: [5,0,0,3,0,2,2,0,0]}, {cells: [0,3,1,2,2,1,4,5,4]}, {cells: [2,2,5,1,4,4,1,5,1]}]}"The service will return:
success: Whether the cube was solved successfullymoves_json: JSON array of moves to solve the cubeerror_message: Error description if solving failed
The cube_tester package allows you to validate the solver by testing it against randomly scrambled cubes.
Edit the constants in cube_tester/include/cube_tester/cube_tester.hpp:
// Number of cubes to test
constexpr int NUMBER_OF_CUBES = 100;
// Number of random moves to scramble each cube
constexpr int NUMBER_OF_RANDOM_MOVES = 25;- NUMBER_OF_CUBES: How many random cubes to generate and solve
- NUMBER_OF_RANDOM_MOVES: Scramble complexity (higher = more scrambled)
First, start the solver service (if not already running):
ros2 run cube_algorithm cube_algorithm_nodeThen, in another terminal, run the tester:
ros2 run cube_tester cube_tester_node- Creates a solved cube
- Applies N random moves to scramble it
- Sends the scrambled cube to the solver service
- Verifies the response
- If successful: Deletes the test file
- If failed: Keeps the cube JSON in
src/cube_tester/failed_cubes/for debugging
At the end, it prints a summary:
Passed: 100/100
Failed: 0/100
RS1/src/
βββ cube_structure/ # Core data structures
β βββ include/ # Headers (structs, constants, cube operations)
β βββ src/ # Implementation (cube building, movements)
β
βββ cube_algorithm/ # Solver implementation (Layer by Layer + OLL/PLL)
β βββ include/ # Solver headers (cross, layers, OLL, PLL)
β βββ src/ # Solver implementations
β βββ srv/ # ROS2 service definition
β
βββ cube_tester/ # Testing utilities
βββ include/ # Tester headers
βββ src/ # Random cube generation and validation
Samuele Riccardi
- π LinkedIn Profile
- πΊ YouTube Demo Video
- π LinkedIn Post
This project is licensed under the MIT License - see the LICENSE file for details.
