C++ Draw Card function

I am reading Brain Overland book and my brain is having a seizure trying to understand the logic of the following function "select_next_available"

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
#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <math.h>

using namespace std;
int rand_0toN1(int n);
void draw_a_card();
int select_next_available(int n);

char *ranks[13]{ "ace","two","three","four","five","six","seven","eight","nine","ten","jack","queen","King", };
char *suits[4]{ "clubs","diamonds","hearts","spades", };

int card_drawn[52];
int cards_remaining = 52;
int main()
{
	int n, i;
	srand(time(NULL));
	while (1)
	{
		cout << "Enter a number (0 to break free): ";
		cin >> n;
		if (n == 0)
		{
			break;
		}
		for (i = 0; i < n; i++)
		{
			draw_a_card();
			
		}
		system("pause");
		system("cls");
	}
	return 0;
}

void draw_a_card()
{
	int r;
	int s;
	int n, card;
	n = rand_0toN1(cards_remaining--);
	card = select_next_available(n);
	r = card % 13;
	s= card / 13;
	cout << ranks[r] << " " << suits[s] << endl;
}

int select_next_available(int n)
{
	int i = 0;
	while (card_drawn[i])
		i++;

	while (n-- > 0)
	{
		i++;
		while (card_drawn[i])
			i++;
	}
	card_drawn[i] = true;
	return i;
}

int rand_0toN1(int n)
{
	return rand() % n;
}


Pretty please, explain to me, line by line the logic of that function. For hours I am staring at it and cannot wrap my head around it.
Last edited on
its using global variables, so I would think that studying this book or code is going to do more harm than good for your learning process. The headers it includes are incorrect. The constant tables are not marked const. C-strings instead of c++ strings. C random instead of c++. Is this book perhaps from 1995 era?

Its also convoluted as all get out.

what its doing is meandering through a global array that marks whether a card has already been seen, to find one that has not yet been seen. It then marks that one as seen and returns the index in the global array presumably so the next function can 'draw' that card.

I am with you, I can't get my head around what in the world the programmer was smoking when he tied the N variable to this logic. The N logic is potentially skipping the next available card and picking one somewhere past that, but why, I cannot begin to understand, and I really don't want to. It looks like some kids homework.

Last edited on
No problem. Sorry that I came with this sort of trouble :)

I understand that if you initialize a global matrix, all the values from it will be zero, meaning false , right?

But for the love of Gates I do not understand how that function work, haha. For example:

1
2
3
int i = 0;
	while (card_drawn[i])
		i++;


That piece of line should call the matrix right?
card_drawn[0] then jump to card_drawn[1] then jump to card_drawn[2] ... etc , right?

Anyway, the book is from 2005. The strangest thing is the program work flawlessly.
I am trying to make exercises so that I can cement the knowledge.

Also, what books/tools/sites do you recommend?
that line is looking for the first entry where card_drawn has a 0 (false) value.
check location 0, is it true or false? If its true, increment I and loop again. Until its false. Which WILL go out of bounds if all the cards are drawn (true) so another issue with the code. In c++ 0 is false, anything else is true, and this is at the bitwise level so you can use oddball Boolean logic on lots of non Boolean expressions. Its the same as saying while (card_drawn[I] != 0), just a shorthand way of writing that expression.

Poorly written code can work flawlessly. But you don't want to emulate what this book is doing.

Yes globals are zero initialized.

This site, and stack overflow is good.
Books... I am cheap and get by off the web, but if you search this forum for recent posts on book recommendations, you will find many.

Last edited on
OH YEAH!!! because while function is using card_drawn[i] as a conditional operator and as long as it is false it will never repeat itself! THANK YOU! It makes so much more sense!
For other people who encounter this problem, I will try to explain as simple as I can:
1. You are declaring a global matrix at Line14, and because it is global the program will initiate all of its index with 0 , which mean false too.
2. the program will obtain a random number Line 44 which will decrement with every repetition that it does. In the first repetition the number can't be grater than 51 ( it's a modulo % symbol of 52 )
3. the random number generated will be used in a function Line 51 to to return true an element from a matrix (card_drawn[i]) and to return the index number from that matrix (card_drawn[i]) which will be used to sort the card

