Rubik's Cube Simulator for PICaxe ;-)

AllyCat

Senior Member
Hi,

Inspired by posts #5 - 7 and 11 in this Recent Thread , here is a simple Simulator to replicate the operation of a Rubik's Cube. The User Interface is (literally) very basic ASCII Art because the Program was written as an initial "Engine" that might be used in a Cube-Solving Program, but I don't know if I will ever complete that. I'm really only interested in attempting to do it with an M2, probably a 14M2. This simulator should run easily in an 08M2 (adequately fast at 4 MHz), but would need the Table memory changed to the internal EEPROM . ;)

The basic Engine uses less than 300 bytes of Program memory, with the code here adding about 200 bytes to display the "Map" data on the PE Terminal and another 160 bytes for additional (optional) commands. The (single digit) commands are as defined for the "Safecracker" method of solving the Cube, but they can be extended and IMHO "improved". Personally, I would prefer the Anticlockwise ("prime") commands to be +1 not -1 so that all the Higher bits are consistent between the two versions of the move. However, the Anticlockwise commands can be considered Optional anyway, because each can be substituted by Three Clockwise Commands, as do Two for the "180 degrees" rotations here. Similarly, only the "Centre Layer" instructions may need to be added, to allow "Wide" commands or Complete Cube rotations to be constructed by combining two or three of the basic routines.

The initial state of the Cube faces is stored as ASCII characters in {Table} EEPROM , so they can be set as desired. I used a mixture of lower and upper case letters to act as simple Face Orientation markers, and to avoid the use of similar character shapes to represent different "Colours". But I can't give more details now because I've reached the Forum's 10,000 character limit again. :(
Code:
; Rubik's Cube Simulator,  AllyCat  October 2021
#picaxe 20m2       ; And most others (with slight modifications)
;#no_data      ; Can Uncomment after tables have been downloaded
#terminal 4800
;#DEFINE SIMPLE            ; 640/475 program bytes (including "show" = ~200 bytes)
symbol tmp = 50            ; Temporary storage for Rotations (40,60,70,80,90 are unused)

;           83 82 81        <--BACK
;          ==========
;     _---| 41 42 43 |---_  <--TOP (4n)
;    / _--| 44 45 46 |--_ \       BACK (8n)
;   / / _-| 47 48 49 |-_ \ \      43 42 41
;  / / /  |----------|  \ \ \    ==========
;51 52 53 | 61 62 63 | 71 72 73 | 81 82 83 | 51
;54 55 56 | 64 65 66 | 74 75 76 | 84 85 86 | 54
;57 58 59 | 67 68 69 | 77 78 79 | 87 88 89 | 57
;  \ \ \  |----------|  / / /    ==========
;   \ \ ^-| 91 92 93 |-^ / /      99 98 97
;    \ ^--| 94 95 96 |--^ /  ^RIGHT (7n)   LEFT (5n)
;     ^---| 97 98 99 |---^  <--DOWN (9n)  FRONT (6n)
;          ==========
;           89 88 87

