objective-c Cocoa: randomize a list

Discussion in 'Mac Programming' started by dj.mooky, Feb 26, 2008.

  1. macrumors newbie

    Joined:
    Feb 26, 2008
    #1
    So, I'm admittedly quite new to cocoa, and only half-way familiar with C to begin with, but I managed last night(till 4a.m.) to get my double elimination brackets app working, for the most part. But I hit a major problem.


    The part that is killing me is a randomizer button, for the players in the tourny.

    I have 16 "nplayerX" NSTextField's, which are used for name input, and to hold temporarily, before randomizing the names into 16 "playerX" NSTextField's that the app will actually use.

    So, I dreamt up a way to make this work, but at 4 a.m. I dont have to question that it isn't the best way.



    So the app does compile with only 2 warnings, the lines:
    [randomName setStringValue:NSSelectorFromString([NSString stringWithFormat:mad:"player", @"%i", (i + 1)])];
    [orderedName setStringValue:NSSelectorFromString([NSString stringWithFormat:mad:"nplayer", @"%d", (d +1)])];

    Need to come up with a way to take a random number (i), and turn it into a binding(?) for "player(i)" and the for (d yadda yadda) so d can sit at the end of "player(d)"

    Any brilliant coders know of a much simpler way to do this, or randomize a list in general?

    Any help or insight would be lovely

    -dj.mooky

    Looking at this, I think I was onto something with making an array of names, and just doing 16 different small segments to make i random, name pnames = nplayer(1-16 in order) with a check statement to see if it's already taken, hmmm.... That does seem easier to my brain, but it is still broken from lack of sleep -- anyway.


    code snippets:
    *************************************
    // TournyController.h


    @interface TournyController : NSObject {


    // Edit Names window fields
    IBOutlet NSTextField *nplayer1;
    IBOutlet NSTextField *nplayer2;
    IBOutlet NSTextField *nplayer3;
    IBOutlet NSTextField *nplayer4;
    IBOutlet NSTextField *nplayer5;
    IBOutlet NSTextField *nplayer6;
    IBOutlet NSTextField *nplayer7;
    IBOutlet NSTextField *nplayer8;
    IBOutlet NSTextField *nplayer9;
    IBOutlet NSTextField *nplayer10;
    IBOutlet NSTextField *nplayer11;
    IBOutlet NSTextField *nplayer12;
    IBOutlet NSTextField *nplayer13;
    IBOutlet NSTextField *nplayer14;
    IBOutlet NSTextField *nplayer15;
    IBOutlet NSTextField *nplayer16;

    // for the randomizer
    IBOutlet NSTextField *randomName; // represents playerX
    IBOutlet NSTextField *orderedName; // represents nplayerX
    // PlayerName *pnames[15]; // failed attempt, left it in for later attempts maybe
    bool placecounter[15]; // mark if that name has been used already

    // Main window fields
    IBOutlet NSTextField *player1;
    IBOutlet NSTextField *player2;
    IBOutlet NSTextField *player3;
    IBOutlet NSTextField *player4;
    IBOutlet NSTextField *player5;
    IBOutlet NSTextField *player6;
    IBOutlet NSTextField *player7;
    IBOutlet NSTextField *player8;
    IBOutlet NSTextField *player9;
    IBOutlet NSTextField *player10;
    IBOutlet NSTextField *player11;
    IBOutlet NSTextField *player12;
    IBOutlet NSTextField *player13;
    IBOutlet NSTextField *player14;
    IBOutlet NSTextField *player15;
    IBOutlet NSTextField *player16;
    }

    - (IBAction)randomizeNames:(id) sender;


    ----------------------------------------
    // tournycontroller.m

    #import "TournyController.h"

    @implementation TournyController

    - (IBAction)randomizeNames:(id) sender {
    // randomizes names while still in entering screen, before putting them in brackets

    // seed the random number generator with the time, to get new randoms
    srand(time(NULL));
    int d=0;
    int i=0;
    for ( i=0; i<15; i++ ) {
    placecounter == false;
    }

    //force playerX to take nplayer values, so that nplayer values can be mixed up
    [player1 setStringValue: [nplayer1 stringValue]];
    [player2 setStringValue: [nplayer2 stringValue]];
    [player3 setStringValue: [nplayer3 stringValue]];
    [player4 setStringValue: [nplayer4 stringValue]];
    [player5 setStringValue: [nplayer5 stringValue]];
    [player6 setStringValue: [nplayer6 stringValue]];
    [player7 setStringValue: [nplayer7 stringValue]];
    [player8 setStringValue: [nplayer8 stringValue]];
    [player9 setStringValue: [nplayer9 stringValue]];
    [player10 setStringValue: [nplayer10 stringValue]];
    [player11 setStringValue: [nplayer11 stringValue]];
    [player12 setStringValue: [nplayer12 stringValue]];
    [player13 setStringValue: [nplayer13 stringValue]];
    [player14 setStringValue: [nplayer14 stringValue]];
    [player15 setStringValue: [nplayer15 stringValue]];
    [player16 setStringValue: [nplayer16 stringValue]];

    // below is failed part of failed attempt.. I need to learn a little more about using arrays and classes it seems
    // [pnames[0] setStringValue:[nplayer1 stringValue]];
    // [pnames[1] setStringValue:[nplayer2 stringValue]];
    // [pnames[2] setStringValue:[nplayer3 stringValue]];
    // [pnames[3] setStringValue:[nplayer4 stringValue]];
    // [pnames[4] setStringValue:[nplayer5 stringValue]];
    // [pnames[5] setStringValue:[nplayer6 stringValue]];
    // [pnames[6] setStringValue:[nplayer7 stringValue]];
    // [pnames[7] setStringValue:[nplayer8 stringValue]];
    // [pnames[8] setStringValue:[nplayer9 stringValue]];
    // [pnames[9] setStringValue:[nplayer10 stringValue]];
    // [pnames[10] setStringValue:[nplayer11 stringValue]];
    // [pnames[11] setStringValue:[nplayer12 stringValue]];
    // [pnames[12] setStringValue:[nplayer13 stringValue]];
    // [pnames[13] setStringValue:[nplayer14 stringValue]];
    // [pnames[14] setStringValue:[nplayer15 stringValue]];
    // [pnames[15] setStringValue:[nplayer16 stringValue]];


    // fix this ****! turn the string into something NSTextField can understand
    i == (rand() % 15);
    for (d=0; d<15; d++) {
    while ( placecounter == false ) {
    i == (rand() % 15);
    }
    [randomName setStringValue:NSSelectorFromString([NSString stringWithFormat:mad:"player", @"%i", (i + 1)])];
    [orderedName setStringValue:NSSelectorFromString([NSString stringWithFormat:mad:"nplayer", @"%d", (d +1)])];

    [orderedName setStringValue:[randomName stringValue]];
    placecounter == true;
    }

    }
     
  2. macrumors G4

    Eraserhead

    Joined:
    Nov 3, 2005
    Location:
    UK
    #2
    random() is better than rand() for finding random numbers.

    For a random list you will need to reduce the limit of the random numbers each time and add an appropriate number to cover the values already filled.
     
  3. macrumors newbie

    Joined:
    Dec 27, 2007
    #3
    Check out the Fisher-Yates shuffle algorithm.

    It does the shuffling in place by swapping elements in the array in a structured way. It's simple and more principled than an ad-hoc method.
     
  4. thread starter macrumors newbie

    Joined:
    Feb 26, 2008
    #4
    i tried to adapt the code you referenced in the shuffle algorithm. Since it came from a java code I cannot, to my knowledge(which is quite limited) use it in an objective-c cocoa framework(I really have no idea if you can mix the two).

    Anyway, I have all the parts working except coming up with a randomized string to shift the strings from one array to another in random fashion. I'm about to write a small test app, to see if I can "control" the random() output at all, i'll report back on my findings, if I find a solution.

    -dj.mooky
     
  5. thread starter macrumors newbie

    Joined:
    Feb 26, 2008
    #5
    Holy expletive batman!

    OK! Well thank you eharley for pointing out that algorithm to me, because it is what eventually(and the same thing done in C++ code..) gave me the idea for a solution to my insanity!

    here it is, just the randomize function -- I hope someone else can find this method useful, and that I caused and pulled several pre-mature gray hairs out for more than just one reason :)

    - (IBAction)randomizeNames:(id) sender {
    // randomizes names while still in entering screen, before putting them in brackets

    int i=0;
    NSMutableArray *pnames=[[NSMutableArray alloc] init];

    // stores all nplayer names to an array pnames
    [pnames addObject:[nplayer1 stringValue]];
    [pnames addObject:[nplayer2 stringValue]];
    [pnames addObject:[nplayer3 stringValue]];
    [pnames addObject:[nplayer4 stringValue]];
    [pnames addObject:[nplayer5 stringValue]];
    [pnames addObject:[nplayer6 stringValue]];
    [pnames addObject:[nplayer7 stringValue]];
    [pnames addObject:[nplayer8 stringValue]];
    [pnames addObject:[nplayer9 stringValue]];
    [pnames addObject:[nplayer10 stringValue]];
    [pnames addObject:[nplayer11 stringValue]];
    [pnames addObject:[nplayer12 stringValue]];
    [pnames addObject:[nplayer13 stringValue]];
    [pnames addObject:[nplayer14 stringValue]];
    [pnames addObject:[nplayer15 stringValue]];
    [pnames addObject:[nplayer16 stringValue]];



    // shuffle the ba-jebus out of the list by taking
    // this is inside another for(40 times) loop to make sure it shuffles well... initial tests did not move slot 1 around alot for some reason
    int r;
    for (r=0; r<40; r++){
    for (i=0; i<[pnames count]; i++) {
    int d= i + (random() %([pnames count] -i));
    NSString *temp=[pnames objectAtIndex:d];
    //[temp setStringValue:[[pnames objectAtIndex:i] stringValue]];

    [pnames removeObjectAtIndex:d];
    [pnames addObject:temp];

    // [pnames objectAtIndex:d] ==[pnames objectAtIndex:i]; // how do you do this correctly?
    // [pnames objectAtIndex:i] ==temp;

    // throws it out to console so I can make sure it's working right, will remove for release purposes
    NSLog( @"made it past while() loop and - random number r: %d, pname[%d]: %@, pname[%d] %@.", d, i, [pnames objectAtIndex:i], d, [pnames objectAtIndex:d]);
    }
    }


    [nplayer1 setStringValue:[pnames objectAtIndex:0]];
    [nplayer2 setStringValue:[pnames objectAtIndex:1]];
    [nplayer3 setStringValue:[pnames objectAtIndex:2]];
    [nplayer4 setStringValue:[pnames objectAtIndex:3]];
    [nplayer5 setStringValue:[pnames objectAtIndex:4]];
    [nplayer6 setStringValue:[pnames objectAtIndex:5]];
    [nplayer7 setStringValue:[pnames objectAtIndex:6]];
    [nplayer8 setStringValue:[pnames objectAtIndex:7]];
    [nplayer9 setStringValue:[pnames objectAtIndex:8]];
    [nplayer10 setStringValue:[pnames objectAtIndex:9]];
    [nplayer11 setStringValue:[pnames objectAtIndex:10]];
    [nplayer12 setStringValue:[pnames objectAtIndex:11]];
    [nplayer13 setStringValue:[pnames objectAtIndex:12]];
    [nplayer14 setStringValue:[pnames objectAtIndex:13]];
    [nplayer15 setStringValue:[pnames objectAtIndex:14]];
    [nplayer16 setStringValue:[pnames objectAtIndex:15]];

    }
     
  6. macrumors 68040

    iSee

    Joined:
    Oct 25, 2004
    #6
    Er, sorry for being off topic but I can't believe Fisher and Yates get to put their names on this. I also "invented" this algorithm--in the 7th grade. It's not that I was particularly smart--it's just a fairly obvious solution (hah, this was on an 8K PET in BASIC, so it was worth it to think up an efficient method).

    Regarding the OP's most recent code:
    Your calculation of d is off. You should be picking a random number from 0 up-to-but-not-including [pnames count]-i. So you just need to remove the "i+" part:
    Code:
    int d= random() % ([pnames count] -i);
    Also, you don't need to shuffle 40 times. If random() was a perfect random number generator then once will be fine. Of course it's not. Running though the shuffle a few times might help hide the bias in your method of generating random numbers, but 40 is pointless. (You might have been doing it before because of the bug with d-once you fix that, I think you'll find you don't need to shuffle repeatedly.
     
  7. macrumors newbie

    Joined:
    Dec 27, 2007
    #7
    It's one thing to suggest the algorithm, it's another to prove that it actually results in all configurations being equally likely to come out the other end.
     

Share This Page