Ok, let say that the first number generated is 17:
- The while functions from Line 54 , 60 will do nothing because their condition is false ( remember global matrix = false indexing )
- only the while from Line 57 will have a condition true , that repeats itself 17 times. It means that at the end of the Line 62 "i" will be 17 and the function will make card_drawn[17] = true; and return i (17) to be used to show the card drawn.
- LET'S say that at the second repetition of generating a random number, it will be the highest possible generated number, which after decrementation is 50!
- still , the while from Line 54 will do nothing since it's still false. The while from Line 57 will start to repeat 50 times! NOW BE CAREFUL! when while from Line 57 will arrive at his 17th repetition it will trigger the while from Line 60 ! ( because in the previous int select_next_available loop you returned the card_drawn[17]=true ) It means that at the end of Line 62 , i equals 51 .
...to program will do all of this in total of 51 times!

As jonnin said, there is still a flaw because if you enter more than 52 draws, the programs gives you an error. You can fix that by adding after Line 43 , this code:

1
2
3
4
5
6
7
8
9
if ( cards_remaining==0)
{
   cout<<"Reshuffling!";
   cards_remaining=52;
   for (int i=0 ; i<52 ; i++)
   {
      card_drawn[i]=0;
   }
}


If you have any additional questions. I will be happy yo explains to you :)
BTW, this is how it should be done:

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
#include <algorithm>
#include <chrono>
#include <iostream>
#include <numeric>
#include <random>
#include <vector>

// A card is a number mapped to suit and rank
//   suit = value / 13
//   rank = value % 13
typedef unsigned char Card;


// Here is a bunch of convenient stuff to turn a card into a string
const char* Suit_Name( Card card )
{
  const char* ns[] = { "Spades", "Diamonds", "Clubs", "Hearts" };
  return ns[ std::min( card, (Card)52 ) / 13 ];
}

const char* Rank_Name( Card card )
{
  const char* ns[] = { "Ace", "One", "Two", "Three", "Four", "Five", "Six", 
                       "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King" };
  return ns[ std::min( card, (Card)52 ) % 13 ];
}

// ... and print one to a stream
std::ostream& operator << ( std::ostream& outs, Card card )
{
  return outs << Rank_Name( card ) << " of " << Suit_Name( card );
}


int main()
{
  // Our list of cards
  std::vector <Card> cards;
  
  // Add a full deck to our list
  cards.resize( 52 );
  std::iota( cards.begin(), cards.end(), 0 );

  // Randomize the list
  std::shuffle( cards.begin(), cards.end(), 
    std::default_random_engine( std::chrono::system_clock::now().time_since_epoch().count() ) );

  // (Heh, changed our mind: only want SEVEN random cards)
  cards.resize( 7 );

  // After that, simply grab cards until there are none left:
  while (!cards.empty())
  {
    std::cout << cards.back() << "\n";
    cards.pop_back();
  }
}
Ten of Clubs
Seven of Spades
Ace of Hearts
Two of Clubs
Jack of Clubs
Four of Diamonds
Seven of Clubs

Enjoy!
I don't want to be mean but for people like me who started learning programing from zero, taking baby steps, the code that you wrote doesn't make any sense (yet)...

Vectors? I am barely at matrix...

You are using std:: when every other tutorials out there (even on this site) use at the beginning the line:

using namespace std; - to shortcut the naming and help noobs ( like me ) to get comfortable with the code.

It took me a couple of days to understand the logic of the code that I initially wrote and now you pasted this and I have no idea what it means ...operator? vectors? ....

I have a question though for you, regarding the following code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void functie(int n)
{
	int i, j, low;
	for (i = 0; i < n - 1; i++)
	{
		low = i;
		for (j = i + 1; j < n; j++)
  		if (matrice[j] < matrice[low])
					low = j;

		if (i != low)
			schimbare(&matrice[i], &matrice[low]);
	}
}


...why is the last if ( the one with i != low ) ignored until the "j for" is complete? Shouldn't be nested? Maybe you can help me understand and cement my knowledge ? :)

EDIT: Pretty please do not take this as a sign of disrespect. I am really thankful for all the helping that I can get. Kudos!
Last edited on
modern c++ is very strange at first. I have programmed for decades and its like a totally new language at times.

that said.. using namespace std has been shown to be a risk in large software projects, so the community at large has gone from recommending it (for over 10 years) to not recommending it (as you just saw). So while its handy, its considered 'bad practice' now. You can google up on a big bunch of reasons or just trust us on this for now. The reasons alone are pretty frustrating reading for a new coder, though.

vector is an array replacement. That is really all you need to know immediately.
eg
int x[10]; is almost the same as
vector <int> x(10);

and both can be accessed by x[index]. the vector x has a .size() to tell you its 10 long, and many other features that arrays lack.


