Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

MorphingDragon

macrumors 603
Original poster
Mar 27, 2009
5,160
6
The World Inbetween
Im designing a game engine for use with card games. (Ill post the source-code when I get back home) I have an NSObject Subclass called Card, which has the variables

Code:
//(S)pades, (C)lubs, (H)earts, (D)iamonds, (J)oker
char type;

//(1)Ace ... (11)Joker, (12)Queen, (13)King (0)Joker
int number;

//2 of Clubs, King of Spades etc
NSString *cardName;

and the method
Code:
-(void)setVariablesWithType:(char)t number:(int)n name:(NSString *)name {

type = t;
number = n;

switch (name) {
 switch code to set full name of cards
}

Anyway, I have a subclass of NSMutableArray called Deck with the methods (void)prepare; (Adds 54 cards to the deck) and (Card *)drawRandomCard; (Avoids the need for a rather complicated shuffle method, and deletes the card from the array)

I didn't quite think through the how I was going to implement the prepare method.

I thought of using some loops, [variable copy], a temp card object; and some preprocessor macros... But doesn't copy only copy the pointer and arrays only hold pointers? (Making the temporary object style useless)

Am I approaching this the wrong way?
 

gnasher729

Suspended
Nov 25, 2005
17,980
5,565
Just saying that subclassing an NSMutableArray for something like this is most likely not the right thing to do. Your deck of cards is not a mutable array. It should be its own class, derived from NSObject, and possibly have a member of type NSMutableArray or implement its data storage any other way you like.

If you make it a subclass of NSMutableArray, then I can use all NSMutableArray methods, and most likely the results will be nonsense.

And an NSArray holds reference counted objects. So you create an NSCard object (reference count 1), add it to the NSArray (reference count 2), and the drawRandomCard removes it from the array (reference count 1) and hands it to you (still reference count 1); you are now the owner.
 

MorphingDragon

macrumors 603
Original poster
Mar 27, 2009
5,160
6
The World Inbetween
I get up to preparing the deck then I get stuck at how to get what I want without a '10' being returned.
Code:
//
//  Card.h
//  CardDeck
//
//  Created by Robert Litchfield on 6/06/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import <Cocoa/Cocoa.h>


@interface Card : NSObject {
	//(S)pades, (C)lubs, (H)earts, (D)iamonds, (J)oker
	char suit;
	
	//(1)Ace ... (11)Joker, (12)Queen, (13)King (0)Joker
	int number;
	
	//2 of Clubs, King of Spades etc
	NSMutableString *cardName;
}
@property (readwrite, copy) NSMutableString *cardName;
@property (readwrite) int number;
@property (readwrite) char suit;

-(void)setVariablesWithType:(char)t number:(int)n;
-(void)print;
-(void)dealloc;
@end
Code:
//
//  Card.m
//  CardDeck
//
//  Created by Robert Litchfield on 6/06/10.
//  Copyright 2010 MissingBoxStudio. All rights reserved.
//

#import "Card.h"


@implementation Card
	
@synthesize cardName;
@synthesize number;
@synthesize suit;

-(void)setVariablesWithType:(char)t number:(int)n{
	suit = t;
	number = n;

	NSString *tempString = [[NSString alloc] init];
	NSString *temp2String = [[NSString alloc] init];
	
	//Merge Strings
	//Preferbly, find a way to use less lines of code. 
	
	switch (n) {
		case 1:
			tempString = @"Ace of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 2:
			tempString = @"2 of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 3:
			tempString = @"3 of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 4:
			tempString = @"4 of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 5:
			tempString = @"5 of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 6:
			tempString = @"6 of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 7:
			tempString = @"7 of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 8:
			tempString = @"8 of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 9:
			tempString = @"9 of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 10:
			tempString = @"10 of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 11:
			tempString = @"Jack of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 12:
			tempString = @"Queen of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 13:
			tempString = @"King of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		default:
			tempString = @"Joker";
			cardName = [NSMutableString stringWithString:tempString];
			break;
	}
	
	switch (t) {
		case 's':
			temp2String = @"Spades";
			[cardName appendString:temp2String];
			break;
		case 'c':
			temp2String = @"Clubs";
			[cardName appendString:temp2String];
			break;
		case 'd':
			temp2String = @"Diamonds";
			[cardName appendString:temp2String];
			break;
		case 'h':
			temp2String = @"Hearts";
			[cardName appendString:temp2String];
			break;
		default:
			break;
	}
	[tempString release];
	[temp2String release];
	
	[self print];
}

-(void)print{
	//For debugging purposes
	NSLog(@"%c, %i, %@", suit, number, cardName);
}
-(void)dealloc{
	[cardName release];
	[super dealloc];
}
@end
Code:
//
//  Deck.h
//  CardDeck
//
//  Created by Robert Litchfield on 7/06/10.
//  Copyright 2010 MissingBoxStudio. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import "Card.h"


@interface Deck : NSObject {
	NSMutableArray *deck;
	NSMutableArray *usedPile;
}

-(void)prepare;
-(Card *)drawRandomCard;
-(void)printDeck;
-(void)printUsedPile;
@end
Code:
//
//  Deck.m
//  CardDeck
//
//  Created by Robert Litchfield on 7/06/10.
//  Copyright 2010 __MyCompanyName__. All rights reserved.
//

#import "Deck.h"


@implementation Deck

-(void)prepare {
	int i;
	char suit;
	
	[deck release];
	[usedPile release];
		
    Card *tempCard = [[Card alloc] init];
	suit = 's';
	for (i = 1; i <= 13; ++i) {
		[tempCard setVariablesWithType:suit number:i];
		//Add object by copying tempCard
	}
	//[tempCard release];
}
-(void)printDeck {
	NSLog(@"Printing Deck");
}
@end

Maybe I should be more clearer at what I want to do...

I want to quickly create an array of 54 Card objects with correct suit, number, name etc.
 

JoshDC

macrumors regular
Apr 8, 2009
115
0
How about making the suit and value a couple of enums?

Code:
typedef enum Suit {
	Spade = 0,
	Heart = 1,
	Club = 2,
	Diamond = 3,
} Suit;

Similarly for the values. Then just two loops:

Code:
for (int suit = 0; suit < 4; suit++) {
	for (int value = 1; value < 14; value++) {
		[tempCard setVariablesWithType:suit number:value];
	}
}

I don't quite understand why you're storing the cardName. Wouldn't it be easier to have it a read only property that gets calculated when it's read?

Also the value switch statement could be:

Code:
	switch (n) {
		case 1:
			tempString = @"Ace of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 11:
			tempString = @"Jack of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 12:
			tempString = @"Queen of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		case 13:
			tempString = @"King of ";
			cardName = [NSMutableString stringWithString:tempString];
			break;
		default:
			tempString = [NSString stringWithFormat:@"%i of", n];
			cardName = [NSMutableString stringWithString:tempString];
			break;
	}

I'm ignoring the jokers for simplicity, shouldn't be hard to add them though. A joker can be red or black if it matters to your game.
 

Littleodie914

macrumors 68000
Jun 9, 2004
1,813
8
Rochester, NY
Note: All this code is untested, I don't have Xcode with me right now. :)

I have a few suggestions. First, I don't think that your Card type should be mutable. You should be able to create a card using an init method like so:

Code:
- (id)initWithSuit:(char)suit number:(int)number;

This avoids the copy calls altogether, though I want to mention that the copy method does copy the actual object. The object returned from copy should be completely independent from the original, and should be treated as a newly-allocated and newly-initialized object that you are responsible for retaining/releasing.

Your cardName method is a bit obfuscated, as all instances of NSObject respond to a method:

Code:
- (NSString *)description;

You should probably move your cardName code into description.

Finally, I think it would be helpful to maintain Suit as its own class, simply so it can provide its own implementation of the description method as well. Then, in your description method in Card, you could save a large amount of code by implementing it as:
Code:
- (NSString *)description {
    NSString *cardName = [[NSString alloc] initWithFormat:@"%d of %@", number, [suit description]];
    
    return [cardName autorelease];
}

Finally, don't implement accessor methods (getters/setters) that require more than one variable. Eg. write 'setNumber' and 'setSuit', not 'setNumber andSuit'. What if you only want to set one of those values? A good time (really the only good time) to provide an object with all of the information it needs is at initialization, hence the init method I mentioned above.

Good luck, happy coding! :)

