Results 1 to 10 of 20
I'm working on an engine for Risk in C. I was wondering if someone could help me with the dice. If you're unfamiliar with Risk, there are two sets of ...
- 03-20-2008 #1Just Joined!
- Join Date
- Feb 2008
- Posts
- 72
simulating two sets of dice in C
I'm working on an engine for Risk in C. I was wondering if someone could help me with the dice. If you're unfamiliar with Risk, there are two sets of dice. One of three, and the other of two. Two players each roll a set and the numbers determine who wins. The problem is, my dice aren't random enough. The set of three ALWAYS beats the other. Once in a while its a tie ("one each") but I've yet to see results that showed the set of two winning. The attacker's dice (the set of three) are almost always all higher than the defender's. Right now I'm just using the common pseudo-random number generator with time(). Can someone help me with a more random generator that will even out the results? You can download dice.c here and dice.h here.
- 03-21-2008 #2Just Joined!
- Join Date
- Mar 2008
- Posts
- 20
First of all, if you want an array of three ints, you need to declare it with [3] and not [2] (the number of ints in the array, not the index of the last int). As for your random numbers, here's the reason it's not working:
You already know that if you set the seed to some constant (like 123 or something) then every time you run the program you'll get the same set of random numbers. This isn't just true for the whole program, but for each function. That is, every time you call the get_dice () function, if rand () has the same seed, it will return the same number. So right now, the attacker's rolls will be different from the defender's rolls because you reseeded rand () in between, but within those two sets, the rolls will not be random.
What I usually do when I have a program that needs to call rand () more than once in the same second is this:
Define a global int called randseed, and set it to 0.
Seed rand () with time (NULL) + randseed.
At the end of every single function that calls rand (), (but before the return statement, obviously) add randseed++.
Now you'll never call the same function with the same seed, because every time an affected function is called, the seed changes.
- 03-21-2008 #3
As a general rule, take a tip from the Perl book and use as a seed not just
butCode:time(NULL)
Code:time(NULL)+getpid()
--
Bill
Old age and treachery will overcome youth and skill.
- 03-21-2008 #4Just Joined!
- Join Date
- Feb 2008
- Posts
- 72
I did what both of you guys said, and I'm still getting the same problem. The numbers are still different each time the program is run, but the attacker's are always higher. The links in the OP have been updated with the new source code.
- 03-22-2008 #5
First, your comment preceding function getdice() says "Returns a pseudorandom number 0 through 6". The comment is accurate; that's what the code actually does. But why would you want to do that? You have a seven-sided die or something? You later seem to eliminate the resultant 0, but why even bother to get it? What's wrong with using a single-sided die in the first place, 1 through 6?
Second, your program does not tend to deal out higher die values to the attacker than to the defender. I modified your code in dice.h as shown in red below:
Then I recompiled the program and ran it several times. There seems to be no long-range bias toward giving the attacker higher values of the die roll.Code:#ifndef DICE_H #define DICE_H #include <stdlib.h> #include <stdio.h> #include <time.h> // Attacker's dice static int a_dice[3]; // Defender's dice static int d_dice[2]; // Used to get a different seed every time static int randseed = 0; // Set the seed for the dice void randomize() { srand(time(NULL) + randseed + getpid()); } // Set the seed for the defender's dice /* void d_randomize() { srand(time(NULL) + randseed + getpid()); } */ // Returns a pseudorandom number 0 through 6 int get_dice(char *who) { randomize(); int n = rand() % 7; randseed++; printf("%s %d\n",who,n); return n; } // Rolls the attacker's dice. void attacker_rolldice() { a_dice[0] = get_dice("a"); a_dice[1] = get_dice("a"); a_dice[2] = get_dice("a"); /* Since dice do not have a side 0, keep rolling until you get results with no zeroes */ while (a_dice[0] == 0) { a_dice[0] = get_dice("a"); } while (a_dice[1] == 0) { a_dice[1] = get_dice("a"); } while (a_dice[2] == 0) { a_dice[2] = get_dice("a"); } } // Roll the defender's dice. void defender_rolldice() { d_dice[0] = get_dice("d"); d_dice[1] = get_dice("d"); // Check for zeroes while (d_dice[0] == 0) { d_dice[0] = get_dice("d"); } while (d_dice[1] == 0) { d_dice[1] = get_dice("d"); } } /* Sort the dice from highest to lowest so we can tell who won. Bubble sort the attacker's dice and do a simple check and swap on the defender's. */ void sort_dice() { int a_diceL = 3; // The length of a_dice[] int i; int j; int tmp; // Sort the attacker's dice for (i = 0; i <= 3; i++) { for (j = 0; j <= 3; j++) { if (a_dice[j+1] > a_dice[j]) { tmp = a_dice[j]; a_dice[j] = a_dice[j+1]; a_dice[j+1] = tmp; } } } // Sort the defender's dice if (d_dice[1] > d_dice[0]) { tmp = d_dice[0]; d_dice[0] = d_dice[1]; d_dice[1] = tmp; } } /* Determines the results of the dice. Returns 2 if the attacker loses 2 units, 3 if the defender loses 2 units, and 4 if each player loses one unit. Returns 1 on error. */ int get_results() { // Used to count how many units are lost for the attacker and defender. int a_strikes = 0; int d_strikes = 0; // Sort the dice. sort_dice(); /* Compare the attacker's highest two dice to the defender's dice to get the results. */ if (a_dice[0] == d_dice[0]) { a_strikes++; // The defender always wins ties! } if (a_dice[1] == d_dice[1]) { a_strikes++; } if (a_dice[0] > d_dice[0]) { d_strikes++; } if (a_dice[1] > d_dice[1]) { d_strikes++; } if (a_dice[0] < d_dice[0]) { a_strikes++; } if (a_dice[1] < d_dice[1]) { a_strikes++; } // Count the strikes and return the results if (a_strikes == 2) { return 2; } if (d_strikes == 2) { return 3; } if (a_strikes == 1 && d_strikes == 1) { return 4; } // If we get this far, something wen't wrong return 1; } #endif
Third, it is customary to set the random seed only once per run of a program. You don't buy anything (and you might lose something if you do it wrong) by setting the seed more than once per run. Again, making the seed a function of the time and the process ID seems to work well for most applications.
Fourth, it is customary to refrain from putting real code in a .h file. ("h" stands for "header".) Usually header files contain stuff that is not real code: macro definitions (of code or otherwise), other constant definitions, and most often function prototypes. As such, the header files are included by both the .c files which define the code in the functions which are declared in the header files, and any .c files which use those functions.
Hope this helps.--
Bill
Old age and treachery will overcome youth and skill.
- 03-22-2008 #6Just Joined!
- Join Date
- Feb 2008
- Posts
- 72
I dont know of a way to specify 1 through 6 at first, so I just got 0 through 6 and kept repeating the rolls until there were results with no zeroes, just like the comments say. There's no such thing as a single-sided die. The dice I'm using are six-sided. Six sides, one number per side = six numbers.What's wrong with using a single-sided die in the first place, 1 through 6?
I changed it to match what you posted, and the attacker is still getting higher numbers every time..Then I recompiled the program and ran it several times. There seems to be no long-range bias toward giving the attacker higher values of the die roll.
I'll keep that in mind and modify it accordingly. Thanks.Fourth, it is customary to refrain from putting real code in a .h file. ("h" stands for "header".) Usually header files contain stuff that is not real code: macro definitions (of code or otherwise), other constant definitions, and most often function prototypes. As such, the header files are included by both the .c files which define the code in the functions which are declared in the header files, and any .c files which use those functions.
Well I have to at least set it twice, one for each set of dice. Otherwise, the defender's dice will be the same as the attacker's first two. I changed it to set the seed multiple times because that's what rufio suggested.Third, it is customary to set the random seed only once per run of a program. You don't buy anything (and you might lose something if you do it wrong) by setting the seed more than once per run.
- 03-23-2008 #7Just Joined!
- Join Date
- Mar 2008
- Posts
- 20
Unfortunately, it doesn't work if you have a function that calls rand () which is called more than once a second, as I suspect his would be. I downloaded those files (your edited header, anyway), compiled and ran them without the randseed lines, and got:
thenCode:a 3 a 3 a 3 d 3 d 3 Attacker's dice: Die 1: 3 Die 2: 3 Die 3: 3 Defender's dice: Die 1: 3 Die 2: 3 Attacker loses 2 units!
Not random at all. (I had to take out the getpid () though, because for some reason gcc didn't recognize it. I don't think getpid () changes during runtime though.)Code:a 1 a 1 a 1 d 1 d 1 Attacker's dice: Die 1: 1 Die 2: 1 Die 3: 1 Defender's dice: Die 1: 1 Die 2: 1 Attacker loses 2 units!
- 03-24-2008 #8
I can think of a reason or two why the randomness disappeared entirely in your code.
rufio, would you please post your entire code? Is this possible?--
Bill
Old age and treachery will overcome youth and skill.
- 03-26-2008 #9Just Joined!
- Join Date
- Mar 2008
- Posts
- 20
I just downloaded his dice.c file, copy-pasted your version of the dice.h file, and removed all mentions of randseed.
- 03-26-2008 #10
Well, then, I can't explain it.
I did as I assume you did: I took my edited version of dice.h (the one you started with, most likely) and removed completely every line which contained the string "randseed". That gave me this for dice.h:
and, to review, dice.c looked like this:Code:#ifndef DICE_H #define DICE_H #include <stdlib.h> #include <stdio.h> #include <time.h> // Attacker's dice static int a_dice[3]; // Defender's dice static int d_dice[2]; // Used to get a different seed every time // static int randseed = 0; // Set the seed for the dice void randomize() { // srand(time(NULL) + randseed + getpid()); } // Set the seed for the defender's dice /* void d_randomize() { // srand(time(NULL) + randseed + getpid()); } */ // Returns a pseudorandom number 0 through 6 int get_dice(char *who) { randomize(); int n = rand() % 7; // randseed++; printf("%s %d\n",who,n); return n; } // Rolls the attacker's dice. void attacker_rolldice() { a_dice[0] = get_dice("a"); a_dice[1] = get_dice("a"); a_dice[2] = get_dice("a"); /* Since dice do not have a side 0, keep rolling until you get results with no zeroes */ while (a_dice[0] == 0) { a_dice[0] = get_dice("a"); } while (a_dice[1] == 0) { a_dice[1] = get_dice("a"); } while (a_dice[2] == 0) { a_dice[2] = get_dice("a"); } } // Roll the defender's dice. void defender_rolldice() { d_dice[0] = get_dice("d"); d_dice[1] = get_dice("d"); // Check for zeroes while (d_dice[0] == 0) { d_dice[0] = get_dice("d"); } while (d_dice[1] == 0) { d_dice[1] = get_dice("d"); } } /* Sort the dice from highest to lowest so we can tell who won. Bubble sort the attacker's dice and do a simple check and swap on the defender's. */ void sort_dice() { int a_diceL = 3; // The length of a_dice[] int i; int j; int tmp; // Sort the attacker's dice for (i = 0; i <= 3; i++) { for (j = 0; j <= 3; j++) { if (a_dice[j+1] > a_dice[j]) { tmp = a_dice[j]; a_dice[j] = a_dice[j+1]; a_dice[j+1] = tmp; } } } // Sort the defender's dice if (d_dice[1] > d_dice[0]) { tmp = d_dice[0]; d_dice[0] = d_dice[1]; d_dice[1] = tmp; } } /* Determines the results of the dice. Returns 2 if the attacker loses 2 units, 3 if the defender loses 2 units, and 4 if each player loses one unit. Returns 1 on error. */ int get_results() { // Used to count how many units are lost for the attacker and defender. int a_strikes = 0; int d_strikes = 0; // Sort the dice. sort_dice(); /* Compare the attacker's highest two dice to the defender's dice to get the results. */ if (a_dice[0] == d_dice[0]) { a_strikes++; // The defender always wins ties! } if (a_dice[1] == d_dice[1]) { a_strikes++; } if (a_dice[0] > d_dice[0]) { d_strikes++; } if (a_dice[1] > d_dice[1]) { d_strikes++; } if (a_dice[0] < d_dice[0]) { a_strikes++; } if (a_dice[1] < d_dice[1]) { a_strikes++; } // Count the strikes and return the results if (a_strikes == 2) { return 2; } if (d_strikes == 2) { return 3; } if (a_strikes == 1 && d_strikes == 1) { return 4; } // If we get this far, something wen't wrong return 1; }
Then I compiled it thus:Code:#include <stdlib.h> #include "dice.h" int main() { attacker_rolldice(); defender_rolldice(); sort_dice(); printf("Attacker's dice:\n"); printf("Die 1: %i\n", a_dice[0]); printf("Die 2: %i\n", a_dice[1]); printf("Die 3: %i\n", a_dice[2]); printf("Defender's dice:\n"); printf("Die 1: %i\n", d_dice[0]); printf("Die 2: %i\n", d_dice[1]); int results = get_results(); if (results == 2) { printf("Attacker loses 2 units!\n"); return 0; } if (results == 3) { printf("Defender loses 2 units!\n"); return 0; } if (results == 4) { printf("One each!\n"); return 0; } if (results == 1) { printf("There was an error getting the results :(\n"); return 0; } // We shouldn't get this far.. return 1; }
I ran it twice and got first:Code:gcc dice.c -o dice
and then:Code:a 1 a 4 a 2 d 5 d 1 Attacker's dice: Die 1: 5 Die 2: 4 Die 3: 2 Defender's dice: Die 1: 1 Die 2: 1 Defender loses 2 units!
Please note three things.Code:a 1 a 4 a 2 d 5 d 1 Attacker's dice: Die 1: 5 Die 2: 4 Die 3: 2 Defender's dice: Die 1: 1 Die 2: 1 Defender loses 2 units!
The first is that in my runs the results are both the same. But you don't want them to be. The standard way to avoid that is to call srand() exactly once, and a good way to do that is to use as the parameter to srand() the time plus the value of getpid(). Exactly once, at the beginning of the application, before you do the first rand() call.
The second is that in your runs the results are different from one run to the next. I can't explain that, assuming that you compiled the code exactly as I have it above. It seems impossible to me.
The third is that within each of your runs, each die toss ends with the same result. Again, I can't explain this; it shouldn't be happening that way, again assuming that you compiled the code exactly as I have it above.--
Bill
Old age and treachery will overcome youth and skill.


Reply With Quote