table 41,("wWwwWwwww")    ; Top   White   STANDARD CUBE   Starting Labels
table 51,("gGggGgggg")    ; Left  Green
table 61,("RrRRRRRRR")    ; Front Red
table 71,("bBbbBbbbb")    ; Right Blue
table 81,("oOooOoooo")    ; Back  Orange
table 91,("YyYYYYYYY")    ; Down  Yellow
pause 2000
tablecopy 31,69
; Rotation Lookup Data in Table memory (or could be in EEPROM)
symbol FRNT = 100       ; Table Address for Front Face rotations
table FRNT,(tmp,61,63,69,67,tmp,62,66,68,64,tmp)        ; Continued in next line
table (47,71,93,59,tmp,48,74,92,56,tmp,49,77,91,53)    ; Terminator byte in next block
symbol FRNTE = 125      ; Address of (shared) End terminator byte
symbol CNTR = 125        ; Centre layer aka "Standing" between F & B (Roll)
table CNTR,(tmp,44,72,96,58,tmp,45,75,95,55,tmp,46,78,94,52)
symbol CNTRE = 140
symbol TP = 140     ; Top face
table TP,(tmp,41,43,49,47,tmp,42,46,48,44,tmp)
table (83,73,63,53,tmp,82,72,62,52,tmp,81,71,61,51)
symbol TPE = 165
symbol RGT = 165   ; Right face
table RGT,(tmp,71,73,79,77,tmp,72,76,78,74,tmp)
table (49,81,99,69,tmp,46,84,96,66,tmp,43,87,93,63)
symbol RGTE = 190
symbol LFT = 190     ; Left face
table LFT,(tmp,51,53,59,57,tmp,52,56,58,54,tmp)
table (41,61,91,89,tmp,44,64,94,86,tmp,47,67,97,83)
symbol LFTE = 215
symbol DN = 215           ; Down face
table DN,(tmp,91,93,99,97,tmp,92,96,98,94,tmp)            ; Continued in next line
table (67,77,87,57,tmp,68,78,88,58,tmp,69,79,89,59)
symbol DNE = 240
symbol MID = 240   ; Middle Layer between L and R faces (Pitch)
table MID,(tmp,48,82,98,68,tmp,45,85,95,65,tmp,42,88,92,62)
symbol MIDE = 255      ; Remaining bytes need a WORD pointer, wraps back to zero or is optional
symbol BCK = 255        ; Only Written by NEXT line!
table BCK,(tmp,81,83,89,87,tmp,82,86,88,84,tmp)            ; Back face
table (43,51,97,79,tmp,42,54,98,76,tmp,41,57,99,73)
symbol BCKE = 280
symbol EQU = 280      ; Equator between Top and Down (Yaw)
table EQU,(tmp,64,74,84,54,tmp,65,75,85,55,tmp,66,76,86,56)
symbol EQUE = 295       ; End of tables (may wrap to address 39)              
table (tmp)      ; Terminating marker byte
; "Holding the cube in front of you, imagine the top face is 2, the right face is 4,
; the face straight in front of you is 6, left face 8, and bottom face 10.
; Forget the back face that's furthest from you. It doesn't need a number" (11-12).
; Anticlockwise = -1 , Cube Roll,Pitch,Yaw = 19-24 , Centre,Middle("Stand"),Equator = 13-18
disconnect      ; (Forced) by use of SERRXD command
call show      ; Send Cube Map to the Terminal (optional here)
do
  sertxd(cr,"Enter S/C number,0=End:")    ; Zero to Exit or "Raw" number 1 - 24
  serrxd b1 : sertxd(#b1)     ; Get and report number
#IFDEF SIMPLE
  on b1 gosub done,TopA,Top,RightA,Right,FrontA,Front,LeftA,Left,DownA,Down,BackA,Back    ; 0-12
#ELSE      ; Rotations of full cube and Middle layers
  on b1 gosub done,TopA,Top,RightA,Right,FrontA,Front,LeftA,Left,DownA,Down,BackA,Back,_
  CentreA,Centre,MiddleA,Middle,EquatA,Equat,RollA,Roll,SpinA,Spin,TurnA,Turn                ; 13-24
#endif
  call show      ; Display the map of cube faces
loop
; 1/2 = Rotate Top Face A/CW             ; 3/4 = Rotate Right Face A/CW
; 5/6 = Rotate Front face A/CW           ; 7/8 = Rotate Left Face A/CW
; 9/10 = Rotate Down Face A/CW           ; 11/12 = Rotate Back Face A/CW
; 13/14 = Rotate Centre layer A/CW       ; 15/16 = Rotate Middle layer A/CW       ; Additional options
; 17/18 = Rotate Equator layer A/CW    ; 19/20 = Roll whole cube A/CW
; 21/22 = Pitch whole cube A/CW         ; 23/24 = Yaw whole cube A/CW  
done:
reconnect             ; Cancel the SERRXD
sertxd(cr,lf,"Done")
stop
; SubRoutines:
Top:   ; 2
w1 = TPE     : goto Faceb            ; Then use its Return
TopA:     ; 1  
w1 = TP        : goto FaceAb
Left:    ; 8
w1 = LFTE     : goto Faceb
LeftA:     ; 7
w1 = LFT      : goto FaceAb
Right:     ; 4
w1 = RGTE     : goto Faceb  
RightA:    ; 3
w1 = RGT     : goto FaceAb
Front:    ; 6
w1 = FRNTE : goto Faceb
FrontA:       ; 5
w1 = FRNT     : goto FaceAb
Down:        ; 10
w1 = DNE     : goto Faceb
DownA:      ; 9
w1 = DN        : goto FaceAb            ; End of SafeCracker codes
Back:
w1 = BCKE     : goto Faceb
BackA:
w1 = BCK     : goto FaceAb            ; Or could Fall Through
FaceAb:                                    ; Entry for 26 bytes copied (21 squares)
b1 = 25
FaceA:     ; Number of byte movements (-1) in b1
bptr = tmp
for b1 = b1 to 0 step -1            ; -Step needed for PE6 (Or could use a terminator byte?)
  ReadTable w1, b4
  peek b4 , @bptr
  Inc w1
  bptr = b4
next
return
Faceb:
b1 = 25
Face:         ; Clockwise rotation of face
bptr = tmp
for b1 = b1 to 0 step -1
  ReadTable w1, b4
  peek b4 , @bptr
  Dec w1
  bptr = b4
next
return

#IFnDEF SIMPLE    ; Additional Rotations
Centre:
w1 = CNTRE    : b1 = 15     : goto Face   ; Centre Level 2 (Roll)
CentreA:
w1 = CNTR     : b1 = 15     : goto FaceA
Middle:
w1 = MIDE    : b1 = 15     : goto Face   ; Middle Level 2 (Pitch)
MiddleA:
w1 = MID     : b1 = 15     : goto FaceA
Equat:
w1 = EQU    : b1 = 15    : goto FaceA    ; "Equator" Level 2 (Yaw)
EquatA:
w1 = EQUE    : b1 = 15    : goto Face
Roll:        ; Rotate Whole cube about Front Face (or BackA)
call BackA    : call Centre     : goto Front        ; Then use its Return
RollA:
call Back     : call CentreA : goto FrontA
Spin:         ; Rotate Whole cube about Right Face (Pitch)
call LeftA    : call Middle     : goto Right        ; Then use its Return
SpinA:
call Left    : call MiddleA : goto RightA        ; Then use its Return
Turn:           ; Rotate (Twist) Whole Cube about vertical axis (Yaw)
call DownA : call Equat     : goto Top
; call Spin : call RollA : goto SpinA            ; Slower but doesn't need Equator routine 
TurnA:     ; Or name as Twist?
call Down    : call EquatA    : goto TopA
; call Spin : call Roll : goto SpinA   ; Slower but doesn't need Equator routine
#endif    ; /Simple

show:            ;    ~200 bytes (could be reduced to ~100)
bptr = 41
sertxd(cr,",-> ",@bptrinc,@bptrinc,@bptrinc," <-,")     ; Could use loop (without "arrows")
sertxd(cr,"|   ",@bptrinc,@bptrinc,@bptrinc,"   |")
sertxd(cr,"|   ",@bptrinc,@bptrinc,@bptrinc,"   |")
bptr = 51 : for b1 = 1 to 3
  b10 = @bptr
  sertxd(cr,@bptrinc,@bptrinc,@bptrinc)  : bptr = bptr + 7        ; Use subroutine or loop ?
  sertxd(" ",@bptrinc,@bptrinc,@bptrinc) : bptr = bptr + 7        ; ~ 10 bytes/line
  sertxd(" ",@bptrinc,@bptrinc,@bptrinc) : bptr = bptr + 7
  sertxd(" ",@bptrinc,@bptrinc,@bptrinc) : bptr = bptr - 30
next
bptr = 91
sertxd(cr,"|   ",@bptrinc,@bptrinc,@bptrinc,"   |")                ; Could use loop
sertxd(cr,"|   ",@bptrinc,@bptrinc,@bptrinc,"   |")
sertxd(cr,"'-> ",@bptrinc,@bptrinc,@bptrinc," <-'")
sertxd(cr,"    ===")
return
Cheers, Alan.
 
Last edited:

AllyCat

Senior Member
Hi,

Here are a few more details which I had to omit above because of the forum's 10,000 character limit, and also some further developments. I was surprised that nearly all the Rubik "World Records" have been set within the last few years, not just the "robotic" ones but the "human" ones as well. To "solve" a randomised cube, a typical computing time is now ~10ms, the mechanical (robotic) record is around 350 ms and the human 3.5 seconds ! Obviously those figures are totally out of reach for an aged, amateur hobbyist with a PICaxe, so my particular interest is to aim only for a "minimalistic" design. That was confirmed when I came across this "off the shelf" product, where the main component parts appear to cost (much) less than $100 each (the Video is not overly long and the "demo" starts at about 2 minutes) :


I'm not sure which "World Record" it claims to have broken, but it's impressive anyway (especially the "Bluetooth Cube"). As outlined above (and the linked thread) my present minimalist target has a total planned component inventory of : one 14M2, 4 Servos, 3 LEDs (RGB), two LDRs, a 5V PSU and A childhood box of Meccano (pre-Lego). ;)