Edit: In the description implementation of Card, I forgot to include handling cases when the 'number' field is not intended to represent a number at all. Perhaps in addition to making Suit its own class, you should also create a FaceValue class that encapsulates the type of card. Then FaceValue could implement description and return @"2" for number types, and @"Queen" for other types.
 

MorphingDragon

macrumors 603
Original poster
Mar 27, 2009
5,160
6
The World Inbetween
"cardName" is only temporary, and will be removed when I'm happy with the backend logic. Its just being used to help readability of the outputs.

---

Ill try some of those things tomorrow, its 3AM here. @_@.

Finally, don't implement accessor methods (getters/setters) that require more than one variable. Eg. write 'setNumber' and 'setSuit', not 'setNumber andSuit'. What if you only want to set one of those values? A good time (really the only good time) to provide an object with all of the information it needs is at initialization, hence the init method I mentioned above.

The setter/getter methods for the variables are created by the @synthesize directive.
 

chown33

Moderator
Staff member
Aug 9, 2009
10,751
8,423
A sea of green
Hint to simplify your switch statement:

Code:
static NSArray* cardNames = [[NSArray arrayWithObjects:
  @"Joker", @"Ace", @"2", @"3", ... 
  @"Jack", @"Queen", @"King", nil ] retain];

The first hint should have been the fact that the case values were simple sequential numbers, and each case had virtually identical repeated code. That's exactly the kind of pattern that should scream "Simplify me!".

