3

I'm using CPP to make a blackjack game, but I'm stuck on how to randomly pick an element from my [4][13] array without repeating it and without shuffling the order of the array. Working in VisualStudio. So far I can only find answers for how to do this with a 1-D array. Anybody out there have suggestions?

askewchan
  • 45,161
  • 17
  • 118
  • 134
user2755333
  • 31
  • 1
  • 2

8 Answers8

3

A 2D array can easily be thought of a 1D array. You just need to do a little math.

If you have a solution that works for a 52 element 1D array (a new array with values 0-51 randomly ordered). do the following:

  1. generate your random element, lets call it R (between 0 and 51).
  2. now, convert that number into your 2D array system. (form of x,y)
    1. x = R / 13
    2. y = R % 13
Colin D
  • 5,641
  • 1
  • 23
  • 35
  • 1
    And the solution for the 1d array is: Just create a randomized array with numbers from 1-52, then iterate through it. No repetitions, no performance lack. – Ingo Bürk Sep 06 '13 at 19:10
  • Why don't you add Fisher-Yates to your answer to make it complete. "https://en.wikipedia.org/wiki/Knuth_shuffle" – AndersK Sep 06 '13 at 19:13
  • @claptrap Thanks for the link, looks like an interesting read. However, if I were add information about randomization to my answer, I think I would prefer to just mention `std:random_shuffle` – Colin D Sep 06 '13 at 19:34
2

Use std::random_shuffle to randomly shuffle a vector (say vector<int> v) from 0-51. Since random_shuffle shuffles the given vector, v is now your randomly shuffled vector.

v.back() returns the last element in the vector. So, the following code returns the last element (k) from your vector and then removes it. It also converts it to a double so you can compute k/13.

double k = static_cast<double>(v.back());
v.pop_back();

Now, since your matrix is 4x13, the kth element (row-major) is at row floor(k/13) and column k%13.

Jacob
  • 34,255
  • 14
  • 110
  • 165
1

Well, one way to do this would be to add a bool field to the elements of the array, and when that element of the array gets picked set it to true. Then on future selections, check if that bool is true, and if it is, generate another random element. May not be the most efficient but it is simple.

With this, you could also easily create a method to iterate through the array and set all the bool fields to false to simulate a shuffle.

jlars62
  • 7,183
  • 7
  • 39
  • 60
  • I think "not the most efficient" is an understatement. The more values have been used, the more attempts it will take to find another, unused value. – Ingo Bürk Sep 06 '13 at 19:02
  • after a certain number of tries, say 10 hands or so, there should be a shuffle method to set all the bools to false. This will alleviate this issue to an extent. – user2366842 Sep 06 '13 at 19:03
  • This is actually the method I used in creating a blackjack game in javascript – smac89 Sep 06 '13 at 19:05
  • @IngoBürk True, on average the worst case would take 52 attempts, which probably wouldn't be noticeable. Though for bigger arrays it would become a problem. – jlars62 Sep 06 '13 at 19:05
  • Also, picking an element at random from an array shouldn't be too cpu intensive, so even if you have to try 20 or even 30 times before finding one, it won't be noticeable (maybe a fraction of a second lag). This isn't like calculating factoral numbers or anything where your cpu is actually doing hard work. – user2366842 Sep 06 '13 at 19:07
  • 1
    But why go for an inferior solution if a better one is available? Generate a randomized array with numbers from 1 to 52, then iterate through it and map the index to a 2d position. – Ingo Bürk Sep 06 '13 at 19:11
  • 2
    The problem with just flagging used elements is that as the cards get used up it becomes less and less likely to randomly find an unused one. What you need to do is generate a list of the cards and remove them from the list as each one is used. – Mike Makuch Sep 06 '13 at 19:13
  • that's probably a better solution....though you'll have to rebuild the list every time you're "shuffling" – user2366842 Sep 06 '13 at 19:14
  • 1
    But it's reliable and quite frankly the "I'll just try again and again and again" approach just feels dirty und unasthetic. – Ingo Bürk Sep 06 '13 at 19:17
  • Yes, I understand there are better solutions, and that this solution has problems in efiiciency. All I'm offering to the OP is an answer to his question, and seeing that he is likely new to CPP (c++?), i think simplicity is important. My answer states that it is not the most efficient, which should prompt him to look for more efficient methods if that is something he is interested in. Creating an array of numbers 1-52 in random order with no duplicates may be more complicated than he wants to deal with. The same thing may apply to converting those indexes to 2d indexes. – jlars62 Sep 06 '13 at 19:26
  • Often I think it is more important to first get a working solution, then look for optimizations, instead of first looking for optimizations. But I understand a lot of this is just preference, so you may disagree. – jlars62 Sep 06 '13 at 19:27
  • True he didn't. Im assuming he is representing cards which means he is likely using something more than a primitive. I didn't know about the method `random_shuffle`, but your right with that it is much simpler. – jlars62 Sep 06 '13 at 19:33
  • I agree on "working solution first, then optimizing". But this is just such a common problem that anyone who has ran into it already knows the (or at least a) optimized version. I don't think either solution is more complicated, really. They both operate on the same level of abstraction and none requires substantially more work. – Ingo Bürk Sep 06 '13 at 19:38
  • 1
    Folks, While I realize this is not at all the most efficient of methods, it is unfortunately the one with which I am stuck. I'm doing this for an assignment, and my professor wants it done a specific way so that we can get practice with using arrays of more than one dimension. – user2755333 Sep 06 '13 at 20:45
