How to sort NSMutableArray? Code review needed - objective-c

Good evening.
This code works. It sorts an array of cards by both Suit and Card Value. It is also very much brute force. Can you recommend a better way? Does Objective-C help dealing with a situation where object being sorted itself has multiple fields, on which sorting depends?
-(void) sort: (NSMutableArray *) deck {
NSUInteger count = [deck count];
Card *thisCard;
Card *nextCard;
int this;
int next;
BOOL stillSwapping = true;
while (stillSwapping) {
stillSwapping = false;
for (NSUInteger i = 0; i < count; ++i) {
this = i;
next = i+1;
if (next < count) {
thisCard = [deck objectAtIndex:this];
nextCard = [deck objectAtIndex:next];
if ([thisCard suit] > [nextCard suit]) {
[deck exchangeObjectAtIndex:this withObjectAtIndex:next];
stillSwapping = true;
}
if ([thisCard suit] == [nextCard suit]) {
if ([thisCard value] > [nextCard value]) {
[deck exchangeObjectAtIndex:this withObjectAtIndex:next];
stillSwapping = true;
}
}
}
}
}
}

You have several options.
Perhaps the simplest is to define the method -compare: on Card, which returns an NSComparisonResult. This is preferable if sorting by suit and value is the "standard" way to sort a Card. If you've done this, then your entire sorting method can be turned into
[deck sortUsingSelector:#selector(compare:)];
If you don't want to do this, you can use an array of NSSortDescriptors, one for suit and one for value.
[deck sortUsingDescriptors:[NSArray arrayWithObjects:[NSSortDescriptor sortDescriptorWithKey:#"suit" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:#"value"] ascending:YES], nil]];
If this doesn't work for whatever reason, you could try -sortUsingComparator:
[deck sortUsingComparator:^NSComparisonResult(Card *obj1, Card *obj2) {
int suit1 = [obj1 suit];
int suit2 = [obj2 suit];
if (suit1 > suit2) {
return NSOrderedDescending;
} else if (suit1 < suit2) {
return NSOrderedAscending;
} else {
id value1 = [obj1 value];
id value2 = [obj2 value];
if (value1 > value2) {
return NSOrderedDescending;
} else if (value1 < value2) {
return NSOrderedAscending;
} else {
return NSOrderedSame;
}
}
}

Related

Poker hand strength

I've programatically determined hand rankings as added below. However, the issue is - if per say, two players end up with a pair.. How do I determine the winner?
To each card object I've added a prime number value. Ace being 41 and deuce being 3 and the in betweens. I'm thinking of multiplying these values when the hand is determined and whoever has the highest is winning. I need help determining whether this is the correct approach.
+(BOOL)isFull:(NSArray*)cards {
Cards *card1 = [cards objectAtIndex:0];
Cards *card2 = [cards objectAtIndex:1];
Cards *card3 = [cards objectAtIndex:2];
Cards *card4 = [cards objectAtIndex:3];
Cards *card5 = [cards objectAtIndex:4];
if (([card1.face isEqualToString:card3.face] && [card4.face isEqualToString:card5.face]) || ([card1.face isEqualToString:card2.face] && [card3.face isEqualToString:card5.face])) {
return true;
}
return false;
}
+(BOOL)isFlush:(NSArray*)cards {
NSMutableArray *organizedBySuit = [self organizeCardsSuitOrder:cards];
Cards *card1 = [organizedBySuit objectAtIndex:0];
Cards *card2 = [organizedBySuit objectAtIndex:4];
if ([card1.suit isEqualToString:card2.suit]) { return true; } else { return false; } // cards are organized by suit, so if the first equals the last..
}
+(BOOL)isStraight:(NSArray*)cards {
Cards *card1 = [cards objectAtIndex:0];
Cards *card2 = [cards objectAtIndex:1];
Cards *card3 = [cards objectAtIndex:2];
Cards *card4 = [cards objectAtIndex:3];
Cards *card5 = [cards objectAtIndex:4];
if ((card1.rankByInt) == 0 && (card2.rankByInt) == 9 && (card3.rankByInt) == 10 && (card4.rankByInt) == 11 && (card5.rankByInt) == 12) {
return true;
}
else if ((card1.rankByInt) < 9) {
if ((card2.rankByInt) == (card1.rankByInt) + 1) {
if ((card3.rankByInt) == (card1.rankByInt) + 2) {
if ((card4.rankByInt) == (card1.rankByInt) + 3) {
if ((card5.rankByInt) == (card1.rankByInt) + 4) {
return true;
}
}
}
}
}
return false;
}
+(BOOL)isTrip:(NSArray*)cards {
NSArray *faces = [self returnArrayOfFaces];
__block int pairCounter = 0;
for (int i = 0; i < [faces count]; i++) {
for (int t = 0; t < [cards count]; t++) {
Cards *card = [cards objectAtIndex:t];
if ([card.face isEqualToString:faces[i]]) {
pairCounter++;
}
}
if (pairCounter > 2) {
return true;
}
pairCounter = 0;
}
return false;
}
+(BOOL)isTwoPair:(NSArray*)cards {
NSArray *faces = [self returnArrayOfFaces];
__block int pairCount = 0;
__block int doublePairCount = 0;
for (int i = 0; i < [faces count]; i++) {
for (int t = 0; t < [cards count]; t++) {
Cards *card = [cards objectAtIndex:t];
if ([card.face isEqualToString:faces[i]]) {
pairCount++;
}
}
if (pairCount > 1) {
doublePairCount++;
}
pairCount = 0;
}
if (doublePairCount > 1) {
return true;
}
return false;
}
+(BOOL)isPair:(NSArray*)cards {
NSArray *faces = [self returnArrayOfFaces];
__block int pairCounter = 0;
for (int i = 0; i < [faces count]; i++) {
for (int t = 0; t < [cards count]; t++) {
Cards *card = [cards objectAtIndex:t];
if ([card.face isEqualToString:faces[i]]) {
pairCounter++;
}
}
if (pairCounter > 1) {
return true;
}
pairCounter = 0;
}
return false;
}
And the cards are generated to include their primes here.
+(NSMutableArray*)createDeck:(id)sender {
[sender removeAllObjects];
NSArray *faces = [[NSArray alloc] initWithObjects:#"A",#"2",#"3",#"4",#"5",#"6",#"7",#"8",#"9",#"10",#"J",#"Q",#"K", nil];
NSArray *suits = [[NSArray alloc] initWithObjects:#"h",#"d",#"c",#"s", nil];
NSArray *primes = [[NSArray alloc] initWithObjects:[NSNumber numberWithInt:41],[NSNumber numberWithInt:2],[NSNumber numberWithInt:3],[NSNumber numberWithInt:5],[NSNumber numberWithInt:7],[NSNumber numberWithInt:11],[NSNumber numberWithInt:13],[NSNumber numberWithInt:17],[NSNumber numberWithInt:19],[NSNumber numberWithInt:23],[NSNumber numberWithInt:29],[NSNumber numberWithInt:31],[NSNumber numberWithInt:37], nil];
for (int i = 0; i < 52; i++) {
Cards *card = [[Cards alloc]init];
card.face = [NSString stringWithFormat:#"%#", faces[i % 13]];
card.suit = [NSString stringWithFormat:#"%#", suits[i / 13]];
card.rankByInt = i % 13;
card.symbol = [Cards symbolForSuit:card.suit];
card.prime = [[primes objectAtIndex:(i % 13)] intValue];
[sender addObject:card];
}
[sender shuffle];
return sender;
}
So if possible can you provide me with the way to go forward to essentially 'rank' each hand by the cards values after I have retrieved their rank such as flush, straight etc..
Also, if you see any ways to improve the efficiency of my checks for hand ranks, please share.
First evaluate the hand type. If those are equal, compare the highest card that makes up the hand (unless it's a full house, then compare the set). If all cards in the hand type are equal, and the hand type is less than 5 cards, compare the next highest card in the 5 card hand. Only if the best combination of 5 cards are equal between two hands do the hands tie.
I wrote a poker hand generator and strength comparer in python that may be of some interest to you: https://github.com/Alec-ala/poker-stud-showdown

NSDictionary Search Time Complexity

Is the search operation selector objectForKey: of the NSDictionary class of order 1 time complexity like a hashtable?
I hate to have a mostly-link answer, but everything you might want to know and more is here:
Exposing NSDictionary
This is the code suggested for objectForKey:
- (id)objectForKey:(id)aKey
{
NSUInteger sizeIndex = _szidx;
NSUInteger size = __NSDictionarySizes[sizeIndex];
id *storage = (id *)object_getIndexedIvars(dict);
NSUInteger fetchIndex = [aKey hash] % size;
for (int i = 0; i < size; i++) {
id fetchedKey = storage[2 * fetchIndex];
if (fetchedKey == nil) {
return nil;
}
if (fetchedKey == aKey || [fetchedKey isEqual:aKey]) {
return storage[2 * fetchIndex + 1];
}
fetchIndex++;
if (fetchIndex == size) {
fetchIndex = 0;
}
}
return nil;
}
As Bartosz Ciechanowski says:
Worse case performance is linear
Read the rest!
He proves that there is definitely object instance == checking before an isEqual test. And a lot more.

Generating primes List in objective C?

I create this objective C class to Genrate prime numbers from n to limit. I have problem I could not get the item in NSMutableArray inside for loop. can some one show me how can I fix it?
#implementation Prime
-(NSMutableArray *)generatePrimes:(int)upperLimit{
NSMutableArray *primes = [[NSMutableArray alloc]init];
bool isPrime;
int j;
[primes addObject:[NSDecimalNumber numberWithInt:2]];
for (int i= 3; i <= upperLimit ; i+=2) {
j = 0;
isPrime = YES;
NSInteger index;
for(id obj in primes)
{
index = [primes indexOfObject:obj];
if((index * index) <= i )
{
if(i % index == 0)
{
isPrime = NO;
break;
}
}
}
if(isPrime)
{
[primes addObject:[NSDecimalNumber numberWithInt:i]];
}
}
return primes;
}
#end
This question is a little vague, but it seems to me what you're trying to do is convert the NSDecimalNumber back into an int. What your code is actually doing is getting the number's index inside the array (ie the first object is 0, the second is 1, etc.) If you are trying to get the original value of i, change these lines:
for(id obj in primes)
{
index = [primes indexOfObject:obj];
to this:
for(NSDecimalNumber num in primes)
{
index = [num integerValue];
I'd also recommend using a different name than index, as that's misleading as to what you're actually doing.
-(NSMutableArray *)generatePrimes:(int)upperLimit
{
NSMutableArray *primes = [[NSMutableArray alloc]init];
bool isPrime;
for (int i=2; i<upperLimit; i++)
{
bool prime = true;
for (int j=2; j*j<=i; j++)
{
if (i % j == 0)
{
prime = false;
break;
}
}
if(prime)
{
[primes addObject:[NSDecimalNumber numberWithInt:i]];
}
}
return primes;
}
or
-(NSMutableArray *)generatePrimes:(int)upperLimit
{
NSMutableArray *primes = [[NSMutableArray alloc]init];
[primes addObject:[NSDecimalNumber numberWithInt:2]];
for(int i=3; i < upperLimit; i++)
{
bool prime=true;
for(int j=0;j<primes.count && (((NSDecimalNumber *)primes[j]).integerValue*((NSDecimalNumber *)primes[j]).integerValue) <= i;j++)
{
if(i % (((NSDecimalNumber *)primes[j]).integerValue) == 0)
{
prime=false;
break;
}
}
if(prime)
{
[primes addObject:[NSDecimalNumber numberWithInt:i]];
}
}
return primes;
}
Hope this helps!

Anagram algorithm objective C

i have written the following code to check anagram want to know is this perfect & is there any better way to implement the same in objective C
-(BOOL) findAnagram :(NSString *) string1 :(NSString *) string2
{
int len = string1.length;
if (len != string2.length)
{
return false;
}
for (int i=0; i < len; i++)
{
int h = 0;
int q = 0;
for (int k = 0; k < len ; k ++)
{
if ([string1 characterAtIndex:i] == [string1 characterAtIndex:k])
{
h++;
}
if ([string1 characterAtIndex:i] == [string2 characterAtIndex:k])
{
q++;
}
}
if (h!=q)
{
return false;
}
}
return TRUE;
}
A better performing version than yours, which is a O(n ^ 2) algorithm, is a O(n) algorithm:
BOOL anagrams(NSString *a, NSString *b)
{
if (a.length != b.length)
return NO;
NSCountedSet *aSet = [[NSCountedSet alloc] init];
NSCountedSet *bSet = [[NSCountedSet alloc] init];
for (int i = 0; i < a.length; i++)
{
[aSet addObject:#([a characterAtIndex:i])];
[bSet addObject:#([b characterAtIndex:i])];
}
return [aSet isEqual:bSet];
}
You want to know if two strings contain exactly the same characters? Easiest way would probably be to sort both of them and compare the sorted version.
Another way would be to count the number of appearances of each letter (how many As, how many Bs, and so forth), then compare those counts.
(Note: The second way is just a variation of the first one, it's one efficient way to sort a string)
It looks fine to me. But the code style is slightly odd. I would write it like this:
- (BOOL)isStringAnagram:(NSString *)string1 ofString:(NSString *)string2 {
int len = string1.length;
if (len != string2.length) {
return NO;
}
for (int i=0; i < len; i++) {
int h = 0;
int q = 0;
for (int k = 0; k < len; k++) {
if ([string1 characterAtIndex:i] == [string1 characterAtIndex:k]) {
h++;
}
if ([string1 characterAtIndex:i] == [string2 characterAtIndex:k]) {
q++;
}
}
if (h != q) {
return NO;
}
}
return YES;
}
The main issue I have is with the method name. While it's possible to have parameters that have nothing before them in the name, it is not advisable. i.e. you had findAnagram:: as the name whereas I've used isStringAnagram:ofString:.
This is an implementation on #zmbq suggestion of sorting and comparing.
You should consider the requirements of deleting spaces and being case insensitive.
- (BOOL)isAnagram:(NSString *)leftString and:(NSString *)rightString {
NSString *trimmedLeft = [[leftString stringByReplacingOccurrencesOfString:#" " withString:#""] lowercaseString];
NSString *trimmedRight = [[rightString stringByReplacingOccurrencesOfString:#" " withString:#""] lowercaseString];
return [[self stringToCharArraySorted:trimmedLeft] isEqual:[self stringToCharArraySorted:trimmedRight]];
}
- (NSArray *)stringToCharArraySorted:(NSString *)string {
NSMutableArray *array = [[NSMutableArray alloc] init];
for (int i = 0 ; i < string.length ; i++) {
[array addObject:#([string characterAtIndex:i])];
}
return [[array sortedArrayUsingSelector:#selector(compare:)] copy];
}
called like this
BOOL isAnagram = [self isAnagram:#"A BC" and:#"cba"];
Check the following method which check Anagram strings.
-(BOOL)checkAnagramString:(NSString*)string1 WithAnotherString:(NSString*)string2{
NSCountedSet *countSet1=[[NSCountedSet alloc]init];
NSCountedSet *countSet2=[[NSCountedSet alloc]init];
if (string1.length!=string2.length) {
NSLog(#"NOT ANAGRAM String");
return NO;
}
for (int i=0; i<string1.length; i++) {
[countSet1 addObject:#([string1 characterAtIndex:i])];
[countSet2 addObject:#([string2 characterAtIndex:i])];
}
if ([countSet1 isEqual:countSet2]) {
NSLog(#"ANAGRAM String");
return YES;
} else {
NSLog(#"NOT ANAGRAM String");
return NO;
}
}
Another run of the mill algorithm:
- (BOOL) testForAnagramWithStrings:(NSString *)stringA andStringB: (NSString *)stringB{
stringA = [stringA lowercaseString];
stringB = [stringB lowercaseString];
int counter = 0;
for (int i=0; i< stringA.length; i++){
for (int j=0; j<stringB.length;j++){
if ([stringA characterAtIndex:i]==[stringB characterAtIndex:j]){
counter++;
}
}
}
if (counter!= stringA.length){
return false;
}
return true;
}

How to check NSMutableArray for objects with identical flags?

I have a Card object, that has a flag isFlipped. I store them in a NSMutableArray. I want to check if two objects in my array have the flag on, and if they do, I remove them.
As far as I understand I need to iterate over array, but how do I get another object with a flag?
- (void) checkCards
{
for (Card *card in cards) {
if (card.flipped)
{
if ( ??? )
{
}
}
}
}
Store the index of the cards that you want to remove in variables and if the value of both the variables are set then just remove the cards. See the following
- (void) checkCards {
int card1 = -1;
int card2 = -1;
for(int i = 0; i < [cards count]; i++) {
Card *card = [cards objectAtIndex: i];
if(card.flipped) {
if(card1 == -1) {
card1 = i;
} else {
card2 = i;
}
if(card1 != -1 && card2 != -1) {
// remove cards
break;
}
}
}
}
I would use the NSArray method, indexesOfObjectsPassingTest:. You can use it like this:
NSIndexSet *indexSet = [cards indexesOfObjectsPassingTest:^BOOL (Card *obj, NSUInteger idx, BOOL *stop) {
return obj.isFlipped = YES;
}];
[cards removeObjectsAtIndexes:indexSet];
This will remove all cards, whose isFlipped is YES, so if there could be more than 2, and you only want to remove 2, then you would have to iterate through the indexSet and stop after removing 2.