You ask how to determine the data structure. Your choice is arbitrary as long as the work you define is working correctly. For example, you can imagine a cube as:
newCube[] := { {red, red, red, red, red, red, red, red, red}, {orange, orange, orange, orange, orange, orange, orange, orange, orange}, {yellow, yellow, yellow, yellow, yellow, yellow, yellow, yellow, yellow}, {green, green, green, green, green, green, green, green, green}, {indigo, indigo, indigo, indigo, indigo, indigo, indigo, indigo, indigo}, {purple, purple, purple, purple, purple, purple, purple, purple, purple} }
Then you can either define a twist (and possibly anti-twist), one for each movement (3 axes, 3 layers for turning on an axis, 2 directions for twisting, alternatively 6 axes, 3 layers for twisting on an axis), or two rotation and twist operations, and suppose you can combine them to generate effects such as inverseRotate[simpleTwist[rotate[cube], ...], ...] .
To figure out the code you need, you must have a map from your view to the real object. Perhaps it would be better to demonstrate an example for a coin that is either heads or tails:
newCoin[] := {heads} flipCoin[coin_] := {If[coin[[0]]==heads, tails, heads]}
This can be more difficult if it is not easy to imagine your object with basic data structures, such as lists. You can even imagine your cube using matrices such as:
newCube[] := { /red, red, red\ /orange, orange, orange\ |red, red, red| |orange, orange, orange| \red, red, red/, \orange, orange, orange/, ... }
But the way the matrices are stitched cannot be easily presented. Therefore, their order in the list is arbitrary.
If you are still confused, you can do this:
Give each slot in your representation an arbitrary number (in the worst case, you will call them 0 to 53, but you can be more elegant). Then, using a real Rubik's Cube, write these numbers on each face. Then, when you complete the operation, write down your new positions. This is called a permutation , which allowed you to allow movement / rotation in your semigroup data structure. As mentioned earlier, there are quite a lot of them (18), and you should write them down. Then you can have something like:
newCube[] := {0,1,2, 3,4,5, 6,7,8, ...53} permutations = { {12,15,0, 3,4,5, 6,7,8, ...}, (*figure these out yourself*) {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . }, {. . . } } twistCube[cube_, moveNumber_] := Permute[ cube, FindPermutation[permutations[[moveNumber]]] ]
You can optimize this with computer tricks, rather than calling FindPermutation every time by doing permutations = FindPermutation /@ {...}