1

I'm very new to C++ and have a pretty daunting assignment due this week. I won't get into too much detail about it as I don't want work done for me, but can anyone point me in the right direction as to how I would go about picking random characters from a multi-dimensional string array?

char gameZero[16][6] = { {'A','A','C','I','O','T'}, {'A','H','M','O','R','S'}, {'E','G','K','L','U','Y'}, {'A','B','I','L','T','Y'}, {'A','C','D','E','M','P'}, {'E','G','I','N','T','V'}, {'G','I','L','R','U','W'}, {'E','L','P','S','T','U'}, {'D','E','N','O','S','W'}, {'A','C','E','L','R','S'}, {'A','B','J','M','O','Q'}, {'E','E','F','H','I','Y'}, {'E','H','I','N','P','S'}, {'D','K','N','O','T','U'}, {'A','D','E','N','V','Z'}, {'B','I','F','O','R','X'} };

The goal is to print out a 4x4 board of these characters, picking one random character from each array at the start of every game so not one board is the same.

Any advice/tips on how to do this would be greatly appreciated-- thank you!

EDIT:

I have gotten to a point where I can generate a random indices from both arrays, however the current way I am doing it only outputs one character, whereas I need ONE char from each of the 16 assortments within the array. Any ideas on how I can output one from each? I'd imagine I have to put the index generators inside some sort of loop.

mt19937 gen(time(nullptr)); // random number generator

// used to generate random number in specific range 
uniform_int_distribution<int> random_outer(0, outer_size - 1);
uniform_int_distribution<int> random_inner(0, inner_size - 1);

int index_outer = random_outer(gen); // used to generate random outer index
int index_inner = random_inner(gen); // used to generate random inner index

