Blackjack: Issues with repeating integers in array

Pages: 12
I've been making a blackjack program to refamiliarize myself with the C++ programming language; I have learnt up to reading and writing from a file, but it's been quite a while. I've been trying to dynamically allocate a struct that essentially functions as a deck of cards. I've been having many issues, including having my code crash upon every build due to some VS error; anything from some asinine issue with printf & scanf; to other errors such as "wntdll.pdb" not loaded. at times, the vs activity log has popped up in microsoft explorer, with many red error in the xml file. Hell, sometimes, I've had my program not run on four separate attempts, just to have it function with a printf Hello World statement included.
Either way, I'm not trying to blame VS, as I know that I'm a beginner, and it's probably some issue with my lack of familiarity with dynamic memory allocation; or some other newbie mistake that is way above my head. There are a few consistent issues I've seen outside of the aforementioned issues with the IDE as well.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#include <iostream>
#include <time.h>
int randIndex[52];
int randMark = 0;
struct card {
    int value;
    int index = 0;
    int sumTotal = 0;
    char suit;
    char ID;
};
void setDeck(card*);
char setSuit(int);
void distributeCard(card*, card*, card*, int);
int repetitionCheck(int);
int main()
{
    srand(time(0));
    card* gameDeck = new card;
    card* pDeck = new card;
    card* cDeck = new card;
    printf("Blackjack!\n\n");
    setDeck(gameDeck);
    for (int i = 0; i < 52; i++)
    {
        if (i % 4 == 0)
        {
            printf(" >\n\n");
        }
        printf("|%d%c|\t", gameDeck[i].value, gameDeck[i].suit);
    }
    distributeCard(gameDeck, pDeck, cDeck, 26);
    printf("Player Hand\n");
    for (int i = 0; i < 26; i++)
    {
        if (i % 4 == 0)
        {
            printf(" >\n\n");
        }
        printf("|%d%c|\t", pDeck[i].value, pDeck[i].suit);
    }
    printf("\CPU Hand\n");
    for (int i = 0; i < 26; i++)
    {
        if (i % 4 == 0)
        {
            printf(" >\n\n");
        }
        printf("|%d%c|\t", cDeck[i].value, cDeck[i].suit);
    }
    printf("\nRand Hand\n");
    for (int i = 0; i < 52; i++)
    {
        if (i % 4 == 0)
        {
            printf(" >\n\n");
        }
        printf("|%d|\t", randIndex[i]);
    }
    delete gameDeck;
    delete pDeck;
    delete cDeck;
    return 0;
}
void setDeck(card* aDeck)
{
    int tempIndex;
    for (int i = 0; i < 13; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            tempIndex = aDeck->index;
            aDeck[tempIndex].value = i + 1;
            aDeck[tempIndex].suit = setSuit(j);
            aDeck->index++;
        }
    }
}
char setSuit(int aNum)
{
    switch (aNum)
    {
    case 0: return 'S';
        break;
    case 1: return 'C';
        break;
    case 2: return 'D';
        break;
    case 3: return 'H';
        break;
    default: return '?';
        break;
    }
}
void distributeCard(card* gDeck, card* pDeck, card* cDeck, int max)
{
    int pIndex, cIndex, nuNum;
    for (int i = 0; i < max; i++)
    {
        nuNum = repetitionCheck(rand() % 52);
        randIndex[randMark] = nuNum;
        randMark++;
        pIndex = pDeck->index;
        pDeck[pIndex].value = gDeck[nuNum].value;
        pDeck[pIndex].suit = gDeck[nuNum].suit;
        pDeck->index++;
        nuNum = repetitionCheck(rand() % 52);
        randIndex[randMark] = nuNum;
        randMark++;
        cIndex = cDeck->index;
        cDeck[cIndex].value = gDeck[nuNum].value;
        cDeck[cIndex].suit = gDeck[nuNum].suit;
        cDeck->index++;
    }
}
int repetitionCheck(int aNum)
{
    for (int i = 0; i < randMark; )
    {
        if (aNum == randIndex[i])
        {
            aNum = rand() % 52;
            i = 0;
        }
        else
            i++;
    }
    return aNum;
}