But back to my "Rubik Simulator" program which has grown in size to almost fill an M2 slot (but around 30% of the Program is used only to create the "image" map of the cube to send to the user). The original program above accepted about 25 "numerical" commands from the PICaxe Terminal in "Raw" (i.e. 1 - 2 digit) mode. That grew to about 35 commands which were getting difficult to remember, even though I wrote the Program! Therefore, I changed to single ASCII character commands, using the PE6 terminal "ASCII" mode (or characters within quotes " " in PE5), and have now allocated ALL of the Alphanumeric characters to some function or another. Conveniently, in the PICaxe Simulator you can "stack up" command characters in the Terminal's Transmit buffer, press the Enter key and go away to make a cup of coffee. :) However, once loaded into a real PICaxe, the response is more than fast enough, faster than you can move a physical cube. But as there is no serial "handshaking", it's limited to single characters, or the few "parametric" commands such as kW (to select the White tile Colour ; C was already used to specify a Centre Layer move), followed by an <Enter> . Thus it's suitable for Program/Algorithm testing and might be used as the "engine" in a second slot of a "Cube Solving" program. The present "UI" is intended for "human" use but can be easily stripped down to a more "computer readable" form (i.e. without spaces, braces and "hints", etc.).

Obviously F(ront), R(ight), etc. were easy, but I chose lower case characters (i.e. the "default" keyboard setting) for Clockwise moves and then use the Shift Key to select the "Primed" (Anti-Clockwise) moves. The alternative T(op) was used in place of the normal U(p) which allowed for U(ndo), but it's very easy to change the "mapping" for any or all of the commands in the program. The "Safecracker" code numbers were retained (or actually reintroduced) with the "10" replaced by "0" (zero). The program can now Log (and report) a sequence of up to about 350 moves, but the Undo (and Redo) were then easy to implement and have proved remarkably useful. I believe the X, Y and Z (whole cube rotation) commands are consistent with the standard notation, not very memorable but Yaw meets most purposes (and XX does the same as ZZ). The present cube map can be saved and recovered in EEPROM or RAM with P(ut), G(et) , H(istory) and O(utput), etc. Another major function can now fiNd any "Tile" colour at either CorNers or Edges (lower case n). It returns each mapped "Address", the layer number (0-4), the adjacent edge colour(s) and the centre tile colour of these adjacent face(s). It can be selected to search for just one or for several tiles in a defined address range (the default is all 4 specified tiles over the whole cube), which should be sufficient data for a "Solving" Algorithm or Program, running in another Slot or in a co-processor.

