random number

Kaamoss
Unregistered
 
Post: #1
This is a silly question but I need to get a random number between 1 and 3 from some code I'm writing. At this point I am using the line:
random = rand()%(3+1);

This is clearly the incoract syntax for what I am trying to accomplish. If you know how I would go about doing what I want to do that would be awesome.

Thanks.
Quote this message in a reply
Member
Posts: 184
Joined: 2004.07
Post: #2
You are thinking of
Code:
int random = ( rand() % 3 ) + 1;
Because % n will give the remainder, which will be between 0 and n inclusive.

By the way, when you take an integer random number generator modulo a number, the result is not completely random, though it's usually close enough for most purposes. If you want to make the built-in rand more random you can use this:
Code:
int random = ( (rand()*1.0)/RAND_MAX ) * 3;
Which will still give 0,1 or 2, but more randomly.
Quote this message in a reply
Puzzler183
Unregistered
 
Post: #3
Theoretically speaking, that's actually no more random. Any way you do it you have something pseudorandom (and not even a good one, Mersenne Twister is much better & faster) and you won't get an even distribution (although for this small modulus it's only slightly off).
Quote this message in a reply
Moderator
Posts: 613
Joined: 2004.09
Post: #4
You can get a new set of random numbers each time your app is launched by seeding the random number with the current time, this way it will be random each time the user views it.

Kyle Richter
DragonForged.com
Twitter: @kylerichter
Quote this message in a reply
Kaamoss
Unregistered
 
Post: #5
Isuppose that I could seed it with the time but since what I am doing is simple I don't think it's necessary. All that is happening is that an array of 52 instances of a class are being separated into two equal arrays of twenty six. Then the contents of one array is added back the the main array x number of random times between 1 and 3. This is done alternating through the two 26 length arrays untill all of the elements from one of the arrays have been used, then the rest are thrown in at the end. This is done a random amount of times. I was figuring around 8-12 times. This should randomize it up relativly well I would think. My description seems a bit hard to follow. I could post it in code if that would be easier.
Quote this message in a reply
Puzzler183
Unregistered
 
Post: #6
kodex Wrote:You can get a new set of random numbers each time your app is launched by seeding the random number with the current time, this way it will be random each time the user views it.

That of course works unless you want to be able to repeat the "random" numbers.
Quote this message in a reply
Moderator
Posts: 613
Joined: 2004.09
Post: #7
if you wanted to have repeatable random numbers then why dont you just make a number array and call from that? Using a repeatable random number sequence seems kind of pointless, since depending on your FPU your array of random numbers may differ from comp to comp.

Kyle Richter
DragonForged.com
Twitter: @kylerichter
Quote this message in a reply
Kaamoss
Unregistered
 
Post: #8
The random numbers need not be repeatable
Quote this message in a reply
Puzzler183
Unregistered
 
Post: #9
Well, it isn't pointless given that if you want to play back games, you need it and it is completely repeatable since internally, integers are used. The OP doesn't need it but it's definitely something to think about since a lot of games do.
Quote this message in a reply
Member
Posts: 208
Joined: 2005.04
Post: #10
If you want a different set of random numbers each time your program is run, you must seed the generator. It's really easy:

Code:
srand(time(NULL));

or, if you want to use random() instead of rand() - because random() is more random - you can do this:

Code:
srandom(time(NULL));

Just call one of these once at the top of your main() function.
Quote this message in a reply
Moderator
Posts: 770
Joined: 2003.04
Post: #11
Also, if you ever want to port this to say, M$ Windows, RAND_MAX is actually a 16 bit number in Visual C++ (ie rand() is a joke!)

As Puzzler suggested, try some other pseudo-random number generator like Mersenne Twister
Quote this message in a reply
VolganPoet
Unregistered
 
Post: #12
Here's an example of using enum and bitfields in Objective C
Code:
enum SUITE {clubs, spades, hearts, diamonds};

@interface Card: NSObject {
       struct { SUITE suite:2, unsigned value:4,
                unsigned trump:1, unsigned wild:1,
                unsigned points:4, unsigned rank:5 } id;
}
- (id) initSuite:(SUITE) s value:(unsigned) v;
- (SUITE) getSuite;
- (unsigned) getValue;
- (void) logDescription;
@end

Now, some games allow a trump suite rather than a trump card, so you might use a notification to mark cards as trump if the trump suite changes.

Rank will help to order cards. If your game has Aces high or if Jack of Diamonds is higher than Ace of Hearts and so on.

Points can be used for games like 21, spades or rummy.

You might also override isEqualTo: or all of NSComparisonMethods
Code:
@implementation Card
- (id) initSuite:(SUITE) s value:(unsigned) v
{
   [super init];
    id.suite=s; id.value=v;
    id.rank = v == 1 ? 14 : v;
    id.points = v > 10 ? 10 : v;
    return self
};
- (NSString*)description  // override default from NSObject
{
  unichar suite[] = {0x2663,0x2660,0x2661,0x2662}; // Unicode characters
  char * value[] =
    {"Joker","A","2","3","4","5","6","7","8","9","10","J","Q","K","?","?"};
  return [NSString stringWithFormat:@"%s%C",
                   value[id.value],suite[id.suite]];
}
- (void) logDescription
{ NSLog (@"%@\n", [self description]); }

/* other methods*/
@end

Notice that there are 13 cards not 12 and I've assign Ace as 1 with Joker as 0; so all uninitialized cards will be jokers, but not wild nor have any value.

The other methods should be fairly obvious or too lengthy to include here.
Quote this message in a reply
Kaamoss
Unregistered
 
Post: #13
Thanks for the code snipits that makes more sense in some ways I suppose. Also if anyone else is looking at this later on guess I should mention that it will be used in a texas holdem game so there are no wild cards or anything like that. I appreciate the help though, from everyone. It will be a joy helping/learning on these forums I'm sure.
Quote this message in a reply
VolganPoet
Unregistered
 
Post: #14
So, here's a version using NSMutableArray. I've compressed it down a bit, dropping the bits where I couldn't see the merit. Note that required initialization is done in an init method (not a make method). The shuffleDeck algorithm, though clear enough, was rather lengthy so, here I'll just use shuffle.
Code:
@interface Deck: NSObject {
    NSArrary * deck;
}
- (Card *) dealCard;
- (void) shuffle;
- (void) printToLog;
@end
For the implementation we'll use random and we provide dealloc for all our alloc'd pointer (java doesn't require this). And the print method is much more compact.

Note that NSArray is not like a C vector in some regards. Capacity is a hint here and slots are not indexable until the array has enough elements for that index to be valid.
Code:
@implementation Deck
// set up the random number generator
+ (void)initialize { srandomdev(); [super initialize]; }

// create a new deck like it comes out of the box.
- (id) init
{
   if ( (self = [super init]) != nil ) {
       deck = [[NSMutableArray alloc] initWithCapacity:52];
       unsigned suite,value;
       for (suite =0; suite <4; suite ++)
          for(value =1; value <14; value ++)
             [deck addObject: [[Card alloc] initSuite:(SUITE)suite value: value]];
    }  // cards now have a retainCount == 2
    return self;
}
// always clean up memory
- (void) dealloc
{ // normally you don't need to do this to an array, but that's the way I allocated it
   [deck makeObjectsPerformSelector:@selector(release)]; // reduce rc from 2 to 1
   [deck release];  // now array will reduce rc from 1 to 0 and destroy contents
   [super dealloc];
};
- (void) shuffle
{
   unsigned count =  [deck count];
    if (count < 52 )
        [NSException raise:NSInternalInconsistencyException
                            format: @"You may only shuffle with a full deck"];
    int i = (count/2)*3; // 26 is an ok shuffle
    for (; i >= 0;  i--)
        [deck exchangeObjectAtIndex:((unsigned)random())% count
                 withObjectAtIndex:((unsigned)random())% count];
}
- (Card *) dealCard
{
    // retainCount is 2, until remove -> 1. NSAutoreleasePool will decrement it later
    return (Card *) [[deck removeLastObject] autorelease];
    // retain elsewhere if you keep a reference to it
}
- (void) printToLog
{
  NSLog(@"Deck of cards(%d) contains\n:",[cards count]);
  [deck makeObjectsPerformSelector:@selector(logDescription)];
  NSLog(@"</deck>\n:");
@end
NSArray can reduce the amount of code you need to write. The method makeObjectsPerformSelector is more like Eiffel than Java. As for speed, check it against your version, write a test case that creates and shuffles 100 decks. It is possible for shuffle to leave some cards where they were originally, but that should only occur randomly. You could use: exchangeObjectAtIndex ((unsigned)random())%26 withObjectAtIndex ((unsigned)random())%26+26; but I doubt that would be significantly different. Your test cases might also compare against an unshuffled deck to determine the likelyhood that cards are where they started.

And it wouldn't handle jokers, if you added a method to insert jokers, you would have 54 cards.

I couldn't see why you needed to support NSCopying, but this deck should work much like a real deck.

You could also have an initWithCard: (Class*) for creating specialized cards or initYuker to create a Yuker deck (fewer cards) which would require refactoring shuffle. The Yuker deck could be a subclass. Your base deck could also have methods to tell the cards which are wild or trump.

We can change the dealCard method to ensure that cards know they are trump if trump changes after the first card is dealt.
Code:
- (Card *) dealCard
{
   Card * card = (Card *) [[deck removeLastObject] autorelease];
   [[NSNotificationCenter defaultCenter] addObserver: card
      selector:@selector(trumpDidChange:)
      name:@"CardGameChangesTrump" object:self];
   return card;
}

@implementation Card(TrumpGame)
- (void) dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver: self];
    [super dealloc];
}
- (void) trumpDidChange:(NSNotification*)notification
{
     id.trump = [[notification object] isTrumpCard:self] ? 1 : 0;
}
@end
We didn't need a dealloc method before, because we didn't retain anything. Perhaps Deck will check with its Game delegate to determine if a card is trump.

Note: that the code for setting trump (such as by suite) is not shown. The code here only maintains cards no longer in the deck. A changeTrump: method would set up all the cards in the deck, then use the notification scheme to update cards not in the deck (in play or in flops).
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #15
phydeaux Wrote:...
Because % n will give the remainder, which will be between 0 and n inclusive.
...

That is not correct. x % n will never give you n. So, the range would be more correctly 0 .. n-1 or [0, n)

As a sidenote, if you want truly random numbers, use the "mersenne twister" algorithm. Or, use random() instead of rand(), which is slower, but more random.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  What do you want to see in a Random Number Generator? Justin Brimm 16 6,832 Jan 21, 2007 10:38 AM
Last Post: Justin Brimm
  Can't use NSString to put a number on screen? kensuguro 3 4,744 Nov 7, 2005 01:16 AM
Last Post: Jordan
  Getting Number From Character In NSString Nick 2 4,268 Oct 31, 2005 10:08 AM
Last Post: Nick
  Even Number of Triangles for Any 3D model unknown 1 2,695 Sep 21, 2005 08:34 AM
Last Post: Zekaric