Skip to main content

A basic, pure-Python Rubik's cube solver

Project description

PyPI PyPI - Python Version

Overview

This is a Python 3 implementation of a (3x3) Rubik's Cube solver.

It contains:

  • A simple implementation of the cube
  • A solver that follows a fixed algorithm
  • An unintelligent solution sequence optimizer
  • A decent set of test cases

Installation

The package is hosted on PyPI.

pip install rubik-cube

Import from the rubik package,

>>> from rubik.cube import Cube
>>> c = Cube("OOOOOOOOOYYYWWWGGGBBBYYYWWWGGGBBBYYYWWWGGGBBBRRRRRRRRR")
>>> print(c)
    OOO
    OOO
    OOO
YYY WWW GGG BBB
YYY WWW GGG BBB
YYY WWW GGG BBB
    RRR
    RRR
    RRR

Implementation

Piece

The cornerstone of this implementation is the Piece class. A Piece stores two pieces of information:

  1. An integer position vector (x, y, z) where each component is in {-1, 0, 1}:

    • (0, 0, 0) is the center of the cube
    • the positive x-axis points to the right face
    • the positive y-axis points to the up face
    • the positive z-axis points to the front face
  2. A colors vector (cx, cy, cz), giving the color of the sticker along each axis. Null values are place whenever that Piece has less than three sides. For example, a Piece with colors=('Orange', None, 'Red') is an edge piece with an 'Orange' sticker facing the x-direction and a 'Red' sticker facing the z-direction. The Piece doesn't know or care which direction along the x-axis the 'Orange' sticker is facing, just that it is facing in the x-direction and not the y- or z- directions.

Using the combination of position and color vectors makes it easy to identify any Piece by its absolute position or by its unique combination of colors.

A Piece provides a method Piece.rotate(matrix), which accepts a (90 degree) rotation matrix. A matrix-vector multiplication is done to update the Piece's position vector. Then we update the colors vector, by swapping exactly two entries in the colors vector:

  • For example, a corner Piece has three stickers of different colors. After a 90 degree rotation of the Piece, one sticker remains facing down the same axis, while the other two stickers swap axes. This corresponds to swapping the positions of two entries in the Piece’s colors vector.
  • For an edge or face piece, the argument is the same as above, although we may swap around one or more null entries.

Cube

The Cube class is built on top of the Piece class. The Cube stores a list of Pieces and provides nice methods for flipping slices of the cube, as well as methods for querying the current state. (I followed standard Rubik's Cube notation)

Because the Piece class encapsulates all of the rotation logic, implementing rotations in the Cube class is dead simple - just apply the appropriate rotation matrix to all Pieces involved in the rotation. An example: To implement Cube.L() - a clockwise rotation of the left face - do the following:

  1. Construct the appropriate rotation matrix for a 90 degree rotation in the x = -1 plane.
  2. Select all Pieces satisfying position.x == -1.
  3. Apply the rotation matrix to each of these Pieces.

To implement Cube.X() - a clockwise rotation of the entire cube around the positive x-axis - just apply a rotation matrix to all Pieces stored in the Cube.

Solver

The solver implements the algorithm described here. It is a layer-by-layer solution. First the front-face (the z = 1 plane) is solved, then the middle layer (z = 0), and finally the back layer (z = -1). When the solver is done, Solver.moves is a list representing the solution sequence.

My first correct-looking implementation of the solver average 252.5 moves per solution sequence on 135000 randomly-generated cubes (with no failures). Implementing a dumb optimizer reduced the average number of moves to 192.7 on 67000 randomly-generated cubes. The optimizer does the following:

  1. Eliminate full-cube rotations by "unrotating" the moves (Z U L D Zi becomes L D R)
  2. Eliminate moves followed by their inverse (R R Ri Ri is gone)
  3. Replace moves repeated three times with a single turn in the opposite direction (R R R becomes Ri)

The solver is not particularly fast. On my machine (a 4.0 Ghz i7), it takes about 0.06 seconds per solve on CPython, which is roughly 16.7 solves/second. On PyPy, this is reduced to about 0.013 seconds per solve, or about 76 solves/second.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rubik-cube-0.0.2.tar.gz (19.4 kB view details)

Uploaded Source

Built Distribution

rubik_cube-0.0.2-py3-none-any.whl (14.1 kB view details)

Uploaded Python 3

File details

Details for the file rubik-cube-0.0.2.tar.gz.

File metadata

  • Download URL: rubik-cube-0.0.2.tar.gz
  • Upload date:
  • Size: 19.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.3

File hashes

Hashes for rubik-cube-0.0.2.tar.gz
Algorithm Hash digest
SHA256 e06971e4fa1c0f6cbb97a2f12e9a9c315cb3013bf634b98eef015a40b633986f
MD5 d6b01100db7079f809c7b0e131642f4b
BLAKE2b-256 007d1ebf605adcde336c4771188ee3f6082d52e34f968dc749013672d0c93e13

See more details on using hashes here.

File details

Details for the file rubik_cube-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: rubik_cube-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 14.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.2 CPython/3.11.3

File hashes

Hashes for rubik_cube-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 8693fb238e2ffb8eb8380aa81f2f5c62c45dba30e598ead54a29981b08b78e30
MD5 4c55874697ad95820a7098d5d050e6df
BLAKE2b-256 7a1795dda81a529aa8f2750bd4395ecf68a5582fff8bf6b87b32ee5c3de80734

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page