The internal "Memory Map" of the cube faces is quite basic and I was concerned that it might be a "dead end", because most of the simpler solution methods work with "Layers" rather than Faces. However, it has the advantages that all "Edge Tiles" have Even-numbered Addresses, Corners are Odd and Centre Tiles end in "5" (i.e. all addresses divisible by 5 are "inactive"). I started at RAM address 40 (or 41) to avoid overlaying the (M2) register addresses (up to 27) but also to allow every address to be represented by a printable ASCII character number (i.e. > 32) and avoiding 3-digit (decimal) values. The "colours" (and commands) are each stored as a complete ASCII value (or a full byte in practice) so there is scope for additional attributes to be stored. But 6 x 6 x 6 (= 216) is less than 256 so all the colours at a corner could be stored in a single byte, thus a whole cube could be stored as 3 Layers in a 24 byte block of memory if desired. In fact 6 x 6 x 7 is less than 256, so also "Grey" could be allocated (often seen in tutorials) to indicate a "Do Not Care" , "Do Not Touch" or "Do Not Know" status, if required. Multiplication or Division by 6 is not very "binary friendly" but makes very little difference to the computational speed of a PICaxe.

The transmitted "Cube Map" is now larger with a more realistic horizontal "Tile" spacing and a number of command "hints" (since there is no "Help" data or a User Manual). However, this can look quite "cluttered", so the hints could be removed when the user is familiar with them, giving a bonus of freeing up some Program Memory. The overall program is quite modular (using many subroutines) so it can be expanded, contracted or rearranged, and the routines or methods might be re-purposed for other User-Interface applications. But that must be enough for now, the latest version of the program is far too large to fit in-line, so must be attached as .BAS file. I'll just show a sample of the output format (the @ symbol prefixes each corresponding "Face" colour) here:

