0

I have a 1-dimensional array of length 52 (a deck), with 4 sets of numbers between 1-13.

At the moment, I have it so that a random number generator selects an element, if that number is between a certain range it is one colour, between another range a different colour, etc..

I want to be able to remove the element that is selected by the RNG, move it to another array, i.e player, and turn it into a 2-dimensional array with its corresponding colour. I want to make the array smaller each time so there is no duplicates. Right now, I have 13 numbers being displayed at a time, and still have a long way to go.

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

int main()
{
    int x = 51;
    while (x >=1)
    {
        int ar[x] = {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};

        int ft = 14;

        srand(time(NULL));
        int rNumber = rand() % x;

        if (rNumber < ft)
        {
            cout << ("yellow") << ar[rNumber] << "\n";
            x--;
        }
        else if (rNumber < ft * 2)
        {
            cout << ("blue") << ar[rNumber] << "\n";;
            x--;
        }
        else if (rNumber < ft * 3)
        {
            cout << ("red") << ar[rNumber] << "\n";
            x--;
        }

        else cout << ("black") << ar[rNumber] << "\n";
        x--;
    }
}
Aaron Ryan
  • 97
  • 1
  • 10
  • Using `std::vector` instead of raw c arrays might help to improve that code a lot. – πάντα ῥεῖ Feb 27 '19 at 23:00
  • Thanks,I'm not familiar with vectors and I'm just trying to see if i can do this with the knowledge i have,I will try read more into them and see can i understand them – Aaron Ryan Feb 27 '19 at 23:04
  • [This](https://stackoverflow.com/questions/8861568/fisher-yates-variation) might work well enough (as a base, at least) to getting a shuffle. Should work on a fixed array, as well – Phil M Feb 27 '19 at 23:06
  • There is a lot of things this code could be doing more efficiently (using structs/classes, using 4 `int[13]` arrays, or at least one `int[4][13]` array, instead of a single big `int[52]` array, using STL algorithms, etc). But, some issues I do see are 1) calling `srand()` too many times, 2) incrementing `x` 1 too many times when the final `else` (black) is reached, and 3) not actually "removing" elements from the array when decrementing `x`. – Remy Lebeau Feb 27 '19 at 23:11
  • Remy Lebeau that is one of the problems I mentioned that I'm not positive on how to remove the elements from the array,and for decrementing on the final else would you recommend using another else if? Or something different altogether? – Aaron Ryan Feb 27 '19 at 23:20
  • You can't really remove an element form an array. The only things you can do with an array of `int` is to find a value that cannot exist in the dataset and use it as a marker for an empty slot, writing this value over deleted elements, or move everything in the array after the element to be removed back one element and update a book-keeping value that stores the number of items in use in the array. – user4581301 Feb 27 '19 at 23:46

2 Answers2

0

To Remove a certain element of an array, I look at the last index in the array, then swap it with the index you want to remove. Then, decrement the size of the array. Eg:

int arr [] = {1,2,3,4,5};
int itemCount = 5; 

If I want to remove the 2nd item in the array, I would do:

arr[1] = arr[itemCount-1]; //Assigns 4th Index of array to 2nd Index.
itemCount--;

New array would look like this if you displayed it:

{1,5,3,4}
Aaron
  • 1
  • 3
0

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 be14. Think about yourifstatementsYELLOW < 14, thenBLUE < 28andRED < 42, that's not right, that only leaves10possible cards forBLACK`. 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.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85