Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
CubeSolving.m
/* |
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
("Apple") in consideration of your agreement to the following terms, and your |
use, installation, modification or redistribution of this Apple software |
constitutes acceptance of these terms. If you do not agree with these terms, |
please do not use, install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and subject |
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
copyrights in this original Apple software (the "Apple Software"), to use, |
reproduce, modify and redistribute the Apple Software, with or without |
modifications, in source and/or binary forms; provided that if you redistribute |
the Apple Software in its entirety and without modifications, you must retain |
this notice and the following text and disclaimers in all such redistributions of |
the Apple Software. Neither the name, trademarks, service marks or logos of |
Apple Computer, Inc. may be used to endorse or promote products derived from the |
Apple Software without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or implied, |
are granted by Apple herein, including but not limited to any patent rights that |
may be infringed by your derivative works or by other works in which the Apple |
Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
*/ |
#import "Cube.h" |
@implementation Cube(CubeSolving) |
/* |
This diagram shows how the Cube state is stored, with a bit of an |
expanded view to show how some of the different faces are lined up. |
+--+--+--+ +--+--+--+ |
|T0|T1|T2| |T8|T7|T6| |
+--+--+--+ +--+--+--+ |
|T3|T4|T5| |T5|T4|T3| |
+--+--+--+ +--+--+--+ |
|T6|T7|T8| |T2|T1|T0| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|L0|L1|L2|F0|F1|F2|R0|R1|R2|P0|P1|P2|L0|L1|L2| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|L3|L4|L5|F3|F4|F5|R3|R4|R5|P3|P4|P5|L3|L4|L5| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|L6|L7|L8|F6|F7|F8|R6|R7|R8|P6|P7|P8|L6|L7|L8| |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |
|B0|B1|B2| |B8|B7|B6| |
+--+--+--+ +--+--+--+ |
|B3|B4|B5| |B5|B4|B3| |
+--+--+--+ +--+--+--+ |
|B6|B7|B8| |B2|B1|B0| |
+--+--+--+ +--+--+--+ |
*/ |
#define EQ(a,b) (state.a == state.b) |
#define IS_CORNER_ORIENTED(a,b,c,d,e,f) \ |
(EQ(a,d) && EQ(b,e) && EQ(c,f)) |
#define IS_CORNER_PLACED(a,b,c,d,e,f) \ |
((EQ(a,d) || EQ(a,e) || EQ(a,f)) && \ |
(EQ(b,d) || EQ(b,e) || EQ(b,f)) && \ |
(EQ(c,d) || EQ(c,e) || EQ(c,f))) |
#define IS_EDGE_ORIENTED(a,b,c,d) \ |
(EQ(a,c) && EQ(b,d)) |
#define IS_EDGE_PLACED(a,b,c,d) \ |
((EQ(a,c) || EQ(a,d)) && \ |
(EQ(b,c) || EQ(b,d))) |
// Internal check macros for corners (BFL, BFR, BLP, BPR, FLT, FRT, LPT, PRT) |
#define BFL_ORIENTED IS_CORNER_ORIENTED(B4,F4,L4,B0,F6,L8) |
#define BFL_PLACED IS_CORNER_PLACED(B4,F4,L4,B0,F6,L8) |
#define BFR_ORIENTED IS_CORNER_ORIENTED(B4,F4,R4,B2,F8,R6) |
#define BFR_PLACED IS_CORNER_PLACED(B4,F4,R4,B2,F8,R6) |
#define BLP_ORIENTED IS_CORNER_ORIENTED(B4,L4,P4,B6,L6,P8) |
#define BLP_PLACED IS_CORNER_PLACED(B4,L4,P4,B6,L6,P8) |
#define BPR_ORIENTED IS_CORNER_ORIENTED(B4,P4,R4,B8,P6,R8) |
#define BPR_PLACED IS_CORNER_PLACED(B4,P4,R4,B8,P6,R8) |
#define FLT_ORIENTED IS_CORNER_ORIENTED(F4,L4,T4,F0,L2,T6) |
#define FLT_PLACED IS_CORNER_PLACED(F4,L4,T4,F0,L2,T6) |
#define FRT_ORIENTED IS_CORNER_ORIENTED(F4,R4,T4,F2,R0,T8) |
#define FRT_PLACED IS_CORNER_PLACED(F4,R4,T4,F2,R0,T8) |
#define LPT_ORIENTED IS_CORNER_ORIENTED(L4,P4,T4,L0,P2,T0) |
#define LPT_PLACED IS_CORNER_PLACED(L4,P4,T4,L0,P2,T0) |
#define PRT_ORIENTED IS_CORNER_ORIENTED(P4,R4,T4,P0,R2,T2) |
#define PRT_PLACED IS_CORNER_PLACED(P4,R4,T4,P0,R2,T2) |
// Internal check macros for edges (BF, BL, BP, BR, FL, FR, FT, LP, LT, PR, PT and RT) |
#define BF_ORIENTED IS_EDGE_ORIENTED(B4,F4,B1,F7) |
#define BF_PLACED IS_EDGE_PLACED(B4,F4,B1,F7) |
#define BL_ORIENTED IS_EDGE_ORIENTED(B4,L4,B3,L7) |
#define BL_PLACED IS_EDGE_PLACED(B4,L4,B3,L7) |
#define BP_ORIENTED IS_EDGE_ORIENTED(B4,P4,B7,P7) |
#define BP_PLACED IS_EDGE_PLACED(B4,P4,B7,P7) |
#define BR_ORIENTED IS_EDGE_ORIENTED(B4,R4,B5,R7) |
#define BR_PLACED IS_EDGE_PLACED(B4,R4,B5,R7) |
#define FL_ORIENTED IS_EDGE_ORIENTED(F4,L4,F3,L5) |
#define FL_PLACED IS_EDGE_PLACED(F4,L4,F3,L5) |
#define FR_ORIENTED IS_EDGE_ORIENTED(F4,R4,F5,R3) |
#define FR_PLACED IS_EDGE_PLACED(F4,R4,F5,R3) |
#define FT_ORIENTED IS_EDGE_ORIENTED(F4,T4,F1,T7) |
#define FT_PLACED IS_EDGE_PLACED(F4,T4,F1,T7) |
#define LP_ORIENTED IS_EDGE_ORIENTED(L4,P4,L3,P5) |
#define LP_PLACED IS_EDGE_PLACED(L4,P4,L3,P5) |
#define LT_ORIENTED IS_EDGE_ORIENTED(L4,T4,L1,T3) |
#define LT_PLACED IS_EDGE_PLACED(L4,T4,L1,T3) |
#define PR_ORIENTED IS_EDGE_ORIENTED(P4,R4,P3,R5) |
#define PR_PLACED IS_EDGE_PLACED(P4,R4,P3,R5) |
#define PT_ORIENTED IS_EDGE_ORIENTED(P4,T4,P1,T1) |
#define PT_PLACED IS_EDGE_PLACED(P4,T4,P1,T1) |
#define RT_ORIENTED IS_EDGE_ORIENTED(R4,T4,R1,T5) |
#define RT_PLACED IS_EDGE_PLACED(R4,T4,R1,T5) |
// External Placement/orientation checks for 8 corners (BFL, BFR, BLP, BPR, FLT, FRT, LPT, PRT) |
- (BOOL)isSolved |
{ |
return |
BFL_ORIENTED |
&& BFR_ORIENTED |
&& BLP_ORIENTED |
&& BPR_ORIENTED |
&& FLT_ORIENTED |
&& FRT_ORIENTED |
&& LPT_ORIENTED |
&& PRT_ORIENTED |
&& BF_ORIENTED |
&& BL_ORIENTED |
&& BP_ORIENTED |
&& BR_ORIENTED |
&& FL_ORIENTED |
&& FR_ORIENTED |
&& FT_ORIENTED |
&& LP_ORIENTED |
&& LT_ORIENTED |
&& PR_ORIENTED |
&& PT_ORIENTED |
&& RT_ORIENTED; |
; |
} |
- (BOOL)isBFLOriented |
{ |
return BFL_ORIENTED; |
} |
- (BOOL)isBFLPlaced |
{ |
return BFL_PLACED; |
} |
- (BOOL)isBFROriented |
{ |
return BFR_ORIENTED; |
} |
- (BOOL)isBFRPlaced |
{ |
return BFR_PLACED; |
} |
- (BOOL)isBLPOriented |
{ |
return BLP_ORIENTED; |
} |
- (BOOL)isBLPPlaced |
{ |
return BLP_PLACED; |
} |
// The following five methods implement the core of the cube solver. |
// The algorithm is basically the one described in the book "How to |
// Solve the Rubik's Cube" by XXXX. I chose that particular solution |
// simply because that was the book I had in fifth grade, and because |
// it was fairly simple to learn. It was also fairly straightforward |
// to write code to implement it. |
// There are many other ways to handle solving the cube using more |
// informed search methods, but they generally require a lot more |
// work and a lot more memory. Of course, they also will produce |
// far more optimal solutions than those found here. |
// It would be relatively straightforward to add other solution methods |
// based on all of the primitives provided here. |
- (BOOL)solveTopEdges |
{ |
while(! |
(FT_ORIENTED && RT_ORIENTED && PT_ORIENTED && LT_ORIENTED)) |
{ |
// Find non-solved edge to work on. |
while(FT_ORIENTED) |
[self performSequence:@"Y+"]; |
// 11 possibilities (aside from being placed correctly) |
if(IS_EDGE_PLACED(F4,T4,R1,T5)) |
[self performSequence:@"R- F-"]; |
else if(IS_EDGE_PLACED(F4,T4,T1,P1)) |
[self performSequence:@"T+ R- T- F-"]; |
else if(IS_EDGE_PLACED(F4,T4,L1,T3)) |
[self performSequence:@"L+ F+"]; |
else if(IS_EDGE_PLACED(F4,T4,F5,R3)) |
[self performSequence:@"F-"]; |
else if(IS_EDGE_PLACED(F4,T4,P3,R5)) |
[self performSequence:@"R2 F- R2"]; |
else if(IS_EDGE_PLACED(F4,T4,P5,L3)) |
[self performSequence:@"L2 F+ L2"]; |
else if(IS_EDGE_PLACED(F4,T4,L5,F3)) |
[self performSequence:@"F+"]; |
else if(IS_EDGE_PLACED(F4,T4,B1,F7)) |
[self performSequence:@"F2"]; |
else if(IS_EDGE_PLACED(F4,T4,B5,R7)) |
[self performSequence:@"B- F2"]; |
else if(IS_EDGE_PLACED(F4,T4,P7,B7)) |
[self performSequence:@"B2 F2"]; |
else if(IS_EDGE_PLACED(F4,T4,L7,B3)) |
[self performSequence:@"B+ F2"]; |
if(!FT_PLACED) |
return NO; |
if(!FT_ORIENTED) |
[self performSequence:@"F- T+ L- T-"]; |
assert(FT_ORIENTED); |
} |
return YES; |
} |
- (BOOL)solveTopCorners |
{ |
unsigned iterations; |
while(!(FLT_ORIENTED && FRT_ORIENTED && LPT_ORIENTED && PRT_ORIENTED)) |
{ |
// Find non-solved corner to work on. |
while(FRT_ORIENTED) |
[self performSequence:@"Y+"]; |
if(!FRT_PLACED) |
{ |
// See if the desired cube is on the top face somewhere (but |
// in the wronge place), and if so, move it to the bottom |
// before the next step |
if(IS_CORNER_PLACED(F4,R4,T4,F0,L2,T6)) |
{ |
[self performSequence:@"Y-"]; |
[self performSequence:@"R- B- R+"]; |
[self performSequence:@"Y+"]; |
} |
else if(IS_CORNER_PLACED(F4,R4,T4,R2,T2,P0)) |
{ |
[self performSequence:@"Y+"]; |
[self performSequence:@"R- B- R+"]; |
[self performSequence:@"Y-"]; |
} |
else if(IS_CORNER_PLACED(F4,R4,T4,L0,T0,P2)) |
{ |
[self performSequence:@"Y+"]; |
[self performSequence:@"Y+"]; |
[self performSequence:@"R- B- R+"]; |
[self performSequence:@"Y-"]; |
[self performSequence:@"Y-"]; |
} |
if(!FRT_PLACED) |
{ |
// Now turn bottom until the desired is in BFR |
iterations = 0; |
while(!IS_CORNER_PLACED(F4,R4,T4,F8,R6,B2)) |
{ |
iterations++; |
if(iterations > 3) |
return NO; |
[self performSequence:@"B+"]; |
} |
// Note: I should fix this to do one of three bring-up |
// sequences that will bring up the cube into the right |
// orientation to begin with. |
// Bring it up. |
[self performSequence:@"R- B- R+"]; |
} |
} |
while(!FRT_ORIENTED) |
[self performSequence:@"R- B2 R+ F+ B2 F-"]; |
} |
return YES; |
} |
- (BOOL)solveVerticalEdges |
{ |
while(!(LP_ORIENTED && FL_ORIENTED && FR_ORIENTED && PR_ORIENTED)) |
{ |
// Find edge to fix |
while(FR_ORIENTED) |
[self performSequence:@"Y+"]; |
if(FR_PLACED) |
{ |
[self performSequence:@"R- B+ R+ B+ F+ B- F- B+ R- B+ R+ B+ F+ B- F-"]; |
assert(FR_ORIENTED); |
continue; |
} |
else |
{ |
// Bring down cube if necessary |
if(IS_EDGE_PLACED(F4,R4,F3,L5)) |
{ |
[self performSequence:@"Y-"]; |
[self performSequence:@"R- B+ R+ B+ F+ B- F-"]; |
[self performSequence:@"Y+"]; |
} |
else if(IS_EDGE_PLACED(F4,R4,R5,P3)) |
{ |
[self performSequence:@"Y+"]; |
[self performSequence:@"R- B+ R+ B+ F+ B- F-"]; |
[self performSequence:@"Y-"]; |
} |
else if(IS_EDGE_PLACED(F4,R4,P5,L3)) |
{ |
[self performSequence:@"Y+"]; |
[self performSequence:@"Y+"]; |
[self performSequence:@"R- B+ R+ B+ F+ B- F-"]; |
[self performSequence:@"Y-"]; |
[self performSequence:@"Y-"]; |
} |
} |
// Rotate bottom until desired cube is in BR or BF |
for(;;) |
{ |
if(IS_EDGE_ORIENTED(F4,R4,F7,B1)) |
{ |
[self performSequence:@"B- R- B+ R+ B+ F+ B- F-"]; |
break; |
} |
else if(IS_EDGE_ORIENTED(F4,R4,B5,R7)) |
{ |
[self performSequence:@"B+ F+ B- F- B- R- B+ R+"]; |
break; |
} |
[self performSequence:@"B+"]; |
} |
assert(FR_ORIENTED); |
} |
return YES; |
} |
- (BOOL)solveBottomCorners |
{ |
if(!(BFL_ORIENTED && BFR_ORIENTED && BLP_ORIENTED && BPR_ORIENTED)) |
{ |
unsigned placed = 0; |
unsigned iterations; |
iterations = 0; |
// Rotate bottom face until 4 or 2 cubes are properly positioned |
for(;;) |
{ |
if(BFL_PLACED && BFR_PLACED && BLP_PLACED && BPR_PLACED) |
{ |
placed = 4; |
break; |
} |
else if(BFL_PLACED && BFR_PLACED) |
{ |
[self performSequence:@"Y+"]; |
[self performSequence:@"Y+"]; |
placed = 1; |
break; |
} |
else if(BFL_PLACED && BLP_PLACED) |
{ |
[self performSequence:@"Y+"]; |
placed = 1; |
break; |
} |
else if(BFR_PLACED && BPR_PLACED) |
{ |
[self performSequence:@"Y-"]; |
placed = 1; |
break; |
} |
else if(BLP_PLACED && BPR_PLACED) |
{ |
placed = 1; |
break; |
} |
else if(BFL_PLACED && BPR_PLACED) |
{ |
[self performSequence:@"Y-"]; |
placed = 2; |
break; |
} |
else if(BFR_PLACED && BLP_PLACED) |
{ |
placed = 2; |
break; |
} |
iterations++; |
if(iterations > 3) |
return NO; |
[self performSequence:@"B+"]; |
} |
if(placed == 1) |
[self performSequence:@"R- B- R+ F+ B+ F- R- B+ R+ B2"]; |
else if(placed == 2) |
[self performSequence:@"R- B- R+ F+ B2 F- R- B+ R+ B+"]; |
assert(BFL_PLACED && BFR_PLACED && BLP_PLACED && BPR_PLACED); |
while(!(BFL_ORIENTED && BFR_ORIENTED && BLP_ORIENTED && BPR_ORIENTED)) |
{ |
// Rotate entire cube until it pattern matches with one of the |
// known 7 patterns, then perform the same sequence. |
iterations = 0; |
for(;;) |
{ |
unsigned c = state.B4; |
if(c == state.B0 && c == state.F8 && |
c == state.R8 && c == state.P8) |
{ |
break; |
} |
else if(c == state.B0 && c == state.R6 && |
c == state.P6 && c == state.L6) |
{ |
break; |
} |
else if(c == state.L8 && c == state.R6 && |
c == state.P6 && c == state.P8) |
{ |
break; |
} |
else if(c == state.F6 && c == state.B2 && |
c == state.B8 && c == state.P8) |
{ |
break; |
} |
else if(c == state.F6 && c == state.F8 && |
c == state.B8 && c == state.B6) |
{ |
break; |
} |
else if(c == state.F6 && c == state.B2 && |
c == state.R8 && c == state.B6) |
{ |
break; |
} |
else if(c == state.L8 && c == state.R6 && |
c == state.L6 && c == state.R8) |
{ |
break; |
} |
iterations++; |
[self performSequence:@"Y+"]; |
if(iterations > 3) |
break; |
} |
if(iterations > 3) |
return NO; |
[self performSequence:@"R- B- R+ B- R- B2 R+ B2"]; |
} |
} |
return YES; |
} |
- (BOOL)solveBottomEdges |
{ |
unsigned positioned = 0; |
if(BF_PLACED) |
positioned++; |
if(BL_PLACED) |
positioned++; |
if(BR_PLACED) |
positioned++; |
if(BP_PLACED) |
positioned++; |
if(positioned == 0) |
{ |
[self performSequence:@"(L- R+) F+ (L+ R-) B2 (L- R+) F+ (L+ R-)"]; |
positioned = 1; |
} |
while(!(BF_ORIENTED && BL_ORIENTED && BR_ORIENTED && BP_ORIENTED)) |
{ |
while(!(BF_PLACED && BL_PLACED && BR_PLACED && BP_PLACED)) |
{ |
while(!BF_PLACED) |
[self performSequence:@"Y+"]; |
[self performSequence:@"(L- R+) F+ (L+ R-) B2 (L- R+) F+ (L+ R-)"]; |
} |
if(BF_ORIENTED && BL_ORIENTED && BR_ORIENTED && BP_ORIENTED) |
break; |
for(;;) |
{ |
if(!BF_ORIENTED && !BL_ORIENTED && !BR_ORIENTED && !BP_ORIENTED) |
{ |
[self performSequence:@"(L- R+) F2 (L+ R-) B2 (L- R+) F+"]; |
[self performSequence:@"(L+ R-) B2 (L- R+) F2 (L+ R-) B-"]; |
break; |
} |
else if(!BF_ORIENTED && BL_ORIENTED && BR_ORIENTED && !BP_ORIENTED) |
{ |
[self performSequence:@"(L- R+) F+ (L+ R-) B+"]; |
[self performSequence:@"(L- R+) F+ (L+ R-) B+"]; |
[self performSequence:@"(L- R+) F2 (L+ R-) B+"]; |
[self performSequence:@"(L- R+) F+ (L+ R-) B+"]; |
[self performSequence:@"(L- R+) F+ (L+ R-) B2"]; |
break; |
} |
else if(BF_ORIENTED && !BL_ORIENTED && BR_ORIENTED && !BP_ORIENTED) |
{ |
[self performSequence:@"(L- R+) F+ (L+ R-) B- (L- R+) F-"]; |
[self performSequence:@"(L+ R-) B- (L- R+) F2 (L+ R-)"]; |
break; |
} |
[self performSequence:@"Y-"]; |
} |
} |
return YES; |
} |
// This method does a fairly simplistic optimization pass over the |
// solution sequence. It by no means produces anything even close |
// to an optimal solution. |
- (NSMutableString *)optimizeSolution:(NSMutableString *)solution |
{ |
unichar currentFace, currentMove; |
unichar bufferFace, bufferMove = 0; |
BOOL changed; |
unsigned index, length; |
NSMutableString *optimized; |
// With some work, this could probably do everything in |
// a single pass. Basically if a buffered move was cancelled, |
// move the index back and re-buffer the previous move. |
do |
{ |
changed = NO; |
optimized = [[[NSMutableString alloc] init] autorelease]; |
bufferFace = 0; |
index = 0; |
length = [solution length]; |
while(index < length) |
{ |
currentFace = [solution characterAtIndex:index]; |
currentMove = [solution characterAtIndex:index+1]; |
index += 2; |
if(currentFace >= 'Y') |
{ |
if(currentFace == 'Y') |
{ |
if(currentMove == '+') |
{ |
// "Rotate" the remaining solution |
[solution replaceOccurrencesOfString:@"F" |
withString:@"_" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
[solution replaceOccurrencesOfString:@"L" |
withString:@"F" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
[solution replaceOccurrencesOfString:@"P" |
withString:@"L" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
[solution replaceOccurrencesOfString:@"R" |
withString:@"P" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
[solution replaceOccurrencesOfString:@"_" |
withString:@"R" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
changed = YES; |
} |
else if(currentMove == '-') |
{ |
// "Rotate" the remaining solution |
[solution replaceOccurrencesOfString:@"F" |
withString:@"_" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
[solution replaceOccurrencesOfString:@"R" |
withString:@"F" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
[solution replaceOccurrencesOfString:@"P" |
withString:@"R" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
[solution replaceOccurrencesOfString:@"L" |
withString:@"P" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
[solution replaceOccurrencesOfString:@"_" |
withString:@"L" |
options:NSLiteralSearch |
range:NSMakeRange(index,length-index)]; |
changed = YES; |
} |
} |
continue; |
} |
if(!bufferFace) |
{ |
bufferFace = currentFace; |
bufferMove = currentMove; |
continue; |
} |
if(currentFace != bufferFace) |
{ |
[optimized appendFormat:@"%c%c",bufferFace,bufferMove]; |
bufferFace = currentFace; |
bufferMove = currentMove; |
continue; |
} |
// Append current move to buffered move |
if(bufferMove == '+') |
{ |
if(currentMove == '-') |
bufferFace = 0; |
else if(currentMove == '+') |
bufferMove = '2'; |
else |
{ |
assert(currentMove == '2'); |
bufferMove = '-'; |
} |
} |
else if(bufferMove == '-') |
{ |
if(currentMove == '+') |
bufferFace = 0; |
else if(currentMove == '-') |
bufferMove = '2'; |
else |
{ |
assert(currentMove == '2'); |
bufferMove = '+'; |
} |
} |
else |
{ |
assert(bufferMove == '2'); |
if(currentMove == '2') |
bufferFace = 0; |
else if(currentMove == '+') |
bufferMove = '-'; |
else |
{ |
assert(currentMove == '-'); |
bufferMove = '+'; |
} |
} |
changed = YES; |
} |
// Output buffered move |
if(bufferFace) |
[optimized appendFormat:@"%c%c",bufferFace,bufferMove]; |
solution = optimized; |
} while(changed); |
return optimized; |
} |
- (NSString *)solveCube |
{ |
CubeState initialState = state; |
NSMutableString *solution, *optimized; |
[self setShowsAnimation:NO]; |
[self setPostsNotification:NO]; |
[self setUndoEnabled:NO]; |
[self beginRecording]; |
// Proceed step by step. Abort if something strange |
// happens (shouldn't happen assuming the solver works right). |
[self solveTopEdges] && |
[self solveTopCorners] && |
[self solveVerticalEdges] && |
[self solveBottomCorners] && |
[self solveBottomEdges]; |
// For the curious, try modifying the code to return |
// the non-optimized solution. |
solution = [self endRecording]; |
optimized = [self optimizeSolution:solution]; |
[self setState:initialState]; |
[self setUndoEnabled:YES]; |
[self setShowsAnimation:YES]; |
[self setPostsNotification:YES]; |
return optimized; |
} |
@end |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-04-21