By the way, the red-hilited fragment is a lying comment:
Code:
	//(1)Ace ... [COLOR="Red"](11)Joker[/COLOR], (12)Queen, (13)King (0)Joker

And you're doing the memory management wrong in your implementation of setVariablesWithType:number:.
 

lloyddean

macrumors 65816
May 10, 2009
1,047
19
Des Moines, WA
Seems to me you're making this way more compicated than it needs to be.

A 'card' can simply be a 'char'

A 'deck' an array of 52 'card's.

Init the 'deck' array with the values 0 - 51.

Shuffle the 'deck' with a single loop iterating every entry, 0 - 51, in the 'deck' and swapping it with another 'card' at a random in position, 0 - 51, within the 'deck'.

The face value and suite are simple to derive.

suit = card / 13; // produces 0 - 3
face = card % 13; // produces 0 - 12

Suite names can also be looked up in a simple array:

const char* suiteName[] = { "C", "D", "H", "S" };

Card value names can be looked up in simple array:

const char* faceName[] = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };

So cards are nothing more than a number 0 - 51 and everything else derived from the card numer.
 

MorphingDragon

macrumors 603
Original poster
Mar 27, 2009
5,160
6
The World Inbetween
Seems to me you're making this way more compicated than it needs to be.

A 'card' can simply be a 'char'

A 'deck' an array of 52 'card's.

Init the 'deck' array with the values 0 - 51.

Shuffle the 'deck' with a single loop iterating every entry, 0 - 51, in the 'deck' and swapping it with another 'card' at a random in position, 0 - 51, within the 'deck'.

The face value and suite are simple to derive.

suit = card / 13; // produces 0 - 3
face = card % 13; // produces 0 - 12

Suite names can also be looked up in a simple array:

const char* suiteName[] = { "C", "D", "H", "S" };

Card value names can be looked up in simple array:

const char* faceName[] = { "A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K" };

So cards are nothing more than a number 0 - 51 and everything else derived from the card numer.

