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.
Cheers, Alan.
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
Last edited: