Shuffling Not Working Correctly

Hello everyone, I have having troubles with a Coaca-based program.


I am working on a custom shuffling program for the game Dominion, and while there are many out there already, the thing that I cannot seem to do with any of the ones I tried was limit the number of sets chosen (e.g. tell program I want to use seven sets, only choose 4 of the seven) and things worked out pretty welll up through Guilds, but Adventures is giving me some grieve.


According to the rules, it is stated that Events are not counted as part of the Kingdoms in supply and that if an Event shows up, the card drawing should continue until ten kingdoms are chosen, and it is recommended to have no more than two event cards.


However, right now, there are always 12 or 13, depending on if the Young Witch card is present or not, since the Bane card is usually the eleventh card chosen, when it should rotate between 10 cards, 11 cards, 12 cards, or 13, if Young Witch is present.


How do I fix this problem.


Right now, the code to determine number of rows, since I am using NSTableViews, looks like this.


Yes, I know that with the code currently there will always put in two Events, but I have tried quite a few ways, like nested loops changing between while loop inside a for loop and vice versa to increment a counter for the rows and add number of events to number in supply, just check number of events, and even controlling how many events are placed into the array that contains the cards, after trying to remove events from array if there are more than two All of which have failed.


Also, I have checked via NSLog about whether the cards were indeed event cards or not and things were presented how they should, but if you need to see how the cards are generated and such, here are the following files:


Card (this is used to create card objects)

Cards (this is used to generate an array of Card objects through JSON input)

Answered by brycec in 31879022

Ok, going through, I think I figured things out, since I have not yet pushed to github, I will put what I did here.


in the Cards class, I added an array just to hold the event cards, and added something similiar to this in each variation of the method that creates the supply of cards:


if ([card event])
        {
            [events addObject:card];
        }
        else
        {
            [cards addObject:card]; /
        }


that slash on line 8 is part of a comment.


in the Rules class, which I did not link to before, I added in the following method:


- (int)events
{
    BOOL events = false;

    int randValue1 = arc4random_uniform(2);

    if (randValue1 == 1)
    {
        events = true;
    }

    if (events == true) {
        int randValue2 = arc4random_uniform(2)+1;
        return randValue2;
    }

    return 0;
}


This makes sure that anywhere from 0 to two events can be present.


In the Game class, I split up the supply method into two variations, which the root view controller has a conditional statement to call, which look like the following:


- (int) supply:(NSMutableArray*)c bane:(Card *)b
{
    /
    if (b != nil) {
    
        game = [[NSMutableArray alloc] initWithCapacity:11];
    
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
        
        [game addObject:b];
    }
    else
    {
        game = [[NSMutableArray alloc] initWithCapacity:10];
        
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
    }
    return (int)game.count;
}


The above deals with only if a Bane card is present.


- (int)supply:(NSMutableArray *)c bane:(Card *)b events:(NSMutableArray *)events
{
    rules = [[Rules alloc] init];

    int e = [rules events];
    NSMutableArray* chosen = [[NSMutableArray alloc] init];

    if (e == 2 && b != nil)
    {
        game = [[NSMutableArray alloc] initWithCapacity:13];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
     
        for (int i = 0; i < 2; i++)
        {
            int randValue = arc4random_uniform((u_int32_t)[events count]);
         
            if ([chosen containsObject:events[randValue]])
            {
            }
            else
            {
                [chosen addObject:events[randValue]];
            }
        }
     
        for (int i = 0; i < [chosen count]; i++)
        {
            [game addObject:chosen[i]];
        }
     
        [game addObject:b];
    }

    else if (e == 1 && b != nil)
    {
        game = [[NSMutableArray alloc] initWithCapacity:12];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
        int randValue = arc4random_uniform((u_int32_t)[events count]);
     
        [chosen addObject:events[randValue]];
     
        [game addObject:chosen[0]];
     
        [game addObject:b];
    }
    else if (e == 2 && b == nil)
    {
        game = [[NSMutableArray alloc] initWithCapacity:12];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
     
        for (int i = 0; i < 2; i++)
        {
            int randValue = arc4random_uniform((u_int32_t)[events count]);
         
            if ([chosen containsObject:events[randValue]])
            {
            }
            else
            {
                [chosen addObject:events[randValue]];
            }
        }
     
        for (int i = 0; i < [chosen count]; i++)
        {
            [game addObject:chosen[i]];
        }
    }
    else if (e == 1 && b == nil)
    {
        game = [[NSMutableArray alloc] initWithCapacity:11];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
     
        int randValue = arc4random_uniform((u_int32_t)[events count]);
     
        [chosen addObject:events[randValue]];
     
        [game addObject:chosen[0]];
    }
    else
    {
        game = [[NSMutableArray alloc] initWithCapacity:10];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
    }
    return (int)game.count;
}


