« Go Back to Phase 1

You may have noticed that in the comments of the previous post, I declared that we could actually represent the STATE of a Rubik's Cube with the same data structure that we store the TRANSFORMATIONS of a Rubik's Cube with.

Here is briefly how that works, and this should be the most difficult bit to figure out in this initial series!

First, note that (as tested below) a solved cube's position, acting as a transformation, is actually an identity on the cube. By identity, I essentially mean that it does nothing (like add 0 or multiplying by 1!). That thought may help guide you while I explain the rest of this.

The position transformations are a bit easier to explain that the orientation bits. We'll start with those.

Given:

  • An existing Rubik's Cube
  • A transformation on that Rubik's Cube

Let's say that the cubeState we're passing in has an EdgePosition array of:

[| 1; 0; 2; 3; 4; 5; 6; 7 |] // all solved edges, except for 2 swapped

and that t (the transformation to be made on cubeState) has an EdgePosition array of:

 [| 2; 1; 0; 3; 4; 5; 6; 7 |]

This begs the question "What should be the edge position (some integer 0-11) at position 0?!"

[I honestly cannot figure out a way to say this in English. Please read the following line of code. Help?]

cubeState.EdgePositions.[t.EdgePositions.[0]]

The permutations of corners follow an exactly similar pattern.

Orientations work in a similar fashion, but:

  • We must grab the ORIENTATION rather than the position of the above scenarios.
  • We must reduce (via modulus) the number of twists down of each operation.
let Execute (cubeState: CubeState, t : CubeState) : CubeState = 
    { 
        EdgePositions = 
            [| 
                cubeState.EdgePositions.[t.EdgePositions.[0]]
                cubeState.EdgePositions.[t.EdgePositions.[1]]
                cubeState.EdgePositions.[t.EdgePositions.[2]]
                cubeState.EdgePositions.[t.EdgePositions.[3]]
                cubeState.EdgePositions.[t.EdgePositions.[4]]
                cubeState.EdgePositions.[t.EdgePositions.[5]]
                cubeState.EdgePositions.[t.EdgePositions.[6]] 
                cubeState.EdgePositions.[t.EdgePositions.[7]]
                cubeState.EdgePositions.[t.EdgePositions.[8]]
                cubeState.EdgePositions.[t.EdgePositions.[9]]
                cubeState.EdgePositions.[t.EdgePositions.[10]]
                cubeState.EdgePositions.[t.EdgePositions.[11]]
            |] 

        EdgeFlips = 
            [|
                cubeState.EdgeFlips.[t.EdgePositions.[0]] + t.EdgeFlips.[0]
                cubeState.EdgeFlips.[t.EdgePositions.[1]] + t.EdgeFlips.[1]
                cubeState.EdgeFlips.[t.EdgePositions.[2]] + t.EdgeFlips.[2]
                cubeState.EdgeFlips.[t.EdgePositions.[3]] + t.EdgeFlips.[3]
                cubeState.EdgeFlips.[t.EdgePositions.[4]] + t.EdgeFlips.[4]
                cubeState.EdgeFlips.[t.EdgePositions.[5]] + t.EdgeFlips.[5]
                cubeState.EdgeFlips.[t.EdgePositions.[6]] + t.EdgeFlips.[6]
                cubeState.EdgeFlips.[t.EdgePositions.[7]] + t.EdgeFlips.[7]
                cubeState.EdgeFlips.[t.EdgePositions.[8]] + t.EdgeFlips.[8]
                cubeState.EdgeFlips.[t.EdgePositions.[9]] + t.EdgeFlips.[9]
                cubeState.EdgeFlips.[t.EdgePositions.[10]] + t.EdgeFlips.[10]
                cubeState.EdgeFlips.[t.EdgePositions.[11]] + t.EdgeFlips.[11]                
            |] |> Array.map (fun z -> (z%2))

        CornerPositions = 
            [|
                cubeState.CornerPositions.[t.CornerPositions.[0]]
                cubeState.CornerPositions.[t.CornerPositions.[1]]
                cubeState.CornerPositions.[t.CornerPositions.[2]]
                cubeState.CornerPositions.[t.CornerPositions.[3]]
                cubeState.CornerPositions.[t.CornerPositions.[4]]
                cubeState.CornerPositions.[t.CornerPositions.[5]]
                cubeState.CornerPositions.[t.CornerPositions.[6]] 
                cubeState.CornerPositions.[t.CornerPositions.[7]]
            |]

        CornerTwists = 
            [|
                cubeState.CornerTwists.[t.EdgePositions.[0]] + t.CornerTwists.[0]
                cubeState.CornerTwists.[t.EdgePositions.[1]] + t.CornerTwists.[1]
                cubeState.CornerTwists.[t.EdgePositions.[2]] + t.CornerTwists.[2]
                cubeState.CornerTwists.[t.EdgePositions.[3]] + t.CornerTwists.[3]
                cubeState.CornerTwists.[t.EdgePositions.[4]] + t.CornerTwists.[4]
                cubeState.CornerTwists.[t.EdgePositions.[5]] + t.CornerTwists.[5]
                cubeState.CornerTwists.[t.EdgePositions.[6]] + t.CornerTwists.[6]
                cubeState.CornerTwists.[t.EdgePositions.[7]] + t.CornerTwists.[7]
            |] |> Array.map (fun z -> (z%3))

    }

Relevant Tests:

[<Fact>]
let ``Solved cube performed on self is solved`` () =
    solvedCube 
    |> Execute solvedCube 
    |> should equal solvedCube

// by definition!