Code:
Quit/ASCII?: ?
    ,-->| Y G w |<--.  [Top <-T-t->,]
   / ,->| R w G |<-. \      w [Y~E~W]
  / / ,>| b R b |<. \ \-> w G Y
+-|-|-|-+-------+-|-|-|-+-------+
: G G Y : o Y o : w Y R : b w R : G
: b G Y : o R R : w b R : b o o : b G
: o o o : w Y Y : R w G : w w Y : o
+-|-|-|-+-------+-|-|-|-+-------+
  \ \ '>| G b b |<' / /-> R b G
   \ '->| G Y o |<-' /      Y
    '-->| G b R |<--' <-d-D^
  ^l-L^   ^f-F^   ^r-R^  ^-b-B^
  [X-x]   [z-Z]  [X~M~P] [Z~C~S]
Quit/ASCII?: N
(43:L4= w@w)[81=b@o + 73=R@b]
(67:L1= w@R)[91=G@Y + 59=o@G]
(71:L3= w@b)[63=o@R + 49=b@w]
(87:L1= w@o)[99=R@Y + 79=G@b]
Quit/ASCII?:
Cheers, Alan.
 

Attachments

Technoman

Senior Member
Congratulations for your work demonstrating the power of table+pointer.
Moreover, I am still being puzzled by this guy :
Any clue?
 

hippy

Technical Support
Staff member
Moreover, I am still being puzzled by this guy ... Any clue?
Very impressive. Especially when the trick is hiding in plain sight.

Two techniques of the magician are at play here; convincing the audience they will be seeing something other than what is actually being done, and distraction, having them see what one wants them to see and not see what is actually happening.
 

AllyCat

Senior Member
Hi,
Congratulations for your work demonstrating the power of table+pointer.
Moreover, I am still being puzzled by this guy :
Yes, an advantage of Tables is that they can be very fast. In particular, the COPYTABLE can get the PE Simulator up and running much more quickly than a string of POKEs. But it's a pity that COPYTABLE doesn't have separate Source and Destination pointers, which would be very useful for maintaining "Menu" based programs (e.g 50 x 10-byte options), etc..