As for the output; it consistently looks like this (I've gotten random garbage data to stop appearing, but there are duplicates here, and on multiple compiles, the series of 2 cards almost rarely show up):
(Note, program output button wasn't working on the ribbon, so the console output may not be displayed in its proper format, apologies in advance).

Blackjack!
|1S|    |1C|    |1D|    |1H|
|2S|    |2C|    |2D|    |2H|
|3S|    |3C|    |3D|    |3H|
|4S|    |4C|    |4D|    |4H|
|5S|    |5C|    |5D|    |5H|
|6S|    |6C|    |6D|    |6H|
|7S|    |7C|    |7D|    |7H|
|8S|    |8C|    |8D|    |8H|
|9S|    |9C|    |9D|    |9H|
|10S|   |10C|   |10D|   |10H|
|11S|   |11C|   |11D|   |11H|
|12S|   |12C|   |12D|   |12H|
|13S|   |13C|   |13D|   |13H|
Player Hand
|13S|   |1S|    |1S|    |5H|
|3D|    |9S|    |5D|    |13H|
|6C|    |13H|   |11S|   |8D|
|7H|    |11H|   |13H|   |6C|
|8H|    |12H|   |10C|   |1H|
|7S|    |13D|   |5H|    |12C|
|12D|   |9C|
CPU Hand
|2D|    |6D|    |9D|    |11D|
|5C|    |1D|    |4H|    |10D|
|1C|    |7C|    |13S|   |10H|
|9S|    |6H|    |11S|   |11C|
|10S|   |7D|    |12S|   |13C|
|7H|    |3D|    |8S|    |8C|
|8H|    |9H|
Rand Hand
|48|    |6|     |0|     |22|
|5|     |34|    |19|    |42|
|10|    |17|    |32|    |2|
|18|    |15|    |51|    |38|
|21|    |1|     |11|    |25|
|40|    |4|     |30|    |39|
|27|    |9|     |43|    |23|
|13|    |14|    |12|    |41|
|31|    |36|    |47|    |26|
|37|    |44|    |3|     |49|
|24|    |16|    |50|    |8|
|7|     |28|    |45|    |29|
|46|    |20|    |33|    |35|


Last edited on
printf and scanf work in c++ but are technically C code. C++ was originally an extension to C (its branched farther apart over the decades) and even today 80% or more of C is directly available in C++. You should avoid the C tools unless you have a really good reason to use them, though.
cin and cout are the c++ read and write to keyboard/screen methods.

visual studio has a moronic complaint about the C tools because some 'genius' at M$ re-wrote them and then added a hack to the compiler to make it complain when you use the originals instead of his 'improved' version. Is that the complaint you get? There is a pragma command you can look up to make it stop being an idiot about it, but its better to just use the c++ versions as I said above.

C++ has an 'array like container' called a vector.
you can put all the cards into a vector, shuffle it (there is a shuffle method for it!) and then just iterate it sequentially to play cards. You don't have to fight with all the duplication stuff by hand. If you want to do it just to be doing it this way, to learn the algorithms and such, you can though.... let us know what you want to see here. One easy way to fix it is to add a 'played' boolean to card struct and initialize to false, set it to true once its been consumed.

<random> is c++ random numbers. even C coders avoid rand() ... rand is old, its extremely poor quality, and its best to forget it exists.

PDB is a program database, for debugging purposes. I don't know what you did to trigger this, but its a project level problem, not a code level problem.

.h was removed due to a language upgrade for the standard includes. The C ones and a few "what were they thinking" ones are preceeded with a C instead,
<ctime>
<cstring> (string.h, and VERY different from c++ strings in <string>)
<cstdio>
--------------
<cmath> (oddball, math is not a C tool)


All that to say, try https://www.learncpp.com/ to learn modern c++. Whatever you are using is teaching you with too much C in it.

Let us know how you want to handle this (I would recommend a do-over with C++ tools) code and if you want to dig in and fix it just cause, we can do that....

----------
your code is pretty advanced for a raw beginner. if you know another language that has OOP features, in C++ a struct and a class are both objects and you can tack the methods to the objects instead of passing the struct to free floating functions. Here again, you are following a C style -- C does not have OOP features directly (you can put some of them in with smoke and mirrors but its not well supported).

x = new type; //get ONE item.
x = new type[count];//get an ARRAY like block of items.

you are not allocating your deck etc as you needed to do.

card* pDeck = new card[52];//jokers, etc? or just 52?
and delete syntax changes slightly for this too:
delete[] pDeck;
using pointers and dynamic memory are avoided in c++ as much as possible via containers that do it for you:
vector<card> Deck(52); //vector will hide and handle the pointers for you.

you can also just put them on the stack and avoid the memory allocation:
card Deck[52];//much simpler. this is a C style array, which is used regularly enough in C++ for simple tasks.
Last edited on
Your card struct just needs to hold value and suit, not the other 3 fields you have specified.

Put all the cards in a vector<card>(52) and use std::shuffle.
By beginner, I mean that I have taken a single course in C++ in a community college many years ago, and now, I am simply trying to refresh my memory on the course by doing this makeshift project.

I have the intention of deliberately using dynamic memory allocation, just so I can become more familiar with it. I am also intending on not using as many arbitrary values as possible, so the array can be of any size, as decided by the user (not too concerned about making a literal Blackjack program, more of one that follows the general form of Blackjack as a baseline). I would love for randIndex to not have a literal 52, and be more flexible based on eventual user input, but that is beyond my understanding at this point. I do not mind using modern c++ tools though, I had no intention of using c tools to begin with. Although I do want to be more familiar with arrays, so vectors are out for the time being. I realize I should have specified as such, and I apologize.

jonnin
I have replaced all printf statements with the appropriate cout, but that has caused my function to not execute all together after a single successful execution. It seems to have an issue with my use of the rand() function, which it didn't seem to have with printf(). I have since tried using notepad++, so I'm not sure that a reinstallation of VS will help. I have also taken your advice, and have tried to utilize the implementation of PRNG(), but every iteration offered on its pertaining section will not run for one reason or another. It states at the line the rand() is called in: "0xC0000005: Access violation reading location 0x00000017." The one time it did run however, there were still repeats in the output for both player hand and cpu hand.

lastchance
apologies, some of the fields such as ID are meant for displaying a certain deck without printing these all out in main(). I have deleted the displayDeck, hit, and winstate functions to pare down my code and figure out what my issue happens to be.
Last edited on
jonnin
For instance, when I try the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <time.h>
#include <random> // for std::mt19937
#include <ctime> // for std::time

using namespace std;

namespace MyRandom
{
	// Initialize our mersenne twister with a random seed based on the clock (once at system startup)
	mt19937 mersenne{ static_cast<mt19937::result_type>(time(nullptr)) };
}

int getRandomNumber(int min, int max)
{
	std::uniform_int_distribution <int>{ min, max }; // we can create a distribution in any function that needs it
	return MyRandom::mersenne; // and then generate a random number from our global generator
}


getRandomNumber return statement has various issues, the current one in this case, is stated as:
E0413	no suitable conversion function from "std::mt19937" to "int" exists

and when I cast int with (int), I get this error:

'type cast': cannot convert from 'std::mt19937' to 'int'	


and when I use the template(?) <int>, I receive "expected an expression" and "syntax error: '<' "

I am sure that I am using these wrong, but the sample in the learncpp.com tutorial you linked me was too literal in its solution unfortunately.

Also, I have since figured out the issue with "wntdll.pdb". I had to enable Miscrosoft Symbol Servers.
Last edited on
heh you are making it too difficlut :)
the twister has its own function that works like your get random number function, you don't need your own just use it directly. you can do % 52 *** to get 0-52 from these values or play with the settings to make it have a range. this site shows how to use the mt within a range

https://www.guyrutenberg.com/2014/05/03/c-mt19937-example/
and another site I like is geeks for geeks (this link won't help you now, but their site is great) https://www.geeksforgeeks.org/stdmt19937-class-in-cpp/

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <random>
#include <ctime>
using namespace std;

int main()
{
  mt19937 mt(time(nullptr));   
  for(int i = 0; i < 10; i++)
  cout << mt() << '\n'; 
  return 0;
}


*** modulo messes up randomness and is generally a poor choice for important code, but it works well enough for classroom problems if you don't want to fool with setting up the range.

take time.h out, its <ctime> just using the C version (it lacks the namespace that c++ uses, mostly).
Last edited on
Creating a deck of cards, shuffling and displaying the cards using C++ features:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <iostream>
#include <vector>
#include <random>
#include <numeric>

int main()
{
   // create a C++ random engine
   // seeding it with the non-deterministic random device
   // https://www.cplusplus.com/reference/random/
   // there are time based methods using <chrono>
   std::default_random_engine prng(std::random_device {} ());

   // creating an alias
   using card = unsigned;

   // manufacture a blank deck of cards
   std::vector<card> deck(52);

   // create the cards, 0 (zero) to 51
   std::iota(deck.begin(), deck.end(), 0);

   // two lambdas to display the card in brief text representation:
   auto rank = [] (card c) { return "AKQJT98765432"[c % 13]; };
   auto suit = [] (card c) { return "SHDC"[c / 13]; };

   // initialize a variable using uniform initialization
   // https://mbevin.wordpress.com/2012/11/16/uniform-initialization/
   card count { };

   // range-based for loop to 'walk through' the deck sequentially
   // and display the cards
   for (const card& c : deck)
   {
      std::cout << rank(c) << suit(c) << ' ';
      count++;

      if (0 == (count % 13)) { std::cout << '\n'; }
   }
   std::cout << '\n';

   // shuffle the deck:
   std::shuffle(deck.begin(), deck.end(), prng);

   // C++17 allows for in-loop initialization of variables for better scoping
   for (card ccount { }; const card& c : deck)
   {
      std::cout << rank(c) << suit(c) << ' ';
      ccount++;

      if (0 == (ccount % 13)) { std::cout << '\n'; }
   }
   std::cout << '\n';
}
AS KS QS JS TS 9S 8S 7S 6S 5S 4S 3S 2S
AH KH QH JH TH 9H 8H 7H 6H 5H 4H 3H 2H
AD KD QD JD TD 9D 8D 7D 6D 5D 4D 3D 2D
AC KC QC JC TC 9C 8C 7C 6C 5C 4C 3C 2C

QD QH 5S 9S 8S KS 2C 4S 2H 3D JD 5H 6D
5D 7D 4D 6S 9C KC 8H QC TD 6H 3C JS 5C
TC 8D 2D AC JC 9D AD 7H 9H AH 2S QS 6C
4H JH TH 8C 3H KD 7S 3S 4C TS AS 7C KH

Of course, the shuffled deck will be different each run.

The deck could have been represented by a std::array, replace line 18 with std::array<card, 52> deck;, changing the header include from <vector> to <array>.

Several of the ideas in the code snippet came from a working paper on random number generation in C++11: http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3551.pdf
jonnin
Thanks for the tip, I was able to get it running again with your sample. However, I seem to be encountering the same issue back when rand() and printf were being used. So it was probably an issue somewhere to my code to begin with, not with the tools I used (although I could be wrong ofc).

My current code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <iostream>
#include <random> // for std::mt19937
#include <ctime> // for std::time

using namespace std;

struct card {
    int value;
    int index = 0;
    int sumTotal = 0;
    char suit;
    char ID;
};

mt19937 mt(time(nullptr));
void setDeck(card*);
char setSuit(int);
void distributeCard(card*, card*, int, int*, int&);
int repetitionCheck(int*, int&);

int main()
{
    int randIndex[52];
    int randMark = 0;
    card* gameDeck = new card;
    card* pDeck = new card;
    card* cDeck = new card;


    cout << "BlackJack!\n\n";

    setDeck(gameDeck);

    for (int i = 0; i < 52; i++)
    {
        if (i % 13 == 0)
        {
            cout << "\n\n";
        }

        cout << '|' << gameDeck[i].value << gameDeck[i].suit << "|\t";
    }

    distributeCard(gameDeck, pDeck, 26, randIndex, randMark);
    distributeCard(gameDeck, cDeck, 26, randIndex, randMark);

    cout << "\n\nPlayer Hand";
    for (int i = 0; i < 26; i++)
    {
        if (i % 13 == 0)
        {
            cout << "\n\n";
        }

        cout << '|' << pDeck[i].value << pDeck[i].suit << "|\t";
    }

    cout << "\n\nCPU Hand";
    for (int i = 0; i < 26; i++)
    {
        if (i % 13 == 0)
        {
            cout << "\n\n";
        }

        cout << '|' << cDeck[i].value << cDeck[i].suit << "|\t";
        
    }

    cout << "\n\nRand Hand";
    for (int i = 0; i < 52; i++)
    {
        if (i % 13 == 0)
        {
            cout << "\n\n";
        }

        cout << '|' << randIndex[i] << "|\t";
        
    }

    return 0;
}

void setDeck(card* aDeck)
{
    int tempIndex;

    for (int i = 0; i < 13; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            tempIndex = aDeck->index;
            aDeck[tempIndex].value = i + 1;
            aDeck[tempIndex].suit = setSuit(j);
            aDeck->index++;
        }
    }
}

char setSuit(int aNum)
{
    switch (aNum)
    {
    case 0: return 'S';
        break;
    case 1: return 'C';
        break;
    case 2: return 'D';
        break;
    case 3: return 'H';
        break;
    default: return '?';
        break;
    }
}

void distributeCard(card* gDeck, card* aDeck, int max, int* randIndex, int& randMark)
{

    for (int i = 0; i < max; i++)
    {
        randIndex[randMark] = repetitionCheck(randIndex, randMark);
        
        aDeck[aDeck->index].value = gDeck[randIndex[randMark]].value;
        aDeck[aDeck->index++].suit = gDeck[randIndex[randMark++]].suit;
    }
}

int repetitionCheck(int* randIndex, int& randMark)
{
    int aNum = mt() % 52;
    for (int i = 0; i < randMark; )
    {
        if (aNum == randIndex[i])
        {
            aNum = mt() % 52;
            i = 0;
        }

        else
            i++;
    }

    return aNum;
}


Despite the changes you recommended, I have similar repeats in my output for both the player and cpu hands; despite the fact that the Game Deck and Rand Hand seem to be completely functioning -- no repeats, and it is these two arrays wherein the other hands are attaining their properties from. Here is the new output (changed formatting to Furry Guy's as it looked better):


BlackJack!


|1S|    |1C|    |1D|    |1H|    |2S|    |2C|    |2D|    |2H|    |3S|    |3C|    |3D|    |3H|    |4S|

|4C|    |4D|    |4H|    |5S|    |5C|    |5D|    |5H|    |6S|    |6C|    |6D|    |6H|    |7S|    |7C|

|7D|    |7H|    |8S|    |8C|    |8D|    |8H|    |9S|    |9C|    |9D|    |9H|    |10S|   |10C|   |10D|

|10H|   |11S|   |11C|   |11D|   |11H|   |12S|   |12C|   |12D|   |12H|   |13S|   |13C|   |13D|   |13H|


Player Hand

|8H|    |12C|   |3D|    |6H|    |5S|    |2S|    |9H|    |2C|    |4H|    |3S|    |12H|   |10S|   |6D|

|11D|   |7D|    |9D|    |4S|    |11C|   |5H|    |6C|    |6S|    |13C|   |2D|    |13H|   |9S|    |10D|


CPU Hand

|5S|    |2S|    |9H|    |2C|    |4H|    |3S|    |12H|   |10S|   |6D|    |11D|   |7D|    |9D|    |4S|

|11C|   |5H|    |6C|    |6S|    |13C|   |2D|    |13H|   |9S|    |10D|   |1H|    |13D|   |10C|   |12S|

Rand Hand

|31|    |45|    |10|    |23|    |17|    |24|    |40|    |9|     |39|    |28|    |29|    |48|    |13|

|2|     |14|    |25|    |30|    |7|     |27|    |43|    |1|     |33|    |11|    |46|    |0|     |18|

|16|    |4|     |35|    |5|     |15|    |8|     |47|    |36|    |22|    |42|    |26|    |34|    |12|

|41|    |19|    |21|    |20|    |49|    |6|     |51|    |32|    |38|    |3|     |50|    |37|    |44|


The debugger either complains about heap corruption:
0xC0000374: A heap has been corrupted (parameters: 0x77DE3960).
Or some other issue such as access violation reading a location of some sort. I can only imagine that my repetitionCheck is somehow failing to index properly, but I wouldn't even know where to begin. I guess somehow, I can be manipulating memory in a reckless manner, but from what I can tell, I've hardly touched it.

Something else of note is that cout has the same warning (green squiggles) that printf did back when I still implemented it.

It states:

C6385: Reading invalid data from '_Deck': the readable size is 1*16 bytes, but the '32' bytes may be read.


Could this somehow be the issue I'm facing?
Last edited on
You still have the same issue in your code as before, which jonnin pointed out in his first message.

Line 25: Allocates ONE instance of card. Not 52.

Line 32: You call setdeck, passing a pointer to the single instance of card.

Lines 94-95: You're indexing through adeck (gameDeck) as if there are multiple occurrences of it. There are not. The is only ONE card.

Line 26-27: Ditto for pDeck and cDeck.

Here's a cleaned up version (not tested).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include <iostream>
#include <random> // for std::mt19937
#include <ctime> // for std::time

using namespace std;

struct card {
    int value;
    int index = 0;
    int sumTotal = 0;
    char suit;
    char ID;
};

mt19937 mt((unsigned int)time(nullptr));
void setDeck(card*);
char setSuit(int);
void distributeCard(card*, card*, int, int*, int&);
int repetitionCheck(int*, int&);

void print_deck(card* deck, int cnt)
{
    for (int i = 0; i < cnt; i++)
    {
        if (i % 13 == 0)
        {
            cout << "\n\n";
        }

        cout << '|' << deck[i].value << deck[i].suit << "|\t";
    }
}

int main()
{
    int randIndex[52];
    int randMark = 0;
    card* gameDeck = new card[52];
    card * pDeck = new card[52]; 
    card* cDeck = new card[52];

    cout << "BlackJack!\n\n";

    setDeck(gameDeck);
    print_deck(gameDeck, 52);
   
    distributeCard(gameDeck, pDeck, 26, randIndex, randMark);
    distributeCard(gameDeck, cDeck, 26, randIndex, randMark);

    cout << "\n\nPlayer Hand";
    print_deck(pDeck, 26);
    
    cout << "\n\nCPU Hand";
    print_deck(cDeck, 26);
    
    cout << "\n\nRand Hand";
    for (int i = 0; i < 52; i++)
    {
        if (i % 13 == 0)
        {
            cout << "\n\n";
        }

        cout << '|' << randIndex[i] << "|\t";

    }
    delete[] gameDeck;
    delete[] pDeck;
    delete[] cDeck;
    return 0;
}

void setDeck(card* aDeck)
{
    int tempIndex;

    for (int i = 0; i < 13; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            tempIndex = aDeck->index;
            aDeck[tempIndex].value = i + 1;
            aDeck[tempIndex].suit = setSuit(j);
            aDeck->index++;
        }
    }
}

char setSuit(int aNum)
{
    switch (aNum)
    {
    case 0: return 'S';
        break;
    case 1: return 'C';
        break;
    case 2: return 'D';
        break;
    case 3: return 'H';
        break;
    default: return '?';
        break;
    }
}

void distributeCard(card* gDeck, card* aDeck, int max, int* randIndex, int& randMark)
{

    for (int i = 0; i < max; i++)
    {
        randIndex[randMark] = repetitionCheck(randIndex, randMark);

        aDeck[aDeck->index].value = gDeck[randIndex[randMark]].value;
        aDeck[aDeck->index++].suit = gDeck[randIndex[randMark++]].suit;
    }
}

int repetitionCheck(int* randIndex, int& randMark)
{
    int aNum = mt() % 52;
    for (int i = 0; i < randMark; )
    {
        if (aNum == randIndex[i])
        {
            aNum = mt() % 52;
            i = 0;
        }

        else
            i++;
    }

    return aNum;
}
Last edited on
AbstractionAnon
There are still duplicates in your version of the code. I'm assuming that my check for repeats function is somehow flawed. I also get that maybe I allocated memory wrong, but can you explain how it is that 80% of my array is correct in my output, despite not doing it the way you have recommended. I still have an array, and it still functions as well as yours does.

Weirdly enough, I sorta feel as though printf, and maybe even somehow cout, is manipulating the memory in my output, which I'm more than willing to accept is a stupid theory, but I don't know how else to solve this issue -- especially when I stopped my program is crashing simply by moving the "Blackjack!" cout prior to setDeck, which makes absolutely no sense to me.

Outside of knowing what is possibly wrong with my repetition function (that is meant to go through the entire index of accepted numbers, 0 through 51, and filter out any repeats; which randHand output show is successful; all just to have the output have a few duplicates regardless for some reason or another.
Last edited on
I did not debug your repetitionCheck function. It's clearly problematiic though.

When you allocate only one card, then index through 26 or 52 entries that don't exist (except the first), that is ILLEGAL. You are referencing memory that does not belong to your array.
You are at the mercy of the heap as to whether you get an illegal address reference, some other error, or simply write to or read from random heap memory.

Yes, printf or cout can allocate memory from the heap. So it's very possible either of those routines overwrote parts of your non-existent arrays.

Check if your implementation has a heap_check routine. If it does try calling it after you modify your non-existent array, It will tell you that you've corrupted the heap.




Last edited on
AbstractionAnon

When you allocate only one card, then index through 26 or 52 entries that don't exist (except the first), that is ILLEGAL. You are referencing memory that does not belong to your array.
You are at the mercy of the heap as to whether you get an illegal address reference, some other error, or simply write to or read from random heap memory.


Yes, but what causes the output to be about 80% correct despite this fact. shouldn't it be incapable of running at all? and the modified code you game me has the precise same issues.
I'll omit all the couts that are not related to the loops, and see if that causes anything to occur.


Check if your implementation has a heap_check routine. If it does try calling it after you modify your non-existent array, It will tell you that you've corrupted the heap.

Not entirely sure what you mean by this. I don't know what a heap check is. If you can point me at some literature that can be of help, I'd appreciate it.
Last edited on
Abstraction Anon

Also, as you have pointed out, doing the following:
1
2
3
    card* gameDeck = new card[52];
    card * pDeck = new card[52]; 
    card* cDeck = new card[52];

in my code worked, despite the fact that copy pasting yours didn't. Although, doesn't this defeat the purpose of what I'm trying to do? Perhaps it isn't possible, but is there a way to accomplish this without explicitly stating that I want an array of 52 cards per deck? I thought that was the purpose of dynamic memory allocation? I am probably off base, and have been operating on faulty assumptions though.
Last edited on
This is the code and output with no repeats btw:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <iostream> //for std::cout
#include <random> // for std::mt19937
#include <ctime> // for std::time
using namespace std;

struct card {
    int value;
    int index = 0;
    int sumTotal = 0;
    char suit;
    char ID;
};

mt19937 mt(time(nullptr));
void setDeck(card*);
char setSuit(int);
void distributeCard(card*, card*, int, int*, int&);
int repetitionCheck(int*, int&);
void printDeck(card*, int);

int main()
{
    int randIndex[52];
    int randMark = 0;
    card* gameDeck = new card[52];
    card* pDeck = new card[26];
    card* cDeck = new card[26];

    gameDeck->ID = 'G';
    pDeck->ID = 'P';
    cDeck->ID = 'C';
    
    setDeck(gameDeck);
    distributeCard(gameDeck, pDeck, 26, randIndex, randMark);
    distributeCard(gameDeck, cDeck, 26, randIndex, randMark);

    printDeck(gameDeck, 52);
    printDeck(pDeck, 26);
    printDeck(cDeck, 26);

    return 0;
}

void setDeck(card* aDeck)
{
    int tempIndex;
    for (int i = 0; i < 13; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            tempIndex = aDeck->index;
            aDeck[tempIndex].value = i + 1;
            aDeck[tempIndex].suit = setSuit(j);
            aDeck->index++;
        }
    }
}

char setSuit(int aNum)
{
    switch (aNum)
    {
    case 0: return 'S';
        break;
    case 1: return 'C';
        break;
    case 2: return 'D';
        break;
    case 3: return 'H';
        break;
    default: return '?';
        break;
    }
}

void distributeCard(card* gDeck, card* aDeck, int max, int* randIndex, int& randMark)
{
    for (int i = 0; i < max; i++)
    {
        randIndex[randMark] = repetitionCheck(randIndex, randMark);
        aDeck[aDeck->index].value = gDeck[randIndex[randMark]].value;
        aDeck[aDeck->index++].suit = gDeck[randIndex[randMark++]].suit;
    }
}

int repetitionCheck(int* randIndex, int& randMark)
{
    int aNum = mt() % 52;
    for (int i = 0; i < randMark; )
    {
        if (aNum == randIndex[i])
        {
            aNum = mt() % 52;
            i = 0;
        }

        else
            i++;
    }

    return aNum;
}

void printDeck(card* deck, int cnt)
{
    switch (deck->ID)
    {
    case 'C': cout << "\n\n\nCPU Hand";
        break;
    case 'P': cout << "\n\n\nPlayer Hand";
        break;
    case 'G': cout << "\n\n\Game Deck";
        break;
    default: break;
    }

    for (int i = 0; i < cnt; i++)
    {
        if (i % 13 == 0)
        {
            cout << "\n\n";
        }

        cout << "|'"<< deck[i].value << deck[i].suit << "|\t";
    }
}





Game Deck

|1S|    |1C|    |1D|    |1H|    |2S|    |2C|    |2D|    |2H|    |3S|    |3C|    |3D|    |3H|    |4S|

|4C|    |4D|    |4H|    |5S|    |5C|    |5D|    |5H|    |6S|    |6C|    |6D|    |6H|    |7S|    |7C|

|7D|    |7H|    |8S|    |8C|    |8D|    |8H|    |9S|    |9C|    |9D|    |9H|    |10S|   |10C|   |10D|

|10H|   |11S|   |11C|   |11D|   |11H|   |12S|   |12C|   |12D|   |12H|   |13S|   |13C|   |13D|   |13H|


Player Hand

|13H|   |2S|    |1C|    |3C|    |10H|   |8H|    |2D|    |5H|    |12S|   |9C|    |1D|    |12C|   |13S|

|13C|   |7S|    |2C|    |9S|    |10D|   |3H|    |10C|   |13D|   |7D|    |5C|    |3D|    |12D|   |9H|


CPU Hand

|4H|    |3S|    |11C|   |8S|    |4C|    |4S|    |11H|   |8D|    |5S|    |6S|    |8C|    |7H|    |1H|

|5D|    |1S|    |2H|    |6C|    |9D|    |11D|   |4D|    |6H|    |10S|   |11S|   |12H|   |7C|    |6D|
Last edited on
the point of dynamic memory is that
1) the size can be determined at run-time
2) you can move large items off the stack, which is relatively small
3) it can be resized, but this is expensive as it consists of making new memory at the new size then copying the old stuff into that before giving the old memory back.
you still have to know, at some level in your program, how much space you want. Dynamic memory does not change that at all, it just gives you the tools to change that size if you must, or to control it at run-time.

