n00b question. Shuffling a deck (NSMutableArray)

Member
Posts: 22
Joined: 2007.05
Post: #1
Gents,

New to programming here and true to personal form I have jumped into the deep end. I am trying to model a deck of cards. I have used a mutable array and am trying to shuffle the deck using:

Code:
- (void)shuffleDeck {
    //some code to shuffle the deck.
    
    int i , j;
    id temp ;
    int size = [deck count];
    
    for( i = 0 ; i < size ; i ++ )
    {
        j = rand() % size ;
        temp = [NSString stringWithFormat:@"%d", deck objectAtIndex:i];
//        temp = [deck ObjectAtIndex:(unsigned)i];
        [deck exchangeObjectAtIndex:(unsigned)i withObjectAtIndex:(unsigned)j];
        [deck replaceObjectAtIndex:(unsigned)j withObject:(id)temp];
    }
    
    // TEST Some code to make sure it is working
    int x;
    int numberOfCards = [deck count];
    NSLog(@"After the shuffle the deck contains %d cards.\n", numberOfCards);
    for ( x = 0 ; x < numberOfCards ; x++) {
        NSLog(@"Card #%d is %@.\n", x, [deck objectAtIndex:(unsigned)x]);
    }
    
}

I know that it all looks hideous and that my code is doodoo but I am trying to learn by working on a project that interests me. Any help here would be of great assistance. Whenever I run the code above I get the error:

Code:
+[NSString stringWithFormat:objectAtIndex:]: selector not recognized

I am sure this is something simple but a smack on my knuckles and a point in the right direction would be greatly appreciated. TIA.
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #2
Instead of:

temp = [NSString stringWithFormat:@"%d", deck objectAtIndex:i];

You need to do:

temp = [NSString stringWithFormat:@"%d", [deck objectAtIndex:i]];
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #3
Oh, since you mentioned you're new to programming, I should point out that consistency of code formatting is important. One thing I noticed is that you do your braces (curly brackets, whatever) in two different ways for the same coding construct:
Code:
for (blah blah blah) {
...
}

for (blah blah blah)
{
...
}

Either is fine, although I personally prefer the second, but you should pick one style and stick with it throughout your code.

Looks like you're doing pretty good though.Smile
Quote this message in a reply
Member
Posts: 22
Joined: 2007.05
Post: #4
Thanks so much for the quick reply. Your tip fixed the problem I was having but a new one has popped up. (I suppose that is going to be a common theme in the programming world eh? ;-) ) So now the code reads:

Code:
- (void)shuffleDeck
{
    //some code to shuffle the deck.
    
    int i , j;
    id temp ;
    int size = [deck count];
    
    for( i = 0 ; i < size ; i ++ )
    {
        j = rand() % size ;
        temp = [NSString stringWithFormat:@"%d", [deck objectAtIndex:i]];                                                                                                                                                                        
        [deck exchangeObjectAtIndex:(unsigned)i withObjectAtIndex:(unsigned)j];
        [deck replaceObjectAtIndex:(unsigned)j withObject:(id)temp];
    }
    
    // TEST Some code to make sure it is working
    int x;
    int numberOfCards = [deck count];
    NSLog(@"After the shuffle the deck contains %d cards.\n", numberOfCards);
    for ( x = 0 ; x < numberOfCards ; x++)
    {
        NSLog(@"Card #%d is %@.\n", x, [deck objectAtIndex:(unsigned)x]);
    }
    
}

that compiles fine (thanks again Jake.) But I seem to be doing something wrong with the shuffle method as the result of the NSLog line looks like:

Code:
...
2007-05-31 20:31:37.619 WBPTDeck[553] Card #3 is 4s.
2007-05-31 20:31:37.619 WBPTDeck[553] Card #4 is Jc.
2007-05-31 20:31:37.619 WBPTDeck[553] Card #5 is 217660.
2007-05-31 20:31:37.619 WBPTDeck[553] Card #6 is 7h.
2007-05-31 20:31:37.619 WBPTDeck[553] Card #7 is 217164.
2007-05-31 20:31:37.619 WBPTDeck[553] Card #8 is 9d.
2007-05-31 20:31:37.620 WBPTDeck[553] Card #9 is 217244.
2007-05-31 20:31:37.620 WBPTDeck[553] Card #10 is 4d.
2007-05-31 20:31:37.620 WBPTDeck[553] Card #11 is 217740.
2007-05-31 20:31:37.620 WBPTDeck[553] Card #12 is 3769360.
2007-05-31 20:31:37.620 WBPTDeck[553] Card #13 is 3776192.
2007-05-31 20:31:37.620 WBPTDeck[553] Card #14 is 6c.
...

So somehow I borked it so that the return is sometimes the string value of the object in the array (which is the desired result) and at others it is something else. (not exactly sure what though.)
Quote this message in a reply
Moderator
Posts: 437
Joined: 2002.09
Post: #5
After doing an exchange of i & j, you then replace i's new location with a string you made of i and stored in temp. You don't need that as far as I can tell.

Furthermore, you've formatted temp as a string using "%d", but what you've passed to the format function isn't an integer, it's another string. This yields garbage.

Just remove the replaceObjectAtIndex line and it'll probably work. (This also means that temp is no longer needed.)

Measure twice, cut once, curse three or four times.
Quote this message in a reply
Member
Posts: 22
Joined: 2007.05
Post: #6
Works a charm. Thanks Matt.
Quote this message in a reply
Member
Posts: 22
Joined: 2007.05
Post: #7
Ok. More n00b questions here.

