Im learning basic C on my own and trying to create a poker client. I have an array with cards (called kortArray) and a players hand (called kortHand). My implementation does not shuffle the deck, but add all 52 cards in sequence and then randomly selecting 5 cards from the deck. I've added a flag (called draget) which tells if a card has been picked up befor or not.
Now, when I run the algorithm below, it usually generates five random numbers which makes the player's or computer's hand. But sometimes it generates less then five numbers, even though I've specifically stated to generate five accepted values.
I have two loops, one that runs five times, and the other is nested and runs until it finds a card which hasn't yet been picked. The printf in the middle tells me that this algorithm doesn't always generate five accepted numbers, and when that happens the player's hand contains cards with nonsense-values.
srand((unsigned)(time(0)));
for(i = 0; i < 5; i++) {
int x = rand()%52 + 1;
while (kortArray[x].draget!=1) {
x = rand()%52 + 1;
if (kortArray[x].draget != 1) {
printf("%i\n", x);
kortArray[x].draget = 1;
kortHand[i] = kortArray[x];
}
}
}
The problem still lies in the +1 for the random numbers.
Also, you are first checking in the the first assignment to x if the card is already picked, and than you assign it to an other card.
Why not use something like:
int nr_cards_picked = 0 /* Number of uniquely picked cards in hand */
/* Continue picking cards until 5 unique cards are picked. */
while (nr_cards_picked < 5) {
x = rand() % 52; /* Take a random card */
if (kortArray[x].draget == 0) {
/* Pick this card. */
kortArray[x].draget = 1; /* Card is picked */
kortHand[i] = kortArray[x]; /* Add picked card to hand */
nr_cards_picked++;
}
}
Forgive compiler errors; I don't have a compiler near here.
This case you only have one time a random number call.
Theoretically it might never end but this is not likely.
You have:
int x = rand()%52+1;
while (kortArray[x].draget!=1){
x = rand()%52;
Arrays in C are indexed starting at 0. Your first call to rand() will generate a value starting at 1. Assuming that you declared kortArray[] to hold 52 values, there is about a 2% chance that you will overrun the array.
Your first call to rand() generates values in the range 1..52. Your second call generates 0..51. ONE OF THEM IS WRONG.
A few things of note.
First of all, random number generators are not guaranteed to be random if you do a mod operation on them. Far better is to divide it out into the 52 segments, and choose like that.
Secondly, you would be far better off moving your call to generate a random number inside the while loop to the end, or just not generate one at the beginning of the for loop.
Where your problem is coming into play is that you are sometimes leaving the while loop without actually entering it, because you are randomly generating a number before you enter the loop.
Given all of this, I would do code somewhat as follows:
srand((unsigned)(time(0)));
for(i=0;i<5;i++){
x=-1;
while (kortArray[x].draget!=1){
x = floor(rand()*52);
}
printf("%i\n", x);
kortArray[x].draget=1;
kortHand[i]=kortArray[x];
}
The nested loop is only entered when kortArray[x].draget is not 1. So everytime it is 1, nothing is done and no card is assigned. First make sure you have a unique x and then in all cases update kortHand[i]
I'd suggest a different algorithm:
create a random number between 0 and number of cards in deck
assign the card from that position to the appropriate hand
swap the last card to that postion
decrease the number of cards in the deck by 1.
continue with 1 until the necessary number of cards are dealt.
This way, you get rid of the flag and you can guarantee linear performance. No need to check whether a card has already been dealt or not.
Related
I'm currently thinking about a code question about the C language, its a game called Blackjack, and here is the original question:
In practice, one need to play the game a large number of times to get an accurate expected
value. Thus, each row of the table should be the results of at least 100,000 experiments. For example, for a particular target points, say 10 points, two cards are drawn first. If the sum of these two cards exceeds 10 points then this experiment is a failure. If the sum is exactly 10 points, then it is a success. If it is less than 10 points, then another card is drawn. If case of neither a failure (more than 10 points) or a success (exactly 10 points), cards are continuously drawn until a conclusive results is obtained. After 100,000 experiments, the probability of getting 10 points should be printed together with the average number of cards of getting 10 points (the third column of the table).
Below is my current code:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int r1,r2,count,sum,cardsadd,k;
int aftersum=sum+k;
unsigned int total,cardsum;
float percent,cards;
printf("Points Probability #Cards\n");
for (int points=4; points<=21; points++){
count = 0;
total = 0;
cardsum = 0;
do{
r1 = rand()%13 + 1;
r2 = rand()%13 + 1;
if(r1>10) r1=10;
if(r2>10) r2=10;
sum = r1+r2;
if(r1==1 && r2==1) sum=12;
else if ((r1==1 || r2==1) && r1!=r2) sum+=10;
count++;
cardsadd=0;
if(sum==points){
total++;
cardsum+=2;
}
else if(sum<points){
while(sum<points){
do{
cardsadd+=1;
k = rand()%13 + 1;
if(k>10) k=10;
else if(k==1){
if(sum<=10) k=11;
}
}while(aftersum>points);
sum+=k;
}
total+=1;
cardsum+=aftersum;
}
}while(count<100000);
percent = (float)total/1000;
cards = (float)cardsum/100000;
printf(" %2d %5.2lf%% ",points,percent);
printf("%.2lf\n",cards);
}
return 0;
}
In my code, variable count is the times needed to execute for each cards (4 to 21), total is the correct times when sum of the cards number is successfully equal to the points we want in the beginning (for loop). And cardsum is the total cards we need in 100000 tests, cardsadd is used when the first two cards drawn is less than the point we want, then we will keep drawing until sum of the point is equal to the points in the beginning.
I don't have the correct answer yet but I know my code is surely wrong, as I can clearly see that the average cards we need to get 4 points is not 2.00.
Hope someone can tell me how I should correct my code to get the answer. If anything is not clearly narrated, I will give a more complete explanation of the parts. Thanks for helping.
With an ace you have 2 possibles scores (the soft and the hard);
You cannot compare "points" with only score in case you have an ace because for example with ace and 5 you can have 6 or 16;
You need to modify your program to take this both scores in consideration (in case of an ace);
Purpose: To store numbers between 1-1000 in a random order.
My Code:
#include<time.h>
int main(){
int arr[1000]={0}, store[1000];
for(int i=0;i<1000;i++){
int no;
while(1){
srand(time(0));
no=rand();
no%=1001;
if(no==0)
continue;
//This ensures Loop will continue till the time a unique random number is generated
if(arr[no-1]!=no){
arr[no-1]=no;
break;
}
}
store[i]=no;
}
For me the code works perfectly fine,however, it took me 58 minutes to execute. Is there a way to speed up the program?
Practical Purpose: I have around 4000 employees and I want to give each one of them a unique random number for an upcoming project.
I tried to execute a code using 1000 to check the efficiency.
Create an array containing 1 to n. Iterate through the list and swap that entry with one that is randomly selected. You will then have a random list containing 1 to n.
From your first sentence the numbers do not have to be random but only need to be in random order.
Therefore you can try a simple approach:
Create an array arr of n elements and initialize with values 1..n
run a loop (counter i) over range 0..n-1
Pick a random number x in range 0..n-i-1
Swap element at index i with index i+x
With this algorithm you don't need to worry about collisions of random numbers.
You swap the numbers and afterwards you decrease the range of candidates.
A number picked once is not available to pick in later steps.
This solution is similar to William's answer. I don't really know if the result has better "randomness" or not.
Try to avoid branches on random numbers. And it will most likely run faster on modern processors.
This because the processor is not able to predict which way to chose on random numbers.
For example
while(1){
srand(time(0));
no=rand();
no%=1001;
if(no==0)
continue;
// ...
}
could be changed to
srand(time(0)); // better outside the loop
while(1) {
no = rand() % 1000 + 1;
// ...
}
I'm a student and in the middle of a midterm project. I am not looking for people to code this for me but I need help with part of it. I am trying to make a Blackjack game and to do that I want to set a list of 52 integers [1,1,1,1,2,2,2,2.....] one for each card in a deck. But how do take a random number from the list and cancel it out? Just like a normal deck once you take a card you can't redraw it. I know a can set a random parameter [n = rand () % 11 + 1] for the program but I want to go above and beyond to get an A
There's no way to remove a random element from an array.
What you can do instead is to swap the randomly selected element with the last element of the array. Along with this, you'll need to store the count of valid elements in the array in a variable array_size_validand decrement that value every time the swapping takes place.
This way you're random index generation function will become:
rand_index = rand () % array_size_valid;
Here is an trivial way: Say you have an array, containing 52 integers:
int deck[52];
And you have initialised it to the value you want, such as 1, 1, 1, 1, 2, 2, 2, 2, .....
When a card is drawn, set the corresponding array element to -1 (or any value other than the original ones):
int drawCard(int deck[])
{
do{
int randIndex = rand () % 52;
} while(deck[randIndex] == -1);
int drawnCard = deck[randIndex];
deck[randIndex] = -1;
return drawnCard;
}
Although this approach may not be efficient enough, it does work, so I think it may be helpful to beginners like you.
You can use a shuffling algorithm, like this: https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle. The Fisher - Yates shuffle starts with an array, then it switches every item of the array with a random item from the array. It is also possible to remove items one by one, but it is more complicated. You can remove items one by one randomly using a binary tree. Store the total number of items under each node. Every time, to remove an item, generate a random number a greater than or equal to 0 and less than the number of items left. Start with the root. Go to the left child if a is less than the number of items under the left child. Otherwise, let a be a-, and go to the right child. Do this recursively. But removing items one by one is more complicated. It is better to just use a shuffling algorithm.
This small part of my program will generate 2 random values in a loop that will loop infinite amount of times until the condition is met. I generate 2 values then add the two values to into totalNum, that totalNum will then be passed to a pointMatch variable. I need pointMatch to take in only the first total then keep that same exact number throughout the program. After that the next generation of numbers will be generated then are added to a total. I need to check if that total is equal to the previous pointMatch, if not generate new numbers again until the condition is met. I just cant seem to keep pointMatch the same, its always equal to total.
The problem is you ware trying to achieve a goal which is very simple through a very complicated approach, if you want pointMatch to remain the same, then don't generate the second random value through rand() instead, just subtract the first one from pointMatch.
int rollDice() {
/* WRONG: just call this once in the whole program */
srand( time( NULL ));
/* pick a random value */
randValue = 1 + rand() % 6;
secondRandValue = pointMatch - randValue;
return randValue + secondRandValue; //return random total
}
Also, it seems as though all the variables are globals and that is generally a bad sign.
This result is as random as the default random number generator can generate random numbers for both randValue and secondRandValue, if you have any good argument against this approach, please share it.
I'm trying to find the best way to save a set of randomly generated numbers so they can be recalled later in another function. Basically I have one function that generates the numbers and makes sure they have no repeats, but I need another function that will search the list of numbers to see if the user has picked one of those numbers. whenever I call the random number function within my search function, I just get a list of different random numbers.
Basically I'm just trying to figure out the best way to either save this array of numbers so it doesn't give me knew numbers the next time I call the function, or the best way to pass it on to the next function.
here is the random number generator function, in case you wanted to see what I'm trying to pass onto the next function.
int i, j;
/*generates the set of random numbers*/
for(i = 0; i < MAX; i++) {
random = rand() % 101;
/*checks to to make sure there is no repeats*/
for (j = 0; j < i; j++) {
if (lucky[j] == random) {
random = rand() % 101;
}
}
lucky[i] = random;
printf("%3d",random);
}
Create a new array first:
int *lucky = malloc(amount_of_numbers_you_want * sizeof(int));
Then fill it with random numbers as usual, and then return it. For example:
int* generate_random_numbers(int amount)
{
int *lucky = malloc(amount * sizeof(int));
/* Fill lucky[] with 'amount' unique random numbers. */
return lucky;
}
Then, whenever you call that function, save the pointer it returns somewhere. Do not forget to free() that pointer when you no longer need it, or else you will leak the memory it occupies.
Since this looks like homework, I'm not giving you full code, but rather a general methodology of how you deal with this kind of problem by using dynamically allocated arrays.
So, first of all, that does not ensure the random number are always different:
if your list has [0.1,0.24,0.555] and you add a new RNG with 0.24, it repeats, but can generate a 0.1 which is also stored in lucky[] (and is thus repeated, as you don't like). It is not very probable, but possible.
The way you want is to have a while(), and only when the new RNG is checked against all the list, it is added.
Finally, generally the best way to save a list of RNGs is to set the seed of the RNG. Given a seed "a", the list of numbers generated by the seed "a" is always the same. In that case, your function can even be checking for non-repetitive RNGs, because the result will always be the same.
#Nikos has given correct answer.
You can allocate memory for an array in caller function as well and pass that to random number generating function. Whatever you do make sure lucky isn't locally defined array. Moreover, your logic to generate numbers seems to be wrong (without repetition). As pointed out by #Nikos this seems to be a school assignment, I will just point obvious mistakes.
a) you don't take care of case where second time generated number (second call to random if first random matches with already existing list) is being checked with older set of generated values correctly.
b) random generating function gives you random number between 0 to RAND_MAX and RAND_MAX % 101 isn't 0. That means the probability of getting random number generated isn't uniform.