vectors, as I showed earlier, can resize for you, but it still suffers #3 (it can't be avoided, but it can be minimized so you do it as infrequently as possible).

you don't have to hard code 52, you can ask the user or get it from a file etc.

only c-arrays have to be hard coded compile time constants.
Last edited on
Yes, but what causes the output to be about 80% correct despite this fact. shouldn't it be incapable of running at all? and the modified code you game me has the precise same issues.
I'll omit all the couts that are not related to the loops, and see if that causes anything to occur.

As I said above, you are at the mercy of the heap as to whether you get an illegal address reference, some other error, or simply write to or read from random heap memory. Even if no other run-time routines use the heap, a heap manager still has certain overhead that it uses to keep track of free and allocated blocks of memory. When you write to memory outside that which you were given, there is a possibility that you're overwriting the heap managers overhead, thereby corrupting the heap.

Not entirely sure what you mean by this. I don't know what a heap check is. If you can point me at some literature that can be of help, I'd appreciate it.

"heap_check" is a somewhat generic term for a run time function that checks the integrity of the heap. A heap_check function is not dictated by the C++ standard, so implementations are free to call it what they want if they even implement it at all. I don't know what compiler you're using, so I can't point you to specific information on heap_check in your environment. Here's an article on a heap checker for the Visual Studio environment.
https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/heapchk?view=msvc-160