The above method is used if event cards can be present, in the event that event cards may be present.


In the root view controller, I made the array that holds user selection available to all methods in the root view controller's class and the method to determine method rows now looks like this:


- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    bane = [rules bane:cards]; /
   
    if ([chosen containsObject:@"Adventures"])
    {
        Cards* other = [[Cards alloc] initWithSupply:[json supply]]; /
        NSMutableArray* events = [other events];
       
        return [supply supply:cards bane:bane events:events];
    }
    return [supply supply:cards bane:bane];
}


I had to create a cards object so that I could retrieve events, because the cards variable makes a call to the shuffle class, which does not return event cards.


So far, I have not seen all 13 cards show up, but it at least does not always display 12 or 13 cards.

Accidentally submitted too early, so this continues from last post.


JSON (reads JSON file and turns in into something the program can use)

MasterViewController (root view controller)

SetListController (used to list sets available)

SetupViewController (used to show cards other than normal supply needed for game)

Shuffle (responsible for shuffling cards)

Sets (responsible for retrieving sets available)


and this is the JSON file used.

I have modified my code a bit, so the issue of two events always is still there, since before there could be more than two, which means that it still always results in 12 or 13 cards.


Right now in the Game class, I have a nest loop combination of two four loops, which I do not really understand why that works, but using a while loop as the outter loop, which is what I should be doing, generates more rows than necessary (beyond 13 cards).


For now, it is not as big a deal, but I would still like help.

You're probably not going to get any useful replies while your question is so large in scope. You can't assume that others know the game rules you're trying to implement, or that they're prepared to go research them, or to study any more than fragments of your code.


AFAICT, the problem you're having is along these lines: "My data model requires me to have an array of things, but I only want to show some of these things in a table view. How do I tell the table view which things to display?"


Because, to drive a table view, you'll often need to know the "n'th" thing to display, or the number of things to display, it may be simplest to keep a second, filtered array consisting only of the objects that are to be displayed, and in the correct display order. This means you have to be thorough in maintaining this second array, but it's otherwise straightforward, usually.

I'll go ahead and see about creating another array, as well as method to count the number of events, so that I can initialize the array with an appropiate size.


I noticed something I kind of need to fix in the mean time too, so I get those things done.


You do make a good point, but I already specified the rule set I am working on now, since the other rules of the game have been followed, at least where I could find details (e.g. when potions are used and what can be a bane card), others are pretty well open to the one setting up the game, such as using shelters in Dark Ages and the Colonies in Prosperity, the former of which specifies only that the card used to determine if shelters are present cannot be the same one that determines if there are Colonies.


In the game of Dominion, there are normally ten cards out in the supply out of 25 (or over 200, if you have all expansions).


An eleventh card can be present if Young Witch is out, which will negate its effects, and must cost 2 or 3 coins and not be either a potion or an event card.

Accepted Answer

Ok, going through, I think I figured things out, since I have not yet pushed to github, I will put what I did here.


in the Cards class, I added an array just to hold the event cards, and added something similiar to this in each variation of the method that creates the supply of cards:


