PROGRAM CUBESOLV; {Adapted from the book The Simple Solution to Rubik's Cube, by James G. Nourse.} TYPE ROW = STRING[7]; FACE = RECORD A : ROW; B : ROW; C : ROW END; VAR U : ABSOLUTE [$7000] FACE; D : ABSOLUTE [$7020] FACE; F : ABSOLUTE [$7040] FACE; B : ABSOLUTE [$7060] FACE; L : ABSOLUTE [$7080] FACE; R : ABSOLUTE [$70A0] FACE; S : FACE; OPTN, HOME, GO : CHAR; MOVES : STRING[255]; TEST, EDGE, CORN : STRING[3]; ERS : STRING[2]; DOWNERS : ARRAY [0 .. 8] OF STRING[12]; COUNT : INTEGER; {$I B:CUBEUTIL} {Include the text of file CUBEUTIL.} FUNCTION MATCH (S1, S2 : STRING) : BOOLEAN; {True if S1 and S2 contain the same elements in any order.} VAR L1, I : INTEGER; BEGIN L1 := LENGTH (S1); IF L1 = LENGTH (S2) THEN BEGIN MATCH := TRUE; FOR I := 1 TO L1 DO IF POS (S1[I], S2) = 0 THEN BEGIN MATCH := FALSE; EXIT END END ELSE MATCH := FALSE END; { \NP } PROCEDURE FLIP; VAR XPOS, YPOS : INTEGER; {Make a quarter-turn move for each character in the MOVES string. See Program Cube for list of legitimate moves.} BEGIN XPOS := (COUNT MOD 80) + 1; YPOS := (COUNT DIV 80) + 21; GOTOXY (XPOS, YPOS); WRITE (MOVES); COUNT := COUNT + LENGTH (MOVES); WHILE MOVES <> '' DO BEGIN OPTN := MOVES[1]; DELETE (MOVES, 1, 1); CASE OPTN OF 'R' : BEGIN CW (R); VUP (7) END; 'r' : BEGIN CCW (R); VDOWN (7) END; 'L' : BEGIN CW (L); VDOWN (1) END; 'l' : BEGIN CCW (L); VUP (1) END; 'U' : BEGIN CW (U); HLEFT (1) END; 'u' : BEGIN CCW (U); HRIGHT (1) END; 'D' : BEGIN CW (D); HRIGHT (3) END; 'd' : BEGIN CCW (D); HLEFT (3) END; 'F' : BEGIN CW (F); ICW (1) END; 'f' : BEGIN CCW (F); ICCW (1) END; 'B' : BEGIN CW (B); ICCW (3) END; 'b' : BEGIN CCW (B); ICW (3) END; 'V' : BEGIN VUP (4) END; 'v' : BEGIN VDOWN (4) END; 'H' : BEGIN HRIGHT (2) END; 'h' : BEGIN HLEFT (2) END; 'I' : BEGIN ICW (2) END; 'i' : BEGIN ICCW (2) END; 'X' : BEGIN CW (R); VUP (1); VUP (4); VUP (7); CCW (L) END; 'x' : BEGIN CCW (R); VDOWN (1); VDOWN (4); VDOWN (7); CW (L) END; 'Y' : BEGIN CW (F); ICW (1); ICW (2); ICW (3); CCW (B) END; 'y' : BEGIN CCW (F); ICCW (1); ICCW (2); ICCW (3); CW (B) END; 'Z' : BEGIN CCW (U); HRIGHT (1); HRIGHT (2); HRIGHT (3); CW (D) END; 'z' : BEGIN CW (U); HLEFT (1); HLEFT (2); HLEFT (3); CCW (D) END; END END END; { \NP } PROCEDURE UP_EDG; VAR K, I : INTEGER; BEGIN FOR I := 1 TO 4 DO BEGIN {Move up edges up to correct positions.} EDGE := CONCAT (U.B[4], F.B[4]); {FU} IF MATCH (CONCAT (U.C[4], F.A[4]), EDGE) THEN MOVES := '' ELSE {RU} IF MATCH (CONCAT (R.A[4], U.B[7]), EDGE) THEN MOVES := 'rf' ELSE {BU} IF MATCH (CONCAT (U.A[4], B.A[4]), EDGE) THEN MOVES := 'Uruf' ELSE {LU} IF MATCH (CONCAT (L.A[4], U.B[1]), EDGE) THEN MOVES := 'LF' ELSE {FR} IF MATCH (CONCAT (F.B[7], R.B[1]), EDGE) THEN MOVES := 'f' ELSE {BR} IF MATCH (CONCAT (R.B[7], B.B[1]), EDGE) THEN MOVES := 'rrfrr' ELSE {BL} IF MATCH (CONCAT (B.B[7], L.B[1]), EDGE) THEN MOVES := 'LLFLL' ELSE {FL} IF MATCH (CONCAT (F.B[1], L.B[7]), EDGE) THEN MOVES := 'F' ELSE {DF} IF MATCH (CONCAT (F.C[4], D.A[4]), EDGE) THEN MOVES := 'FF' ELSE {DR} IF MATCH (CONCAT (R.C[4], D.B[7]), EDGE) THEN MOVES := 'dff' ELSE {DB} IF MATCH (CONCAT (D.C[4], B.C[4]), EDGE) THEN MOVES := 'DDFF' ELSE {DL} IF MATCH (CONCAT (D.B[1], L.C[4]), EDGE) THEN MOVES := 'DFF' ELSE BEGIN DISPLAY; GOTOXY (40, 1); WRITE (ERS, 'Up edge positioning error.'); READ (GO) END; FLIP; {Orient up edges properly and rotate cube to next position.} IF ((U.C[4] = U.B[4]) AND (F.A[4] = F.B[4])) THEN MOVES := 'Z' ELSE IF ((U.C[4] = F.B[4]) AND (F.A[4] = U.B[4])) THEN MOVES := 'fUluZ' ELSE BEGIN DISPLAY; GOTOXY (40, 1); WRITE (ERS, 'Up edge orientation error.'); READ (GO) END; FLIP END; IF GO = ' ' THEN DISPLAY; GOTOXY (35, 15); WRITE ('Up edges finished in ', COUNT, ' moves.'); IF GO = ' ' THEN READ (GO); END; { \NP } PROCEDURE UP_CRN; VAR I, J, K : INTEGER; BEGIN FOR I := 1 TO 4 DO BEGIN {Position up corners.} CORN := CONCAT (U.B[4], F.B[4], R.B[4]); {Look on the down face first for desired cublet.} J := 1; K := 0; WHILE (K = 0) AND (J < 5) DO BEGIN J := J + 1; TEST := CONCAT (F.C[7], D.A[7], R.C[1]); IF MATCH (TEST, CORN) THEN BEGIN K := 9; {Move it up, orienting it at the same time.} IF R.C[1] = U.B[4] THEN MOVES := 'rdRZ' ELSE IF F.C[7] = U.B[4] THEN MOVES := 'FDfZ' ELSE MOVES := 'rDRFDDfZ' END ELSE MOVES := 'D'; FLIP END; {If it wasn't on the down face, look on the up face.} IF K <> 9 THEN BEGIN J := 1; WHILE (K = 0) AND (J < 5) DO BEGIN TEST := CONCAT (U.C[7], F.A[7], R.A[1]); IF MATCH (TEST, CORN) THEN K := J ELSE BEGIN J := J + 1; MOVES := 'u'; FLIP END END; {If positioned correctly, orient it correctly.} IF K = 1 THEN BEGIN IF F.A[7] = U.B[4] THEN MOVES := 'FDDfrDDRZ' ELSE IF R.A[1] = U.B[4] THEN MOVES := 'rddRFddfZ' ELSE MOVES := 'Z'; END ELSE IF K = 0 THEN BEGIN DISPLAY; GOTOXY (40, 1); WRITE (ERS, 'Up corner positioning error.'); READ (GO) END {Wrong up corner. Move it down, then under desired position, then up.} ELSE BEGIN MOVES := 'rdRD'; FOR K := 2 TO J DO MOVES := CONCAT (MOVES, 'U'); FLIP; IF R.C[1] = U.B[4] THEN MOVES := 'rdRZ' ELSE IF F.C[7] = U.B[4] THEN MOVES := 'FDfZ' ELSE MOVES := 'rDRFDDfZ'; END; FLIP END END; IF GO = ' ' THEN DISPLAY; GOTOXY (35, 16); WRITE ('Up corners finished in ', COUNT, ' moves.'); IF GO = ' ' THEN READ (GO); END; { \NP } PROCEDURE VRT_EDG; {Position and orient vertical edges.} VAR I, J, K : INTEGER; BEGIN FOR I := 1 TO 4 DO BEGIN EDGE := CONCAT (F.B[4], R.B[4]); TEST := CONCAT (F.B[7], R.B[1]); IF MATCH (EDGE, TEST) THEN {Right position.} BEGIN IF F.B[7] = F.B[4] THEN MOVES := 'Z' {Correct} ELSE MOVES := 'rDRDFdfDrDRDFdfZ' {Backward} END ELSE {Move it to the bottom if necessary.} BEGIN FOR J := 1 TO 3 DO BEGIN MOVES := 'Z'; FLIP; TEST := CONCAT (F.B[7], R.B[1]); IF MATCH (TEST, EDGE) THEN BEGIN MOVES := 'rDRDFdf'; FLIP END END; MOVES := 'Z'; FLIP; {Move it to the front.} J := 0; TEST := CONCAT (F.C[4], D.A[4]); WHILE (NOT MATCH (TEST, EDGE)) AND (J < 4) DO BEGIN J := J + 1; MOVES := 'D'; FLIP; TEST := CONCAT (F.C[4], D.A[4]); END; {Move it into position, oriented correctly.} IF (F.C[4] = F.B[4]) AND (D.A[4] = R.B[4]) THEN MOVES := 'drDRDFdfZ' ELSE IF (F.C[4] = R.B[4]) AND (D.A[4] = F.B[4]) THEN MOVES := 'ddFdfdrDRZ' ELSE BEGIN DISPLAY; GOTOXY (40, 1); WRITE (ERS, 'Vertical edge positioning error.'); READ (GO) END; END; FLIP END; IF GO = ' ' THEN DISPLAY; GOTOXY (35, 17); WRITE ('Vertical edges finished in ', COUNT, ' moves.'); IF GO = ' ' THEN READ (GO); END; { \NP } PROCEDURE DOWN_CRN; {Position down corners.} VAR I, J, K, M, RIGHT : INTEGER; DFR, DBL : BOOLEAN; DTEST : STRING[12]; BEGIN RIGHT := 0; {Twist and turn until two cubelets are found properly positioned.} WHILE RIGHT < 2 DO BEGIN MOVES := 'D'; FLIP; RIGHT := 0; J := 0; WHILE (RIGHT < 2) AND (J < 4) DO BEGIN J := J + 1; MOVES := 'Z'; FLIP; CORN := CONCAT (L.B[4], F.B[4], D.B[4]); TEST := CONCAT (L.C[7], F.C[1], D.A[1]); IF MATCH (CORN, TEST) THEN RIGHT := RIGHT + 1 END END; CORN := CONCAT (R.B[4], F.B[4], D.B[4]); TEST := CONCAT (R.C[1], F.C[7], D.A[7]); DFR := MATCH (CORN, TEST); CORN := CONCAT (L.B[4], B.B[4], D.B[4]); TEST := CONCAT (L.C[1], B.C[7], D.C[1]); DBL := MATCH (CORN, TEST); IF DFR AND (NOT DBL) THEN {Two incorrect cubelets at back.} MOVES := 'zzrdRFDfrDRDD' ELSE IF (NOT DFR) AND DBL THEN {Two incorrect cubelets at right.} MOVES := 'zrdRFDfrDRDD' ELSE IF (NOT DFR) AND (NOT DBL) THEN {Two incorrect cubelets diagonally.} MOVES := 'zrdRFDDfrDRD'; FLIP; {Orient down corners.} J := 0; M := 0; REPEAT {Generate a string of everywhere a down color on a down corner cubelet could be.} DTEST := CONCAT (D.A[1], D.A[7], D.C[1], D.C[7], L.C[1], L.C[7], F.C[1], F.C[7], R.C[1], R.C[7], B.C[1], B.C[7]); FOR I := 1 TO 12 DO IF DTEST[I] <> 'D' THEN DTEST[I] := ' '; DOWNERS[8] := DTEST; K := 0; {Check it out against the "pictures" from the book.} WHILE DTEST <> DOWNERS[K] DO K := K + 1; IF K = 0 THEN {It is now correct.} J := 0 ELSE IF K = 8 THEN {Not this orientation. Rotate cube and try again.} BEGIN MOVES := 'Z'; FLIP END ELSE {Found it! Run the orientation sequence and iterate.} BEGIN J := 0; M := M + 1; MOVES := 'rdRdrddRdd'; FLIP END; J := J + 1; UNTIL (K = 0) OR (J > 8) OR (M > 3); IF (J > 8) OR (M > 3) THEN BEGIN GOTOXY (40, 2); WRITE ('K, J, M:', K:3, J:3, M:3); DISPLAY; GOTOXY (40, 1); WRITE (ERS, 'Down corner orientation errror.'); READ (GO); END; {Put front face in front, just for a little class.} WHILE F.B[4] <> 'F' DO BEGIN MOVES := 'Z'; FLIP END; IF GO = ' ' THEN DISPLAY; GOTOXY (35, 18); WRITE (ERS, 'Down corners finished in ', COUNT, ' moves.'); IF GO = ' ' THEN READ (GO); END; { \NP } PROCEDURE DOWN_EDG; LABEL 100, 110; VAR I, J : INTEGER; BEGIN 100: REPEAT J := 0; {Find number of properly positioned edges.} FOR I := 1 TO 4 DO BEGIN EDGE := CONCAT (F.B[4], D.B[4]); TEST := CONCAT (F.C[4], D.A[4]); IF MATCH (EDGE, TEST) THEN BEGIN J := J + 1; IF J < I THEN GOTO 110 {There's only one, and this is it.} END; MOVES := 'Z'; FLIP END; 110: IF J < 4 THEN {If there is one, it's in front. If not, it doesn't matter which is front.} BEGIN MOVES := 'vDVDDvDV'; FLIP; END; UNTIL J = 4; {All in position, now orient them.} J := 0; FOR I := 1 TO 4 DO BEGIN IF F.C[4] = F.B[4] THEN J := J + 1; MOVES := 'Z'; FLIP END; IF J = 4 THEN {Done! How lucky can you get?} BEGIN END ELSE IF J = 0 THEN {Seldom happens, but worth considering.} BEGIN MOVES := 'vDDVDDvDVDDvDDVd'; FLIP {Done!} END ELSE {OK, where are they?} BEGIN J := 0; WHILE D.C[4] = 'D' DO {Put a bad one at the back.} BEGIN MOVES := 'z'; FLIP; J := J + 1; IF J > 4 THEN BEGIN DISPLAY; GOTOXY (40, 1); WRITE (ERS, 'Down edge error, try again.'); READ (GO); GOTO 100 END END; {Make sure there's a good one on the right.} IF R.C[4] = 'D' THEN BEGIN MOVES := 'Z'; FLIP END; IF F.C[4] = 'D' THEN {Bad ones front and back, whip out the longest sequence used.} BEGIN MOVES := 'vDVDvDVDvDDVDvDVDvDVDD'; FLIP {Done (whew).} END ELSE {Bad ones back and left. Make some progress, rotate, and re-position.} BEGIN MOVES := 'vDVdvdVdvDDVzvDVDDvDV'; FLIP; END END; GOTOXY (35, 19); WRITE ('Down edges finished in ', COUNT, ' moves.'); END; { \NP } BEGIN {CUBESOLV} HOME := CHR ($1E); {Top left corner.} WRITE (HOME, CHR ($1B), 'Y'); {Go to home and clear screen.} ERS := CONCAT (CHR ($1B), 'T'); {Erase to end of line.} COUNT := 0; DISPLAY; GOTOXY (1, 21); WRITELN ('Initial position. Press any key to continue.'); WRITE ('(Press blank to pause after each step.)'); READ (GO); WRITE (HOME, CHR ($1B), 'Y'); {Test strings for orienting the down corners. One through seven correspond to the numbered pictures in the book.} DOWNERS[0] := 'DDDD '; {Correct} DOWNERS[1] := 'D D D D'; {One down color on down face; that one at} DOWNERS[2] := 'D D D D '; {front left.} DOWNERS[3] := ' D D DD'; {No down colors on down face; down colors} DOWNERS[7] := ' DD DD '; {on left and right at front.} DOWNERS[4] := ' D D D D'; {Two down colors on down face; down color} DOWNERS[5] := ' DD DD '; {on front at left} DOWNERS[6] := ' DD D D '; DOWNERS[8] := ' '; {This is a trick to make sure there is alway s} {one that matches. It gets filled in later.} {Put 'U' on up face and 'F' on front face, just to add a little class.} IF F.B[4] = 'U' THEN MOVES := 'X' ELSE IF D.B[4] = 'U' THEN MOVES := 'XX' ELSE IF B.B[4] = 'U' THEN MOVES := 'x' ELSE IF L.B[4] = 'U' THEN MOVES := 'Y' ELSE IF R.B[4] = 'U' THEN MOVES := 'y' ELSE MOVES := ''; FLIP; WHILE F.B[4] <> 'F' DO BEGIN MOVES := 'Z'; FLIP END; UP_EDG; {Do the up edges first.} UP_CRN; {Then the up corners.} VRT_EDG; {Then the vertical edges.} DOWN_CRN; {Penultimately, the bottom corners.} DOWN_EDG; {Last but not least, the bottom edges.} {Last class.} WHILE F.B[4] <> 'F' DO BEGIN MOVES := 'Z'; FLIP END; DISPLAY; GOTOXY (1, 1); END.