As jonnin explained, when you use new you can allocate a variable length array.
Here's an example:
1
2
3
4
5
	int numcards;

	std::cout << "How many cards in your deck? ";
	std::cin >> numcards;
	card* deck = new card[numcards];



Last edited on
jonnin
the size can be determined at run-time

so you're saying the express purpose of dynamic mem alloc is so you can make the size a variable. that makes sense, you can't put an empty variable as a size with a normal array I guess. essentially, the user can dictate the size with my method, but it simply must be done prior to writing card* pDeck = new [_] essentially?

you can move large items off the stack, which is relatively small

this sounds interesting. any literature you can point me to regarding how to do such a thing, as well as best practices on when to do so?

you still have to know, at some level in your program, how much space you want. Dynamic memory does not change that at all, it just gives you the tools to change that size if you must, or to control it at run-time.

so what I was attempting was essentially wrong headed on my behalf then. was my approach just flawed from the start, and bornt from ignorance then?
Last edited on
1) no, dynamic memory has many purposes. ONE of those is to have run-time sized data. Its not the 'main' reason.

2) er, you can google it but using "new" moves it off the stack, its that simple. Stack size varies by OS, compiler settings, etc, but generally anything more than a few MB needs to move off; the numbers change as computer capacity changes, or whether its a phone or a PC or a server or whatever system. maybe something like https://medium.com/@ghl234/memory-management-heap-vs-stack-memory-4064ad30c20f. At some point the program will not run if you exceed the stack, and it tells you so. Compilers also tend to warn you if you are using what it 'thinks' is 'a lot' ... visual studio does word-like squiggle underline and fusses about big stack using functions.

