I created a pretty complex algorithm for calculating matches on a Bejeweled board that kept track of matches involving wilds. After doing some in-game testing, I found quite a few bugs that needed to be eliminated.
In order to deal with this problem, I first created an undo system for the game, and then when I saw unexpected behavior during a match, I stepped the game back and made a note of the sequence of orbs that caused the problem. Then I wrote unit tests for those sequences of orbs and refined the algorithm until all of the tests passed. I am now quite sure that there are none or very few bugs.
The match requirements:
1. Three or more consecutive orbs of the same type is a match.
2. Wilds match with any color including other wilds and can be part of more than one match at a time.
I have heard that unit tests are more about writing them than reading them, but I will let you decide:
DMBoardEvalTests.m
#import <XCTest/XCTest.h>
#import "DMBoardEval.h"
#import "DMOrb.h"
@interface DMBoardEval (Testing)
-(NSMutableArray *) matchesForOrbs:(NSMutableArray *)orbs;
@end
@interface DMBoardEvalTests : XCTestCase
@end
@implementation DMBoardEvalTests {
DMBoardEval *_boardEval;
}
- (void)setUp {
[super setUp];
_boardEval = [[DMBoardEval alloc]init];
}
#pragma mark - First Bugs Found
-(void) testBugWithTwoMatchesWhenShouldBeOne {
//originally this returned 2 matches because of problems with the wild search algorithm
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeYellow]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeYellow]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeYellow]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBomb]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRed]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 1, @"Pass");
}
-(void) testBugWithOneBigMatchInsteadOfTwoSmall {
//this returns one big match of WRWBrBr when it should be multiple
//it returns them all as one big array because when it reaches the brown, wild search is active
//and the previous one was a wild, so it adds the current one
//fix involved keeping track of the previous orb instead of just whether it is wild
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeYellow]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRed]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeSwapCards]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 2, @"Pass");
}
#pragma mark - Easy Tests
-(void) testThreeOnALineNoWilds {
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 3, @"Pass");
}
-(void) testTwoOnALineNoWilds {
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 2, @"Pass");
}
-(void) testMatchOfThreeBlueWildBlue {
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeSwapCards]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeYellow]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRed]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 1, @"Pass");
}
#pragma mark Dupicate Match bug
-(void) testTwoOnALineWithWild {
//reveals a problem where three mathces will be found, 6 Blue with Wild on the end, one with Wild and 3 greens, one with only 3 greens
//fix involved creating a duplicate match culler
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 2, @"Pass");
}
-(void) testTwoOnALineWithIntermixedWilds {
//related to the same problem as above, gives 5 blue, 6 blue, 3 green, 4 green with wild mixed in
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 2, @"Pass");
}
-(void) testBugAnExtraMatchWithWildCenter {
//the logger showed this as two instances of BrWBr
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeYellow]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 1, @"Pass");
}
#pragma mark - Consecutive Wilds Bug
-(void) testBugWhereNotAllPreviousWildsAreCounted {
//this passed, but the problem is that the second match should have W Ro Br Br and is missing the W
//fixed this bug by adding all of the consecutive wilds to the next match instead of just one previous
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeYellow]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 2, @"Pass");
}
-(void) testPreviousWildsInARow {
//this passes, but the problem is that the second match should have W Ro Br Br and is missing the W
//fixed this bug by adding all of the consecutive wilds to the next match instead of just one previous
//this example will produce 3 matches since one of them is the one with 3 wilds and wilds are not culled
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeSwapCards]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 3, @"Pass");
}
-(void) testBugExtraWildOrbOutOfPlace {
//fixed by putting a break in the previous wild orb loop
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeGreen]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBrown]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeYellow]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRed]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeSwapCards]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRed]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 2, @"Pass");
}
-(void) testAllWildAndOneColorMixedIn {
//similar problem to the above
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBomb]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBomb]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeSwapCards]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 1, @"Pass");
}
-(void) testAllWildAndOneColor {
//similar problem to the above
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBlue]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 1, @"Pass");
}
#pragma mark - Test All Wilds
//all these failed with two matches of 9 because the match culler skips wilds
//fixed by adding a wild counter and a check when all orbs are wild
-(void) testAllWilds {
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 1, @"Pass");
}
-(void) testAllWildOrHigherMix {
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeSwapCards]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBomb]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeSwapCards]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBomb]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 1, @"Pass");
}
-(void) testAllWildOrHigherMixWithInternalMatches {
NSMutableArray *customArray = [[NSMutableArray alloc]init];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeRotate]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBomb]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeWild]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeBomb]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeSwapCards]];
[customArray addObject:[[DMOrb alloc]initWithType:DMOrbTypeSwapCards]];
NSMutableArray *matches = [_boardEval matchesForOrbs:customArray];
XCTAssert(matches.count == 1, @"Pass");
}
@end
Here is the entire match algorithm, including the wild search. The method is huge, so I have extra comments in there to explain the logic. I have tried different ways to shrink this method, but I haven't come up with any good solutions to shrink it further.
DMBoardEval.m
-(NSMutableArray *) matchesForOrbs:(NSMutableArray *)orbs {
NSMutableArray *matches = [[NSMutableArray alloc]init];
NSMutableArray *matchingOrbs = [[NSMutableArray alloc]init];
NSMutableArray *wildMatchOrbs = [[NSMutableArray alloc]init];
BOOL wildSearchActive = NO;
DMOrb *previousOrb = nil;
//need the type in advance to do the wild check
DMOrbType typeToSearchFor = ((DMOrb *)orbs[0]).type;
//if the first orb is wild or higher, search for the next one that isnt
int index = 0;
while ([self wildOrHigher:typeToSearchFor] && index < kNumOrbsPerRow) {
typeToSearchFor = ((DMOrb *)orbs[index]).type;
index++;
}
//Now begin the search//
for (int i = 0; i < kNumOrbsPerRow; i++) {
DMOrb *orb = orbs[i];
if ([self wildOrHigher:orb.type] && !wildSearchActive) {
wildSearchActive = YES;
}
//if the orb does not match and is not wild
if (orb.type != typeToSearchFor && ![self wildOrHigher:orb.type]) {
if (wildSearchActive) {
if (wildMatchOrbs.count >= 3) {
[matches addObject:wildMatchOrbs];
}
//if the previous orb is wild, add all consecutive previous wilds plus the current orb
if ([self wildOrHigher:previousOrb.type]) {
NSMutableArray *previousWildsInARow = [[NSMutableArray alloc]init];
for (DMOrb *orbInWildMatchOrbs in [wildMatchOrbs reverseObjectEnumerator]) {
if ([self wildOrHigher:orbInWildMatchOrbs.type]) {
[previousWildsInARow addObject:orbInWildMatchOrbs];
} else {
break;
}
}
wildMatchOrbs = [NSMutableArray arrayWithArray:previousWildsInARow];
[wildMatchOrbs addObject:orb];
//otherwise reset the search
} else {
wildSearchActive = NO;
wildMatchOrbs = [[NSMutableArray alloc]init];
}
//this check is required on the final orb to make sure it gets added
if (i == kNumOrbsPerRow - 1) {
if (wildMatchOrbs.count >= 3) {
[matches addObject:wildMatchOrbs];
}
}
}
//regular logic
//when a different type is encountered, add the match if it has 3 or more
if (matchingOrbs.count >= 3) {
[matches addObject:matchingOrbs];
}
//reset this array and add the orb for the next iteration
matchingOrbs = [[NSMutableArray alloc]init];
[matchingOrbs addObject:orb];
//if the orb matches or is valid
} else {
[matchingOrbs addObject:orb];
//logic for wild searching
if (wildSearchActive) {
[wildMatchOrbs addObject:orb];
}
//regular logic
//if the last few are the same type, this will make sure the match gets added
if (i == kNumOrbsPerRow - 1) {
if (matchingOrbs.count >= 3) {
[matches addObject:matchingOrbs];
}
if (wildMatchOrbs.count >= 3) {
[matches addObject:wildMatchOrbs];
}
}
}
//if the type is not wild, set this orb type to the one to search for
if (![self wildOrHigher:orb.type]) {
typeToSearchFor = orb.type;
}
previousOrb = orb;
}
return [self removeDuplicateMatches:matches];
}
-(BOOL) wildOrHigher:(DMOrbType)type {
return type == DMOrbTypeWild ||
type == DMOrbTypeBomb ||
type == DMOrbTypeRotate ||
type == DMOrbTypeSwapCards;
}
-(NSMutableArray *) removeDuplicateMatches:(NSMutableArray *)matches {
//first, sort the match array by count size
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]initWithKey:@"@count" ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
[matches sortUsingDescriptors:sortDescriptors];
//starting with the largest, start comparing and building
NSMutableArray *validMatches = [[NSMutableArray alloc]init];
NSMutableArray *orbsAlreadyEncountered = [[NSMutableArray alloc]init];
int wildCount = 0;
for (NSMutableArray *match in matches) {
BOOL redundantMatch = NO;
for (DMOrb *orb in match) {
if (!redundantMatch &&
![self wildOrHigher:orb.type] &&
[orbsAlreadyEncountered containsObject:orb]) {
redundantMatch = YES;
} else {
[orbsAlreadyEncountered addObject:orb];
}
//wild check to fix bug
if ([self wildOrHigher:orb.type]) {
wildCount++;
}
if (wildCount >= kNumOrbsPerRow) {
for (NSMutableArray *validMatch in validMatches) {
if (validMatch.count >= kNumOrbsPerRow) {
redundantMatch = YES;
}
}
}
}
if (!redundantMatch) {
[validMatches addObject:match];
}
}
return validMatches;
}
At the top of the method, I have this large comment explaining all of the logic in one place for easy reference:
//Logical Flow of Method//
//set up the variables needed
//get the type to search for if the first orb is wild
//start search and repeat for all orbs
//if a wild is found, start wild search
//if the orb does not match and is not wild
//if wild search is active
//evaluate the match and rebuild the array based on previous orb
//else if not evaluate the match and rebuild the array
//else if it does match or is wild, add it to the regular array
//if the wild search active, also add it to the wild search array
//if not wild, set the type to search for to the type of the current orb
//cull repeated matches in a separate method
Here is the usage of this mega method:
-(NSMutableArray *) matchesInAllRows {
NSMutableArray *matches = [[NSMutableArray alloc]init];
for (DMRow *row in _board.rows) {
[matches addObjectsFromArray:[self matchesForOrbs:row.orbs]];
}
return matches;
}
-(NSMutableArray *) matchesInAllColumns {
NSMutableArray *matches = [[NSMutableArray alloc]init];
for (DMColumn *column in _board.columns) {
[matches addObjectsFromArray:[self matchesForOrbs:column.orbs]];
}
return matches;
}
All feedback is welcome!