(For me) Its easier to work with Cocoa when I have Foundation objects. Plus having an object say 'Player' with an array for their Hand, their Pile in some games could make some of the logic easier. (I say could as Im at my parent's house and only have a laptop running GNUStep under linux and cant really test the code)

then you could say [player1/self/whatever drawCard:[deck drawRandomCard]];

And why create a complicated loop to shuffle the array when you could just draw it randomly on the spot.

int i = random() % [deck size//Whatever the proper ArrayLength method is];
return [deck objectAtIndex:i];

Then if the deck runs out, you can use arrayWithArray: to replace the deck with the used pile.
 

MorphingDragon

macrumors 603
Original poster
Mar 27, 2009
5,160
6
The World Inbetween
New code (Mostly works :d)

Code:
//
//  Card.h
//  CardDeck
//
//  Created by Robert Litchfield on 6/06/10.
//  Copyright 2010 MissingBoxStudio. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface Card : NSObject {
	//(S)pades, (C)lubs, (H)earts, (D)iamonds, (J)oker
	char suit;
	//(1)Ace ... (11)Jack, (12)Queen, (13)King (0)Joker
	int number;
}
@property (readwrite) int number;
@property (readwrite) char suit;

-(id)initWithSuit:(char)t number:(int)n;
-(void)setNumber:(int)i andSuit:(char)c;
-(void)print;
@end
Code:
//
//  Card.m
//  CardDeck
//
//  Created by Robert Litchfield on 6/06/10.
//  Copyright 2010 MissingBoxStudio. All rights reserved.
//

#import "Card.h"

@implementation Card
	
@synthesize number;
@synthesize suit;

-(id)initWithSuit:(char)t number:(int)n{
	//Initilaise Card Object
	[super init];
	
	//Set Variables
	suit = t;
	number = n;
	
	return self;
}
-(void)setNumber:(int)i andSuit:(char)c {
	suit = c;
	number = i;
}
-(void)print{
	//For debugging purposes
	NSLog(@"Card: %c, %i, %@", suit, number, [self description]);
}	
-(NSString *)description{
	//For debugging purposes
	
	NSString *cardName = [[NSString alloc] init];
	NSString *numberName = [[NSString alloc] init];
	NSString *suitName = [[NSString alloc] init];
	
	//Generate Card Name
	switch ([self number]) {
		case 0:
			numberName = [NSString stringWithString:@"Joker"];
			break;
		case 1:
			numberName = [NSString stringWithString:@"Ace of "];
			break;
		case 11:
			numberName = [NSString stringWithString:@"Jack of "];
			break;
		case 12:
			numberName = [NSString stringWithString:@"Queen of "];
			break;
		case 13:
			numberName = [NSString stringWithString: @"King of "];
			break;
		default:
			numberName = [NSString stringWithFormat:@"%i of ", [self number]];
			break;
	}	
	
	switch ([self suit]) {
		case 'j':
			suitName = [NSString stringWithString:@"Joker"];
			break;
		case 's':
			suitName = [NSString stringWithString:@"Spades"];
			break;
		case 'c':
			suitName = [NSString stringWithString:@"Clubs"];
			break;
		case 'd':
			suitName = [NSString stringWithString:@"Diamonds"];
			break;
		case 'h':
			suitName = [NSString stringWithString:@"Hearts"];
			break;
		default:
			break;
	}
	
	cardName = [numberName stringByAppendingString:suitName];
	
	[numberName release];
	[suitName release];
	
	//I get double release error trying to release cardName... help?
	return cardName;
}
@end
Code:
//
//  Deck.h
//  CardDeck
//
//  Created by Robert Litchfield on 7/06/10.
//  Copyright 2010 MissingBoxStudio. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "Card.h"

@interface Deck : NSObject {
	NSMutableArray *deck;
	NSMutableArray *graveyard;
}

-(void)prepare;
-(Card *)drawRandomCard;
-(void)printDeck;
-(void)printGraveyard;
@end
Code:
//
//  Deck.m
//  CardDeck
//
//  Created by Robert Litchfield on 7/06/10.
//  Copyright 2010 MissingBoxStudio. All rights reserved.
//

#import "Deck.h"

@implementation Deck

-(id)init{
    if ((self = [super init])) {
		srandom(time(NULL));
		
		deck = [[NSMutableArray alloc] initWithCapacity:54];
		graveyard = [[NSMutableArray alloc] initWithCapacity:54];
    }
    return self;
}
-(void)dealloc {
		
	NSLog(@"Releasing Arrays");
	[deck release];
	[graveyard release];
	
	
	NSLog(@"Deallocating");
	[super dealloc];
}
-(void)prepare {
	//Destroy current Deck arrays if any
	[deck removeAllObjects];
	[graveyard removeAllObjects];
	
	//Prepare new deck
	
	int i;
	char c;
	
	for (i = 1, c = 's'; i <= 13; ++i) { 
		[deck addObject:[[Card alloc] initWithSuit:c number:i]];
	}
	for (i = 1, c = 'c'; i <= 13; ++i) { 
		[deck addObject:[[Card alloc] initWithSuit:c number:i]];
	}
	for (i = 1, c = 'd'; i <= 13; ++i) { 
		[deck addObject:[[Card alloc] initWithSuit:c number:i]];
	}
	for (i = 1, c = 'h'; i <= 13; ++i) { 
		[deck addObject:[[Card alloc] initWithSuit:c number:i]];
	}
	for (i = 1, c = 'j'; i <= 2; ++i) { 
		[deck addObject:[[Card alloc] initWithSuit:c number:0]];
	}	
	
	[self printDeck];
	
}
-(void)printDeck {
	int i;
	for (i = 0; i < [deck count]; ++i ) {
		NSLog(@"Deck: %@", [[deck objectAtIndex:i] description]);
	}
}
-(void)printGraveyard {
	int i;
	for (i = 0; i < [graveyard count]; ++i ) {
		NSLog(@"Graveyard: %@", [[graveyard objectAtIndex:i] description]);
	}
}
-(Card *)drawRandomCard {
	int i =  random() % [deck count];
	
	Card *temp = [deck objectAtIndex:i];
	[deck removeObjectAtIndex:i];

	NSLog(@"Drew %@", [temp description]);
	
	return temp;
}
@end

Im having two problems,

When creating the Description string, trying to autorelease cardName returns a double release error.

I also have this strange error appearing, when deallocating a Deck object and when it is listing the contents of the deck.

Code:
The Debugger has exited with status 0.
[Session started at 2010-06-07 21:52:40 +1200.]
2010-06-07 21:52:40.790 CardDeck[8643:10b] Deck: Ace of Spades
2010-06-07 21:52:40.797 CardDeck[8643:10b] Deck: 2 of Spades
2010-06-07 21:52:40.800 CardDeck[8643:10b] Deck: 3 of Spades
2010-06-07 21:52:40.803 CardDeck[8643:10b] Deck: 4 of Spades
2010-06-07 21:52:40.806 CardDeck[8643:10b] Deck: 5 of Spades
2010-06-07 21:52:40.809 CardDeck[8643:10b] Deck: 6 of Spades
2010-06-07 21:52:40.812 CardDeck[8643:10b] Deck: 7 of Spades
2010-06-07 21:52:40.814 CardDeck[8643:10b] Deck: 8 of Spades
2010-06-07 21:52:40.817 CardDeck[8643:10b] Deck: 9 of Spades
2010-06-07 21:52:40.820 CardDeck[8643:10b] Deck: 10 of Spades
2010-06-07 21:52:40.822 CardDeck[8643:10b] Deck: Jack of Spades
2010-06-07 21:52:40.824 CardDeck[8643:10b] Deck: Queen of Spades
2010-06-07 21:52:40.827 CardDeck[8643:10b] Deck: King of Spades
2010-06-07 21:52:40.830 CardDeck[8643:10b] Deck: Ace of Clubs
2010-06-07 21:52:40.832 CardDeck[8643:10b] Deck: 2 of Clubs
2010-06-07 21:52:40.834 CardDeck[8643:10b] Deck: 3 of Clubs
2010-06-07 21:52:40.837 CardDeck[8643:10b] Deck: 4 of Clubs
2010-06-07 21:52:40.839 CardDeck[8643:10b] Deck: 5 of Clubs
2010-06-07 21:52:40.843 CardDeck[8643:10b] Deck: 6 of Clubs
2010-06-07 21:52:40.846 CardDeck[8643:10b] Deck: 7 of Clubs
2010-06-07 21:52:40.848 CardDeck[8643:10b] Deck: 8 of Clubs
2010-06-07 21:52:40.851 CardDeck[8643:10b] Deck: 9 of Clubs
2010-06-07 21:52:40.853 CardDeck[8643:10b] Deck: 10 of Clubs
2010-06-07 21:52:40.855 CardDeck[8643:10b] Deck: Jack of Clubs
2010-06-07 21:52:40.857 CardDeck[8643:10b] Deck: Queen of Clubs
2010-06-07 21:52:40.860 CardDeck[8643:10b] Deck: King of Clubs
2010-06-07 21:52:40.862 CardDeck[8643:10b] Deck: Ace of Diamonds
2010-06-07 21:52:40.865 CardDeck[8643:10b] Deck: 2 of Diamonds
2010-06-07 21:52:40.867 CardDeck[8643:10b] Deck: 3 of Diamonds
2010-06-07 21:52:40.870 CardDeck[8643:10b] Deck: 4 of Diamonds
2010-06-07 21:52:40.872 CardDeck[8643:10b] Deck: 5 of Diamonds
2010-06-07 21:52:40.874 CardDeck[8643:10b] Deck: 6 of Diamonds
2010-06-07 21:52:40.877 CardDeck[8643:10b] Deck: 7 of Diamonds
2010-06-07 21:52:40.879 CardDeck[8643:10b] Deck: 8 of Diamonds
2010-06-07 21:52:40.882 CardDeck[8643:10b] Deck: 9 of Diamonds
2010-06-07 21:52:40.884 CardDeck[8643:10b] Deck: 10 of Diamonds
2010-06-07 21:52:40.886 CardDeck[8643:10b] Deck: Jack of Diamonds
2010-06-07 21:52:40.889 CardDeck[8643:10b] Deck: Queen of Diamonds
2010-06-07 21:52:40.892 CardDeck[8643:10b] Deck: King of Diamonds
2010-06-07 21:52:40.895 CardDeck[8643:10b] Deck: Ace of Hearts
2010-06-07 21:52:40.897 CardDeck[8643:10b] Deck: 2 of Hearts
2010-06-07 21:52:40.899 CardDeck[8643:10b] Deck: 3 of Hearts
2010-06-07 21:52:40.902 CardDeck[8643:10b] Deck: 4 of Hearts
2010-06-07 21:52:40.906 CardDeck[8643:10b] Deck: 5 of Hearts
2010-06-07 21:52:40.909 CardDeck[8643:10b] Deck: 6 of Hearts
2010-06-07 21:52:40.913 CardDeck[8643:10b] Deck: 7 of Hearts
2010-06-07 21:52:40.965 CardDeck[8643:10b] Deck: 8 of Hearts
2010-06-07 21:52:40.967 CardDeck[8643:10b] Deck: 9 of Hearts
2010-06-07 21:52:40.970 CardDeck[8643:10b] Deck: 10 of Hearts
2010-06-07 21:52:40.972 CardDeck[8643:10b] Deck: Jack of Hearts
2010-06-07 21:52:40.973 CardDeck[8643:10b] Deck: Queen of Hearts
2010-06-07 21:52:40.978 CardDeck[8643:10b] Deck: King of Hearts
2010-06-07 21:52:40.981 CardDeck[8643:10b] Deck: JokerJoker
2010-06-07 21:52:40.984 CardDeck[8643:10b] Deck: JokerJoker
2010-06-07 21:52:40.986 CardDeck[8643:10b] Drew 9 of Clubs
2010-06-07 21:52:40.989 CardDeck[8643:10b] Drew King of Diamonds
2010-06-07 21:52:40.990 CardDeck[8643:10b] Drew JokerJoker
2010-06-07 21:52:40.994 CardDeck[8643:10b] Drew 7 of Spades
2010-06-07 21:52:40.996 CardDeck[8643:10b] Drew 8 of Hearts
2010-06-07 21:52:40.998 CardDeck[8643:10b] Drew 4 of Spades
2010-06-07 21:52:41.001 CardDeck[8643:10b] Drew Jack of Diamonds
2010-06-07 21:52:41.003 CardDeck[8643:10b] Drew 2 of Hearts
2010-06-07 21:52:41.005 CardDeck[8643:10b] Deck: Ace of Spades
2010-06-07 21:52:41.006 CardDeck[8643:10b] Deck: 2 of Spades
2010-06-07 21:52:41.007 CardDeck[8643:10b] Deck: 3 of Spades
2010-06-07 21:52:41.011 CardDeck[8643:10b] Deck: 5 of Spades

[Session started at 2010-06-07 21:52:41 +1200.]
GNU gdb 6.3.50-20050815 (Apple version gdb-967) (Tue Jul 14 02:15:14 UTC 2009)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "powerpc-apple-darwin".sharedlibrary apply-load-rules all
2010-06-07 21:52:41.014 CardDeck[8643:10b] Deck: 6 of Spades
2010-06-07 21:52:41.016 CardDeck[8643:10b] Deck: 8 of Spades
2010-06-07 21:52:41.019 CardDeck[8643:10b] Deck: 9 of Spades
2010-06-07 21:52:41.021 CardDeck[8643:10b] Deck: 10 of Spades
2010-06-07 21:52:41.023 CardDeck[8643:10b] Deck: Jack of Spades
2010-06-07 21:52:41.024 CardDeck[8643:10b] Deck: Queen of Spades
2010-06-07 21:52:41.028 CardDeck[8643:10b] Deck: King of Spades
2010-06-07 21:52:41.030 CardDeck[8643:10b] Deck: Ace of Clubs
2010-06-07 21:52:41.032 CardDeck[8643:10b] Deck: 2 of Clubs
2010-06-07 21:52:41.034 CardDeck[8643:10b] Deck: 3 of Clubs
2010-06-07 21:52:41.037 CardDeck[8643:10b] Deck: 4 of Clubs
2010-06-07 21:52:41.150 CardDeck[8643:10b] Deck: 5 of Clubs
2010-06-07 21:52:41.152 CardDeck[8643:10b] Deck: 6 of Clubs
2010-06-07 21:52:41.158 CardDeck[8643:10b] Deck: 7 of Clubs
2010-06-07 21:52:41.161 CardDeck[8643:10b] Deck: 8 of Clubs
2010-06-07 21:52:41.164 CardDeck[8643:10b] Deck: 10 of Clubs
2010-06-07 21:52:41.216 CardDeck[8643:10b] Deck: Jack of Clubs
2010-06-07 21:52:41.218 CardDeck[8643:10b] Deck: Queen of Clubs
2010-06-07 21:52:41.221 CardDeck[8643:10b] Deck: King of Clubs
2010-06-07 21:52:41.223 CardDeck[8643:10b] Deck: Ace of Diamonds
2010-06-07 21:52:41.223 CardDeck[8643:10b] Deck: 2 of Diamonds
2010-06-07 21:52:41.228 CardDeck[8643:10b] Deck: 3 of Diamonds
2010-06-07 21:52:41.230 CardDeck[8643:10b] Deck: 4 of Diamonds
2010-06-07 21:52:41.232 CardDeck[8643:10b] Deck: 5 of Diamonds
2010-06-07 21:52:41.235 CardDeck[8643:10b] Deck: 6 of Diamonds
2010-06-07 21:52:41.238 CardDeck[8643:10b] Deck: 7 of Diamonds
2010-06-07 21:52:41.240 CardDeck[8643:10b] Deck: 8 of Diamonds
2010-06-07 21:52:41.243 CardDeck[8643:10b] Deck: 9 of Diamonds
2010-06-07 21:52:41.245 CardDeck[8643:10b] Deck: 10 of Diamonds
2010-06-07 21:52:41.247 CardDeck[8643:10b] Deck: Queen of Diamonds
2010-06-07 21:52:41.249 CardDeck[8643:10b] Deck: Ace of Hearts
2010-06-07 21:52:41.252 CardDeck[8643:10b] Deck: 3 of Hearts
2010-06-07 21:52:41.254 CardDeck[8643:10b] Deck: 4 of Hearts
2010-06-07 21:52:41.257 CardDeck[8643:10b] Deck: 5 of Hearts
2010-06-07 21:52:41.260 CardDeck[8643:10b] Deck: 6 of Hearts
2010-06-07 21:52:41.263 CardDeck[8643:10b] Deck: 7 of Hearts
2010-06-07 21:52:41.265 CardDeck[8643:10b] Deck: 9 of Hearts
2010-06-07 21:52:41.267 CardDeck[8643:10b] Deck: 10 of Hearts
2010-06-07 21:52:41.270 CardDeck[8643:10b] Deck: Jack of Hearts
2010-06-07 21:52:41.272 CardDeck[8643:10b] Deck: Queen of Hearts
2010-06-07 21:52:41.274 CardDeck[8643:10b] Deck: King of Hearts
2010-06-07 21:52:41.277 CardDeck[8643:10b] Deck: JokerJoker
2010-06-07 21:52:41.279 CardDeck[8643:10b] Graveyard: 9 of Clubs
2010-06-07 21:52:41.281 CardDeck[8643:10b] Graveyard: King of Diamonds
2010-06-07 21:52:41.282 CardDeck[8643:10b] Graveyard: JokerJoker
2010-06-07 21:52:41.287 CardDeck[8643:10b] Graveyard: 7 of Spades
2010-06-07 21:52:41.347 CardDeck[8643:10b] Graveyard: 8 of Hearts
2010-06-07 21:52:41.350 CardDeck[8643:10b] Graveyard: 4 of Spades
2010-06-07 21:52:41.352 CardDeck[8643:10b] Graveyard: Jack of Diamonds
2010-06-07 21:52:41.354 CardDeck[8643:10b] Graveyard: 2 of Hearts
2010-06-07 21:52:41.355 CardDeck[8643:10b] 1
2010-06-07 21:52:41.356 CardDeck[8643:10b] Releasing Arrays
2010-06-07 21:52:41.357 CardDeck[8643:10b] Deallocating
Attaching to process 8643.
(gdb)

Ive spent about 2 hours now in the documentation trying different versions of dealloc. The debugger is highlighting [pool drain].

Stepping into returns this code.
Code:
sharedlibrary apply-load-rules all
Attaching to process 9155.
Single stepping until exit from function objc_msgSend_rtp, 
which has no line number information.
Current language:  auto; currently objective-c
Program received signal:  “EXC_BAD_ACCESS”.
kill

EDIT: It definitely has something to do with draining the pool in my test code. Everything works perfectly apart from when it comes to draining a pool. It works fine with GC or removing the autorelease pool, but that doesn't make any sense.... Getting it working without GC would be awesome though.

EG, this works perfectly. (Notice the lack of memory management coding)

Code:
#import <Foundation/Foundation.h>
#import "Card.h"
#import "Deck.h"

int main (int argc, const char * argv[]) {
	Deck *gameDeck = [[Deck alloc] init];
	[gameDeck prepare];
	
	int i, c;
	for (i = 0, c = [[gameDeck deck] count] - 1; i <= c; ++i) {
		[gameDeck drawRandomCard];
	}
	
	[gameDeck printDeck];
	NSLog(@"Again");
	[gameDeck prepare];
    return 0;
}

EDIT2: Critique welcome.
 

Catfish_Man

macrumors 68030
Sep 13, 2001
2,579
2
Portland, OR
Didn't read all of it, but a suggestion (note: having the loop variable declarations in the loop header requires being in C99 mode. Googling Xcode C99 mode should find instructions on where that's set easily enough):
Code:
-(void)prepare {
	//Destroy current Deck arrays if any
	[deck removeAllObjects];
	[graveyard removeAllObjects];
	
	//Prepare new deck
        char *suits = "scdh";
        for (int suitIdx = 0; suitIdx < 4; ++suitIdx) {
            for (int i = 1, c = 's'; i <= 13; ++i) { 
		[deck addObject:[[[Card alloc] initWithSuit:suits[suitIdx] number:i]] autorelease];
	    }
        }

        //Add the jokers; this could be a loop as you had it,
        //but I think with only two iterations it's clearer that it's "different from the others" this way
        Card *joker = [[[Card alloc] initWithSuit:'j' number:0] autorelease];
        [deck addObject: joker];
        [deck addObject: [[joker copy] autorelease]];
	
	[self printDeck];
}
 