I have created an instance of my deck of cards. The instance has a few variables and 3 methods:

Code:
@interface WBPTDeck : NSObject
{
    NSMutableArray * deck;
    NSString * dealtCard;
    int * numCards, numSuits, dealt;    
}

- (void)setNewDeck;
- (void)shuffleDeck;
- (NSString *)dealACard;

How do I get the value of one of the instance variables? (in this case from my game controller) For the methods I call them like so:

Code:
[newDeck setNewDeck];

which works fine. But how do I get the value of the numCards variable? I am trying to do something like so:

Code:
[statusTextView insertText:(@"New Deck Setup Complete. The deck contains %d cards.\n\n", [newDeck numCards]);

Do I have to create a method to return the value of an instance variable? What about returning the value of one of the objects in the array? I envision something like:

Code:
[newDeck deck ObjectAtIndex:1]

Thanks for the patience and the help. I am surely hooked.
Quote this message in a reply
Member
Posts: 67
Joined: 2006.06
Post: #8
Make a get method

Code:
- (* String)getCardForIndex(int i) {
    return [deck objectAtIndex:i];
}

Also you look a little bit confused about how to call methods. Unless

Quote:[newDeck deck ObjectAtIndex:1]

was a mistake.

newDeck is the object, objectAtIndex is the method, 1 is the argument
The word deck doesn't need to be there.

The machine does not run without the coin.
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #9
StealthyCoin, that won't work either. It should be (assuming the objects in the deck array are NSStrings):
Code:
- (NSString *)getCardForIndex:(int)i
{
    return [deck objectAtIndex:i];
}

hypnotx, you almost have it right with your example (which I edited for correctness):
Code:
[statusTextView insertText:[NSString stringWithFormat@"New Deck Setup Complete. The deck contains %d cards.\n\n", [newDeck numCards]]]

To implement numCards in your WBPTDeck class you would do something like this:
Code:
- (int)numCards
{
    return [deck count];
}
Quote this message in a reply
Member
Posts: 67
Joined: 2006.06
Post: #10
Im going to go ahead and assume I meant - (* NSString)getCardForIndex(int)i {

Both those mistakes come from java Sad, maybe I should work on some of my cocoa projects to get back into the swing of things.


But in other news, if you are planning to do lots with those cards, it would be best to make a Card class instead of using a string. Something that has:
NSString *suit;
NSString *color;
int value;

Or you could use enum and make the color/suit into ints. And enum king queen jack.

I forget how enmu works I havn't used it in so long, isn't it
enum {KING, QUEEN, JACK};
and if you want specific values
enum {KING = 1, QUEEN = 2, JACK = 3};

The machine does not run without the coin.
Quote this message in a reply
Member
Posts: 22
Joined: 2007.05
Post: #11
I think I am catching on. Basically any info I need to pull from my deck instance has to be defined in an instance method. Correct?

Also, thanks for the tip on the cards. If I create a Card class then I would populate the Deck instance with an NSMutableArray that contains the Card instances.

Am I getting this right?
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #12
Yeah, it sounds like you got the idea. Smile

hypnot Wrote:Basically any info I need to pull from my deck instance has to be defined in an instance method. Correct?
Yes, that is correct.

StealthyCoin Wrote:Im going to go ahead and assume I meant - (* NSString)getCardForIndex(int)i {
Hehe, that still won't compile, but I won't pick on you since you haven't done Obj-C for a while. Wink

Your enum code is correct, and it isn't a bad approach. enums are a great way to do integer constants, and unlike defines, also get a little bit of type checking by the compiler. My only gripe with enums for beginners is that they might not be very intuitive. I seem to recall that they took me a long time to pick up when I was learning.

Making a card class is a good idea.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #13
Knuth Shuffle written in a generic manner.

Code:
// NSMutableArray_Shuffle.h
#import <Cocoa/Cocoa.h>

@interface NSMutableArray (NSMutableArray_Shuffle)

- (void)swapObjectAtIndex:(int)firstIndex withObjectAtIndex:(int)secondIndex;

- (void)shuffle;

@end

Code:
// NSMutableArray_Shuffle.m
#import "NSMutableArray_Shuffle.h"

@implementation NSMutableArray (NSMutableArray_Shuffle)

- (void)swapObjectAtIndex:(int)firstIndex withObjectAtIndex:(int)secondIndex
{
    id firstObject = [self objectAtIndex:firstIndex];
    id secondObject = [self objectAtIndex:secondIndex];
    
    [self replaceObjectAtIndex:firstIndex withObject:secondObject];
    [self replaceObjectAtIndex:secondIndex withObject:firstObject];
}

- (void)shuffle
{
    int i;
    for(i = 0; i < [self count]-1; i++)
        [self swapObjectAtIndex:i withObjectAtIndex:random()%([self count]-i)+i];
}

@end

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Member
Posts: 22
Joined: 2007.05
Post: #14
Thanks for the examples unknown. Blows the doors off what I have been working on. I am getting it slowly but surely. I am trying to find some more info on using enums now so that I can create my Card class. I want to write a few poker utilities and it seems my deck is the most important component so far. I am not looking forward to writing the parser for hand history text files but I will cross that bridge when I get to it. Thank you all for the help and pointers. I am having a blast with it.
Quote this message in a reply
Member
Posts: 22
Joined: 2007.05
Post: #15
I am playing around with the shuffle code from unknown above and I get an error at this line:

Code:
[self swapObjectAtIndex:i withObjectAtIndex:random()%([self count]-i)+i];

the error is
Quote: error: invalid operands to binary %
Quote this message in a reply
Post Reply