if ([card event])
        {
            [events addObject:card];
        }
        else
        {
            [cards addObject:card]; /
        }


that slash on line 8 is part of a comment.


in the Rules class, which I did not link to before, I added in the following method:


- (int)events
{
    BOOL events = false;

    int randValue1 = arc4random_uniform(2);

    if (randValue1 == 1)
    {
        events = true;
    }

    if (events == true) {
        int randValue2 = arc4random_uniform(2)+1;
        return randValue2;
    }

    return 0;
}


This makes sure that anywhere from 0 to two events can be present.


In the Game class, I split up the supply method into two variations, which the root view controller has a conditional statement to call, which look like the following:


- (int) supply:(NSMutableArray*)c bane:(Card *)b
{
    /
    if (b != nil) {
    
        game = [[NSMutableArray alloc] initWithCapacity:11];
    
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
        
        [game addObject:b];
    }
    else
    {
        game = [[NSMutableArray alloc] initWithCapacity:10];
        
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
    }
    return (int)game.count;
}


The above deals with only if a Bane card is present.


- (int)supply:(NSMutableArray *)c bane:(Card *)b events:(NSMutableArray *)events
{
    rules = [[Rules alloc] init];

    int e = [rules events];
    NSMutableArray* chosen = [[NSMutableArray alloc] init];

    if (e == 2 && b != nil)
    {
        game = [[NSMutableArray alloc] initWithCapacity:13];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
     
        for (int i = 0; i < 2; i++)
        {
            int randValue = arc4random_uniform((u_int32_t)[events count]);
         
            if ([chosen containsObject:events[randValue]])
            {
            }
            else
            {
                [chosen addObject:events[randValue]];
            }
        }
     
        for (int i = 0; i < [chosen count]; i++)
        {
            [game addObject:chosen[i]];
        }
     
        [game addObject:b];
    }

    else if (e == 1 && b != nil)
    {
        game = [[NSMutableArray alloc] initWithCapacity:12];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
        int randValue = arc4random_uniform((u_int32_t)[events count]);
     
        [chosen addObject:events[randValue]];
     
        [game addObject:chosen[0]];
     
        [game addObject:b];
    }
    else if (e == 2 && b == nil)
    {
        game = [[NSMutableArray alloc] initWithCapacity:12];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
     
        for (int i = 0; i < 2; i++)
        {
            int randValue = arc4random_uniform((u_int32_t)[events count]);
         
            if ([chosen containsObject:events[randValue]])
            {
            }
            else
            {
                [chosen addObject:events[randValue]];
            }
        }
     
        for (int i = 0; i < [chosen count]; i++)
        {
            [game addObject:chosen[i]];
        }
    }
    else if (e == 1 && b == nil)
    {
        game = [[NSMutableArray alloc] initWithCapacity:11];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
     
        int randValue = arc4random_uniform((u_int32_t)[events count]);
     
        [chosen addObject:events[randValue]];
     
        [game addObject:chosen[0]];
    }
    else
    {
        game = [[NSMutableArray alloc] initWithCapacity:10];
     
        for (int i = 0; i < 10; i++)
        {
            [game addObject:c[i]];
        }
    }
    return (int)game.count;
}


The above method is used if event cards can be present, in the event that event cards may be present.


In the root view controller, I made the array that holds user selection available to all methods in the root view controller's class and the method to determine method rows now looks like this:


- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
    bane = [rules bane:cards]; /
   
    if ([chosen containsObject:@"Adventures"])
    {
        Cards* other = [[Cards alloc] initWithSupply:[json supply]]; /
        NSMutableArray* events = [other events];
       
        return [supply supply:cards bane:bane events:events];
    }
    return [supply supply:cards bane:bane];
}


I had to create a cards object so that I could retrieve events, because the cards variable makes a call to the shuffle class, which does not return event cards.


So far, I have not seen all 13 cards show up, but it at least does not always display 12 or 13 cards.

Shuffling Not Working Correctly
 
 
Q