3) yes, you thought that dynamic memory means it changes as you need it. The truth is, that you the coder have to change the size as you need it or use something that does so for you. C++ (and many other high level languages) offers pre-made containers that do what you are talking about, but they HIDE the details is all; it still does it the way I said, it just does it for you and hides the gory details. Some types of containers get memory on demand, like a linked list, graph, or tree, if coded that way. This means asking the OS for memory for every item. This gets you into talking optimization and tradeoffs... data structures that grow this way are not efficient at some tasks, but are very good at other task.
A short example... a classic (get memory as it needs it) linked list can be inefficient at finding and item (its pretty much iteration, and the memory is not connected, so its memory hopping iteration, yuck). But it is every efficient at adding one item. A vector, on the other hand, is very efficient at accessing things (you can get to any item in a single jump, eg vec[23] goes right there) but terrible at resizing to add one more past its last capacity. You have to decide what your current program will do the most of and compromise around what your program will be doing most of the time. This is not even c++, its data structures and theory and algorithms.

there are reasons for 3). the operating system has to track and manage the memory used by all processes, so you must go through the OS to get memory. It can't give you a free for all 'take what you want' -- it has to tell you if it has it to give, and to assign you memory not used by other programs, and so on. You can do your own management (ask for huge amounts of memory and shuffle it around your program on demand internally) and many large programs do this.