It's not clear if the Rubik cubes in that video have been pre-prepared, but I believe that Blindfold Solving is just a branch of "Speedcubing" and the World Record is almost fifty consecutive blindfolded solves. Theoretically, it's been proved that the cube can be solved in 20 moves, but more realistically perhaps 40 - 50, which might be similar to remembering the sequence of a deck of playing cards (apparently a method used by good poker players). But there are also commercial "Braille" cubes (with different textures for the six colours) or the Bluetooth Cube in post #2 above transmits it's position, which might be intercepted to deliver audio (or vibrational) "prompts" if it were a "trick". Towards the end of that video, the "human" appears to "randomise" two cubes with identical patterns and then competes with the "Robot" in a race to solve them.

IMHO, more impressive than the (human) World Record of solving a cube in 3.5 seconds (which might be attributed to "luck") is 5 consecutive solves in an average of about 5 seconds each (highest and lowest times omitted). Presumably the reason that most of the (human) records have been set quite recently is the vast improvements in the mechanics of the "Speed" cubes; their bearings can be manually tensioned to the user's preference and they have built-in magnets to align the individual cubelets, etc.. Watch the techniques and dexterity of a "Speedcuber" in action, in slow motion.

I think the point of the introduction to the "Talent" video is that by exchanging or covering two of the "Stickers" on the cube it can be made impossible to solve. The centre squares of each face are always fixed in relation to each other and the colours of opposite faces can never meet at an edge. Apparently the colouring of the cubes was standardised in about 1988, but my cube pre-dates that and has its white and yellow sides swapped. ;)

Cheers, Alan.
 

hippy

Technical Support
Staff member
It's not clear if the Rubik cubes in that video have been pre-prepared, but I believe that Blindfold Solving is just a branch of "Speedcubing" and the World Record is almost fifty consecutive blindfolded solves.
It does seem this is a case of simply reversing the moves to solve pre-prepared cubes.

If one watches the rotations when the camera is on the cube it is mostly a repeating set of limited face turns, which makes the single-handed solving less impressive than it first appears. While the three cubes are different I would guess they are not actually that different; a few turns at the start and they would be the same. So it's probably the same reversal every time, just one sequence to remember.

It is easy to be distracted by the whirling colours and not notice that other faces and areas of the cube are not being altered all that often.

Blindfolded solving is a neat trick but some Speed-Cubers take exception to it being characterised as "solving the cube" when it's merely remembered moves, when their impressive speeds in solving actual random cubes are set against "but this guy does it quicker blindfolded".
 

erco

Senior Member
It's faster for me to just unpeel the various colored square stickers and replace them on matching sides.

Then I sleep like a baby, guilt-free.
 

Jeff Haas

Senior Member
It's obvious that he's just reversing the moves using memorized patterns. He never picks up the cubes and studies them to see what pattern each scrambled cube is in.
If he was really solving them blindfolded, he would have handed them out to the panel, had all three cubes mixed up, then put them down on the table, studied each for a moment, and then put on the blindfold and solved them.
 

Buzby

Senior Member
I agree, the cubes were not scrambled by a third party, so a preset sequence of moves is all that is needed to 'solve' them. It might even be the same sequence for all three cubes. Because we never saw all sides before he touched them, a simple rotation might have revealed that the cubes were identical.

I have seen a real cube solver do this, but he studied the cube for about a minute before being blindfolded, and he was much slower, although he did seem to make less moves than in this video.
 

Jeff Haas

Senior Member
Actually, the sequence is set up perfectly for the maximum theatrical effect. The first cube takes the longest, then the second cube is faster, and the third cube is pretty fast but one-handed. So it may be the same sequence of moves but shorter on the second and third cubes. Plus he's learned it one-handed, so either that's a different sequence just for one hand, or his left hand isn't really doing much in the earlier sequences.

It's still fun and impressive, but as Hippy said, there's the impression of what's happening, and what's really happening.
 
Top