as far as operators go, you know them :)
4+5 , '+' is an 'operator'. you can overload operators in your own custom types (struct and class are user defined types), so you can make your own type and add them together if you overload the + operator. String does this, and + appends one string to another (concatenate). He overloaded the operator you need so you can use cout on your type in a nice clean way.

I am not sure what the code is supposed to do, so I can't comment on why the logic is as you posted. What it does is check to see if low changed in j loop, and if it did, it shimbares them. Whatever that does.

Heh, modern C++, if written correctly, looks strange because it is clear and concise. Almost everything is What Is Written On The Can. The parts that most trip noobs up is idiomatic stuff, like that stream insertion operator to print a card. Even so, just looking at the (one line) body should give a pretty decent hint about its function.

Here is the same thing I posted before, but in C. You will notice that, except for having to implement a random number generator and a shuffle algorithm, it is almost identical in structure to the C++ stuff.

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
#include <stdio.h>
#include <stdlib.h>
#include <time.h>


// A card is a number mapped to suit and rank
//   suit = value / 13
//   rank = value % 13
typedef unsigned char Card;


// C++ defined this for us: in C we have to define it ourselves
int min( int a, int b ) { return (a < b) ? a : b; }


// Here is a bunch of convenient stuff to turn a card into a string
const char* Suit_Name( Card card )
{
  const char* ns[] = { "Spades", "Diamonds", "Clubs", "Hearts" };
  return ns[ min( card, (Card)52 ) / 13 ];
}

const char* Rank_Name( Card card )
{
  const char* ns[] = { "Ace", "One", "Two", "Three", "Four", "Five", "Six", 
                       "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King" };
  return ns[ min( card, (Card)52 ) % 13 ];
}


// ... and print one to a stream
FILE* print_card( FILE* f, Card card )
{
  fprintf( f, "%s of %s", Rank_Name( card ), Suit_Name( card ) );
  return f;
}


// Again, C++ provides handily for you when dealing with random 
// numbers and shuffling algorithms. In C we must roll our own every time.
// (This is the Knuth-Fisher-Yates shuffle algorithm for our cards)
void swap( Card* a, Card* b ) { Card c = *a;  *a = *b;  *b = c; }

int random_less_than( int n )
{
  int x, N = RAND_MAX / n * n;
  do x = rand(); while (x >= N);
  return x % n;
}

void shuffle( Card* cards, int n )
{
  if (n) while (--n) swap( cards + n, cards + random_less_than( n ) );
}


int main()
{
  // Our list of cards
  Card cards[ 52 ];
  unsigned ncards = 0;

  // Add a full deck to our list
  while (ncards < 52) cards[ncards] = ncards, ncards++;

  // Randomize the list
  srand( time( 0 ) );
  shuffle( cards, ncards );

  // (Heh, changed our mind: only want SEVEN random cards)
  ncards = 7;

  // After that, simply grab cards until there are none left:
  while (ncards)
  {
    fprintf( print_card( stdout, cards[--ncards] ), "\n" );
  }
}

Clearly-written C and C++ share some characteristics; the only real changes are idiomatic constructs.


It is worth your time to learn modern C++ and not spend time trying to learn something else as a supposed prerequisite. You don’t need to learn C or Java or anything else to learn good C++. Modern languages like to make expressiveness and syntax work together. /me end soapbox rant


I have a question though for you, regarding the following code
I actually know a few people from Romania, and from what they describe, higher education there is either hit or miss. You seem to be on the “miss” end of it right now. I feel very sorry you have to put up with the confusion it causes.

That function is poorly named and, admittedly, a little difficult to read, at least in my opinion. It is a selection sort. There are two loops:

    • the outer loop goes from element i=0 to (n-1)
      every element < i is sorted
      every element ≥ i is not sorted (or unsorted)

    • the inner loop searches all the unsorted elements for the smallest value

Once the smallest unsorted element is found, we swap it with whatever was at i (but only if the smallest is not already at i).

The element at i is now sorted, so we increment i (restoring the conditions listed above) and continue with the next iteration of the outer loop.


Pretty please do not take this as a sign of disrespect.
Heh, I know I have a fiery reputation, but you have done absolutely nothing to show disrespect. What you have demonstrated is an interest in learning and awareness of what is going on around you and ongoing explorations beyond the original topic of this thread. All those are good points. :O)

Hope this helps.

@jonnin
Use the Extended Mind.
Translate says that shimbare is Romanian for what we would call swap or exchange. ;-)

[edit] Fixed an embarrassing typo (I used “their” instead of “there” <shame>).
Last edited on
Topic archived. No new replies allowed.