I highly advise you to focus your early studies on using the containers c++ provides, and circle back to doing your own memory stuff much later in your studies. You will write literally thousands of arrays, vectors, lists, etc (c++ containers) for every new/delete construct you need to build.
Last edited on
@OP - Sorry I was editing my post to add the example while you posted your last message. The example I posted should make clear how to allocate a variable size array.

Just to beat a dead horse, I added the following cout statements to your original program:
1
2
3
4
5
6
7
8
    card* gameDeck = new card;
    card* pDeck = new card;
    card* cDeck = new card;

    cout << "Address of gameDeck[0]=" << &gameDeck[0] << endl;
    cout << "Address of gameDeck[51]=" << &gameDeck[51] << endl;
    cout << "Address of pdeck[0]=" << &pDeck[0] << endl;
    cout << "Address of pdeck[25]=" << &pDeck[25] << endl;

1
2
3
4
Address of gameDeck[0]=0000024910B7FBA0
Address of gameDeck[51]=0000024910B7FED0
Address of pdeck[0]=0000024910B7FC90
Address of pdeck[25]=0000024910B7FE20

As you can see pdeck overlaps gameDeck. i.e. 0000024910B7FC90 is between gameDeck[0] and 0000024910B7FED0. So guaranteed, when you write to pdeck, you're overwriting part of gameDeck. In addition, you're corrupting the heap by overwriting boundary tags.
Last edited on
AbstrationAnon
Thank you, this clears things up quite a bit! do you know if there is a way to keep my implementation wherein I don't specify a size, and not have the struct pointers overlap one another?

jonnin

1) no, dynamic memory has many purposes. ONE of those is to have run-time sized data. Its not the 'main' reason.

ah, sorry for my ignorance, I should not have phrased that so generally.


maybe something like https://medium.com/@ghl234/memory-management-heap-vs-stack-memory-4064ad30c20f.

I very much appreciate any and all external literature you have offered. I am thoroughly going through the previous link you have given me, and will be checking this article out as well. many thanks.

yes, you thought that dynamic memory means it changes as you need it

I did not mean to give that impression. I simply thought that dynamic mem allocation allowed me to keep programs more 'abstract', without the need to have literals in the program. I guess I did have the impression that the array could be expanded, like vectors allow (at least to my understanding); so maybe this is the correct framing of my thought process after all, lol.

I highly advise you to focus your early studies on using the containers c++ provides, and circle back to doing your own memory stuff much later in your studies. You will write literally thousands of arrays, vectors, lists, etc (c++ containers) for every new/delete construct you need to build.

fair enough. I'm kinda flying blind with very little guidance in the way of an education, so I'm not sure what sort of 'progression' I should be going by. I appreciate the input though, and will follow it to the best of my understanding, it's much appreciated.
Last edited on
Pages: 12