1

I would suggest you strongly consider using the std::deque<T> container. a double ended queue to represent the deck itself then you can push and pop 'cards' out of it as you deal them out.

UpAndAdam
  • 4,515
  • 3
  • 28
  • 46
1

A side note: don't hardcode the number 52: chances are you'd want to shuffle multiple decks in the same array. In the game of blackjack casinos often use multiple decks in blackjack "shoe", in particular 4 decks per shoe is quite common. This has significant implication on conditional probabilities: less correlation between the face up cards and face down ones. You won't want to shuffle the 52-card deck separately, you'd need to shuffle the entire N*52-card shoe. The algorithms proposed in the other answers would work with 3-D arrays as well as with 2-D arrays, with only minor modifications.

Michael
  • 5,775
  • 2
  • 34
  • 53
0

One method I always like was to take the 2d array and then generate 2 coordinates to swap. Do that n number of times and then you can just iterate through the 2D array using some for loops.

CoderDake
  • 1,497
  • 2
  • 15
  • 30
0

I would rather use a list with all the cards in it. IF you insist on a 2d array with [4] being suites [13] being numbers the answer is just as simple make the deck in place and never changing values however, make the array variables Boolean.

Ill write pseudo code.

initialize cardsselected to zero

Choose Card
        Randomly select 2 numbers 1 will be 0-3 and the other 0-12. 
        If the array at [random suite][random number] is not false 
             choose it 
             increment cardsselected 
             set value to false
             return true
        else if cardsselected  equals 52 return false
        else 
            run chooseCard() 
            return true

This is just a solution. It is not very optimized, it can take forever.

To optimize it.

You can keep Your 2d array to hold the cards in place just generating random numbers. To optimize the random number generation you can create a list of random numbers 1-52 and randomly pick from that. Then remove the number that was picked from the list.

progrenhard
  • 2,333
  • 2
  • 14
  • 14
  • I actually thought of trying to do it by assigning sequential numbers to each cell, but my professor wants us to build the "deck" as a char with initial value " ". This is so we can later produce an output listing which cards went to which players, by updating a given cell with the number of the player to whom the card was dealt. Like I said, I realize it may not be the most efficient of methods. I'm just trying to follow instructions :-/ – user2755333 Sep 06 '13 at 20:49
  • @user2755333 This will still work... just very not efficiently. You have your array of chars correct? Have two arrays or two decks.. one a "clean deck" and a "usable deck." The clean deck will have the values always perfectly in order. Same with the usable deck however you will be setting the values to a default char (null) whenever you select it. Instead of false. Then follow the same logic as my code and it should work. Then whenever you want to reshuffle throw the values of the clean deck into the usable deck. – progrenhard Sep 06 '13 at 20:57
0

There is a simple solution that involves using prime numbers. If you take a random initial index in your array (for simplicity, let it be a 1D array), then increment this index by a prime that is bigger than array length and take only the reminder after division by array length, you will obtain a sequence of random non-repeating indices.

Something like this:

#include <cstdlib>
#include <iostream>
#include <time.h>

const int Cols = 4;
const int Rows = 13;
const int N = Cols * Rows;

const int NPrimes = 12;
const int primes[NPrimes] = {59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107};

int prime = primes[0];
int index = 0;

void shuffle()
{
    srand(time(NULL)); // Random seed
    prime = primes[rand() % NPrimes]; // Get random prime number
    index = rand() % N; // Get random first index
}

// Compute next index in sequence
int nextIndex(int index)
{
    return (index + prime) % N;
}

int _tmain(int argc, _TCHAR* argv[])
{
    shuffle();

    for (int i = 0; i < N; i++) {
        int col = index / Rows;
        int row = index % Rows;
        std::cout << "[" << col << ", " << row << "] ";
        index = nextIndex(index);
    }
    std::cout << std::endl;
    return 0;
}

After N iterations the sequence will repeat itself, obviously.

Archie
  • 2,644
  • 21
  • 21