cout << gameZero[index_outer][index_inner] << endl;
John B
  • 109
  • 8
  • Just to understand your question better for e.x if we pick 'A' at random for 1st element in 4x4 then any of the rest 15 can't be 'A'? – Abdul kareem Oct 06 '19 at 23:58
  • @Abdulkareem hi! There is no restriction on how many duplicate letters that can appear on the board, it is essentially to shuffle the board to display a random set of one character from each of the 16 arrays. So, technically, if there was an A in each array, it would be fine to have a 4x4 board of A's. (That wouldn't be a very fun game, but there is nothing telling me otherwise at the moment) – John B Oct 07 '19 at 00:03
  • 1
    If it is a two dimensional array, with dimension `16` and `6`, then generate two random integral values. One between `0` and `15`, and the other between `0` and `5`. Then use that pair of values as indices to access an element of the array. There is plenty of information on generating random integral values in C++. – Peter Oct 07 '19 at 00:06
  • @Peter After doing some more digging I figured this would be the best approach and am trying it right now. Thank you! – John B Oct 07 '19 at 00:10
  • So one strategy could be begin by initializing a pointer to point to first array out of 16 then in first iteration pic a random number(you can use `rand()`) between 0 and 5 inclusive and advance pointer by that many locations, assign that pointer value to the new array's current element. In the next iteration advance the ponter using result you got from `rand()`i.e., by ('rand( )' - 5 +1) – Abdul kareem Oct 07 '19 at 00:11
  • @Abdulkareem I wish I could say I knew much of pointers. I'm having a hard time getting a full grasp of how to implement them/what they do, unfortunately :( They are one thing I have been to get used to in my spare time – John B Oct 07 '19 at 00:15
  • @John B that is not an issue as `*(p+i)` is equal to `p[i]` so you can use the later and if even the selection of rows have to be random then follow the process of @Peter as I am going sequentially through rows picking random numbers among them – Abdul kareem Oct 07 '19 at 00:18
  • @Peter we could also generate just 1 random number instead of 2 with values between 0 and 95 and assign the *(p+ans) in the new array, right? – Abdul kareem Oct 07 '19 at 00:24
  • @Abdulkareem - since the array is contiguous, sure. I wouldn't recommend it though - resorting to pointer arithmetic in C++ is possible, but also often a code smell. If the data is in a two-dimensional array, the code will be clearer by explicitly generating two indices. Generating two indices will also work if the data is bundled into standard containers (like `std::vector >` where the data is not necessarily contiguous - and pointer arithmetic like you describe would not work. – Peter Oct 07 '19 at 00:43
  • @Peter sure, thanks – Abdul kareem Oct 07 '19 at 00:47
  • So, I've gotten to a point where I can now generate and print a random index from within the 2D array. I'm only having trouble figuring out how I can now pick one random from all 16 character bundles, generating 16 random characters. Would I perhaps need to only generate a random index from the inner array and loop through the other 16 elements? Something like: gameZero [i] [//whatever random number is generated] – John B Oct 07 '19 at 00:55

4 Answers4

2

You can use the following algorithm:

  • generate random number x in the interval [0, std::size(gameZero )[
  • generate random number y in the interval [0, std::size(gameZero[0])[
  • Access gameZero[x][y] for a random character of the 2D array.
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Off by one error. The ranges would need to be `[0, std::size(gameZero) - 1]` and `[0, std::size(gameZero[0]) - 1]` respectively. – Peter Oct 07 '19 at 00:45
  • @Peter `[0, std::size(gameZero) - 1]` and `[0, std::size(gameZero)[` are the same thing. The reversed bracket denotes a half-open interval. – eerorika Oct 07 '19 at 01:13
  • The modern mathematical notation for an open closing end of an interval is `)`. `[` for the open end of an integral range is archaic, even in mathematical literature, and has been actively discouraged over the last 100 years or so by most active mathematical publications. – Peter Oct 07 '19 at 02:01
  • @Peter I haven't heard of that before. Do you have a reference for your claim? Both notations are in the ISO 80000-2:2009 standard. I used the bracket notation here to improve readability by making it easy to distinguish from the value expression that ends in closing paren. – eerorika Oct 07 '19 at 02:57
0
  • provide a list of int with 6 items.
  • initialize it with index values from 0 - 5.
  • for every cell in a board: //from 0 - 15.
    • shuffle the list-of-int.
    • for every 4x4 board you have: //let's say max of 6.
      • set the current cell of the current board with item in list-of-int at index iBoard.
        • //example: board[iBoard][iCell] = listInt[iBoard]
acegs
  • 2,621
  • 1
  • 22
  • 31
0

Given your gameZero of type char[16][6], you need only generate a single random number that represents an offset from the beginning of gameZero in the range (0, sizeof gameZero). An array of any fundamental type (char, int, double, etc...) is guaranteed to be sequential in memory. Further, in your case with type char, each char is 1-byte. So you can access any of the characters in gameZero by computing an offset from the start of the array.

As you have found, C++ provides its own Pseudo-random number generation. You can pick any one of the distribution engines to create a random distribution. When you need a random number from the make a call using your distribution passing the engine you have created an instance to as its argument. For example to use create a random distribution using the std::mersenne_twister_engine, you could do:

#include <random>
...
    char gameZero[16][6] = {{'A','A','C','I','O','T'}, 
    ...
    std::random_device rd;              /* random device/seed */
    std::mt19937 gen(rd());             /* standard mersenne_twister_engine */
    std::uniform_int_distribution<> dist(0, sizeof gameZero);    /* dist */

Then in order to pick a random element representing an offset within gameZero you simply request a random number using:

        int off = dist(gen);        /* get random offset in gameZero */

From the offset, the [x][y] element within gameZero can be computed simply by using the number of columns in your array (6 in your case), e.g.

        int x = off / 6,            /* compute row from offset */
            y = off - 6 * x;        /* compute col from offset */

(note: the integer division is intentional in computing the x (row) value above)

You can then access the random element simply with gameZero[x][y].

For example, if in a loop you request a random offset and compute the [x][y] indexes as explained above, you can output the offset, the computed indexes and the character at that index similar to the following:

$ ./bin/randarr2d
92 - gameZero[15][2] = F
 9 - gameZero[ 1][3] = O
14 - gameZero[ 2][2] = K
91 - gameZero[15][1] = I
86 - gameZero[14][2] = E
46 - gameZero[ 7][4] = T
92 - gameZero[15][2] = F
33 - gameZero[ 5][3] = N
87 - gameZero[14][3] = N
57 - gameZero[ 9][3] = L
87 - gameZero[14][3] = N
50 - gameZero[ 8][2] = N
10 - gameZero[ 1][4] = R
23 - gameZero[ 3][5] = Y
...

Understand, you can access the element directly with the offset alone as well, but that would cause a technical violation of array bounds. (e.g. *(*gameZero + off)) The short-story is a 2D array is actually an array of 1D arrays. When you access an array it is converted to a pointer to its 1st element. In the case of the 2D array, that is a pointer to the first array. (the 1st 6-char array in your case), so if you apply an offset of more than 5, you are technically addressing an element outside of the bounds of the 1st 1D array even though you are still within the bounds of your 2D array. Computing the x, y indexes from the single offset eliminates that issue.

Look things over and let me know if you have any further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thank you so much for the incredibly detailed response! My problem is now solved. I am new to this forum, so I find it very refreshing to see such a helpful community. – John B Oct 07 '19 at 14:33
0

With a mixture of help thanks to all of the people who responded in this thread, I believe I've come to a reasonable answer to my problem. If anyone happens to come across this thread, here it what I came up with:

void shuffleDice() { // shuffles Game #0 dice. 

mt19937 gen(time(nullptr));
uniform_int_distribution<int> random_inner(0, inner_size - 1);

int n = random_inner(gen); // generates random number for inner array 
for (int i = 0; i < 16; i++) // loops through outer array
{
    cout << gameZero[i][n] << '\n';
}

Keep in mind I am new to programming/C++, so it may not be the prettiest, but it works nonetheless.

John B
  • 109
  • 8