chown33

Moderator
Staff member
Aug 9, 2009
10,751
8,423
A sea of green
Code:
-(NSString *)description{
	//For debugging purposes
	
	NSString *cardName = [[NSString alloc] init];
	NSString *numberName = [[NSString alloc] init];
	NSString *suitName = [[NSString alloc] init];
Exactly what are those 3 assignments intended to do? Who owns the objects? What effect does ownership have later, when you assign another value to the variables?

In what way is this code better than:
Code:
	NSString *cardName = @"";
or:
Code:
	NSString *cardName = nil;
Your code looks like cargo-cult programming to me.

Code:
	cardName = [numberName stringByAppendingString:suitName];
	
	[numberName release];
	[suitName release];
	
	//I get double release error trying to release cardName... help?
	return cardName;

Who has ownership of numberName and suitName at the point you are calling release on them? If you don't know, then you should review the memory management guide's rules of ownership.

Who has ownership of cardName when it's being returned? Is it autoreleased or not at the point it's returned?
 

Sydde

macrumors 68030
Aug 17, 2009
2,552
7,050
IOKWARDI
If you are doing live draws, the deck could simply be a long long. Methods would look something like (.h file omitted):

Code:
- (void)shuffle { theDeck = 0; }
- (NSString *)drawCard {
    NSString    *theDraw;
    // these constants would actually be ivars
    char    *suits[4] = { "Diamonds", "Clubs", "Hearts", "Spades" };
    char    *faces[4] = { "Jack", "Queen", "King", "Ace" };
    long long  cardMask;
    int     aCard;
    int     cardVals;
    BOOL didDraw = NO;
    if ( -1 == theDeck ) return @""; // no cards left
    while ( !didDraw ) {
        aCard = random() % 54;
        cardMask = 1 << aCard;
        didDraw = ( 0 == ( cardMask&theDeck ) );
        if ( didDraw ) theDeck |= cardMask;
    }
    if ( 51 < aCard ) {
        // set the draw ivars to a Joker value
        lastDrawnCard = -1;
        lastDrawnSuit = -1;
        return @"Joker";
    } else {
        // get the suit
        cardVals = aCard % 13;
        if ( cardVals > 8 ) {
            // note: Ace = 12 (highest number )
            theDraw = [NSString stringWithCString:faces[cardVals - 9]];
        } else {
            theDraw = [NSString stringWithFormat:@"%d", cardVals + 2];
        }
        // save the card number in an ivar
       lastDrawnCard = cardVals;
        cardVals = aCard / 13;
        theDraw = [theDraw appendFormat:@" of %s",[NSString stringWithCString:suits[cardVals]];
        // save the suit in an ivar for reference
        lastDrawnSuit = cardVals;
}
- (int)lastDraw {
    // lowest byte is the card value, 2-10, Jack = 11, Ace = 14
    // higher byte is the suit number
    return ( lastDrawnCard + 2 )|( lastDrawnSuit << 8 );
}
 

PatrickCocoa

macrumors 6502a
Dec 2, 2008
751
149
Make sure to do some tests of randomness on your final output. Random numbers are trickier than they appear, and things that you do with them that look like they should deliver random results sometimes don't. There is a lot of practical literature on this issue.

When you think you're done, deal a hand and note the first card. Do that 100 times and look at the data you collect. No spades? No aces? There may be a problem.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.