If I understand your question, you have a number of problems that are preventing you from reaching your goal of separating the deal of 52 cards into four arrays by suit to collect somewhat of a "hand" of cards by suit where no card in any one suit is a duplicate of (1 - 13
), e.g. "Ace -> King".
If I have that right, then you are wanting to loop over your deck using rand
to pick a "card number" which you then use to categorize the card by suit with your if (rNumber < FT) ...
logic resulting is something similar to:
YELLOW BLUE RED BLACK
13 12 5 11
7 8 1 5
8 13 13 1
10 1 12 7
6 7 3 12
3 4 11 3
11 3 2 4
0 0 9 6
0 0 6 2
(Where 0
simply means "not dealt", e.g. there were only 7 - YELLOW and BLUE cards
generated in this run compared to 9 = RED and BLACK
cards. (the difference between the total and 52
representing duplicate cards generated) To ensure a complete deal, you would need to loop while (x < 52)
incrementing x
on each unique card generated rather than simply looping 52
times -- that is left to you)
As others have suggested in the comments, you can simplify things while making your code less error prone by using the containers provided by C++, e.g. vector
or map
or the like where automatic memory management and built-in bound checking iteration routines can prevent you from straying off into undefined behavior. That said, there is nothing wrong with understanding and learning how to handle plain-old arrays. (you will find them quite frequently in a lot of the C++ written over the past 30 years). Since you have chosen arrays, let's move forward with your problem.
In order to achieve some reasonable distribution of cards, you have a number of issues to fix. The primary two problems you have are int rNumber = rand() % x;
returning a decreasing number which along with your choice of ft = 14
and your comparisons ensure you are zero (or very close to zero) BLACK
cards ever generated.
The random card number you generate each iteration should be a valid card number from 0 - 51
to correspond to the valid indexes of your ar
array, e.g.
#define SUITS 4 /* no. of suits */
#define FT 13 /* cards per-suit (left as FT as your original was ft) */
#define CARDS 52 /* cards per-deck */
...
int rNumber = rand() % CARDS; /* mod with total cards not x */
Next, 'ftshould not be
14. Think about your
ifstatements
YELLOW < 14, then
BLUE < 28and
RED < 42, that's not right, that only leaves
10possible cards for
BLACK`. Instead you need:
if (rNumber < FT) { /* separate into suit, copy to deal array */
...
(that way the valid indexes for 13 - cards
(e.g. 0 - 12
) map exactly to corresponding array elements for each of the cards per-suit)
To handle separating cards into a separate 2D-array where each row represents a suit and where each of the columns can then hold each unique card of each suit, just declare a 2D array of sufficient size, e.g.
int deal[SUITS][FT] = {{0}}, /* 2D array holding cards by suit */
count[SUITS] = {0}, /* count per-suit in deal */
(where count
just holds the number of cards added to each suit in deal
, e.g. when a card is added to deal[YELLOW][n]
, you increment count[YELLOW]++;
)
To make things easier on yourself you can create a global enum
which maps your suit colors to proper values for use throughout the remainder of your code, e.g.
enum { YELLOW, BLUE, RED, BLACK }; /* color constants */
Now you have the constants YELLOW = 0
, BLUE = 1
, RED = 2
and BLACK = 3
which allows easy mapping of your cards to suits during your separation conditionals, e.g.
if (rNumber < FT) { /* separate into suit, copy to deal array */
cout << ("yellow - ") << ar[rNumber] << "\n";
if (isunique (deal[YELLOW], count[YELLOW], ar[rNumber]))
deal[YELLOW][count[YELLOW]++] = ar[rNumber];
}
else if (rNumber < FT * 2) {
cout << ("blue - ") << ar[rNumber] << "\n";
if (isunique (deal[BLUE], count[BLUE], ar[rNumber]))
deal[BLUE][count[BLUE]++] = ar[rNumber];
}
...
That is pretty much it. After the cards are all separated, you can loop over the count
array and find the maximum number of cards in any one suit and then output your deal
array showing how the cards were randomly selected and filtered into the different suits. Putting it together in a short example, you could do something like:
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <ctime>
using namespace std;
#define SUITS 4 /* no. of suits */
#define FT 13 /* cards per-suit */
#define CARDS 52 /* cards per-deck */
enum { YELLOW, BLUE, RED, BLACK }; /* color constants */
/* function to scan 'a' for 'val`
* returning 1 if 'val` doesn't exist, 0 otherwise
*/
int isunique (int *a, int sz, int val)
{
for (int i = 0; i < sz; i++)
if (a[i] == val)
return 0;
return 1;
}
int main (void) {
int ar[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 1,
2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13 },
deal[SUITS][FT] = {{0}}, /* 2D array holding cards by suit */
count[SUITS] = {0}, /* count per-suit in deal */
maxcount = 0, /* max cards per-suit in deal */
x = CARDS;
srand (time(NULL));
while (x--) /* loop 52 times */
{ /* get card number (0 - 51) */
int rNumber = rand() % CARDS; /* mod with total cards not x */
if (rNumber < FT) { /* separate into suit, copy to deal array */
cout << ("yellow - ") << ar[rNumber] << "\n";
if (isunique (deal[YELLOW], count[YELLOW], ar[rNumber]))
deal[YELLOW][count[YELLOW]++] = ar[rNumber];
}
else if (rNumber < FT * 2) {
cout << ("blue - ") << ar[rNumber] << "\n";
if (isunique (deal[BLUE], count[BLUE], ar[rNumber]))
deal[BLUE][count[BLUE]++] = ar[rNumber];
}
else if (rNumber < FT * 3) {
cout << ("red - ") << ar[rNumber] << "\n";
if (isunique (deal[RED], count[RED], ar[rNumber]))
deal[RED][count[RED]++] = ar[rNumber];
}
else {
cout << ("black - ") << ar[rNumber] << "\n";
if (isunique (deal[BLACK], count[BLACK], ar[rNumber]))
deal[BLACK][count[BLACK]++] = ar[rNumber];
}
}
for (int i = 0; i < SUITS; i++) /* find max cards in any one suit */
if (count[i] > maxcount)
maxcount = count[i];
/* output deal */
printf ("\n%8s %8s %8s %8s\n\n", "YELLOW", "BLUE", "RED", "BLACK");
for (int i = 0; i < maxcount; i++)
printf ("%8d %8d %8d %8d\n",
i < count[YELLOW] ? deal[YELLOW][i] : 0,
i < count[BLUE] ? deal[BLUE][i] : 0,
i < count[RED] ? deal[RED][i] : 0,
i < count[BLACK] ? deal[BLACK][i] : 0);
}
Example Use/Output
You can delete the raw cout
statements in each if(..) else if (..)...
when you are satisfied the raw card numbers are being separated by suit in the way you intend. They were left below (with a space and hyphen added between the color and number for readability) because that is the way you had your original code.
$ ./bin/card_array
blue - 12
black - 11
red - 5
blue - 8
red - 1
black - 5
blue - 8
yellow - 13
blue - 8
blue - 8
blue - 13
blue - 1
blue - 12
red - 13
black - 1
black - 7
black - 5
red - 12
blue - 7
red - 3
blue - 8
red - 11
red - 2
yellow - 7
yellow - 8
black - 12
red - 5
yellow - 13
blue - 4
black - 7
red - 12
black - 3
black - 4
blue - 3
yellow - 10
blue - 4
red - 9
yellow - 6
yellow - 6
yellow - 3
blue - 13
red - 5
black - 6
black - 11
black - 6
red - 6
black - 5
red - 11
black - 6
yellow - 11
black - 2
yellow - 13
YELLOW BLUE RED BLACK
13 12 5 11
7 8 1 5
8 13 13 1
10 1 12 7
6 7 3 12
3 4 11 3
11 3 2 4
0 0 9 6
0 0 6 2
While you can make the logic simpler by using the C++ containers, e.g. vector
, map
, etc. there is nothing wrong with learning how to handle simple arrays (there is a lot of C++ code out there written in the last 30 years that uses them exclusively). Look things over and let me know if you have further questions.