First (Proper) Program

Started by
7 comments, last by Splinter of Chaos 15 years, 4 months ago
Hello , Im very new to programming with c++ im getting there though - sorta lol. Im making a simple Blackjack type game which at this moment runs in a console app - will be updated once i get more experience in GUI's i have encountered two problems i hope you can help with: Firstly on creating my Array for my deck of cards i have: int Deck [51] = {1,2,3,4,5,6,7,8,9,10,10,10,10,1,2,3,4,5,6,7,8,9,10,10,10,10,1,2,3,4,5,6,7,8,9,10,10,10,10,1,2,3,4,5,6,7,8,9,10,10,10,10}; but that gives a to many initializers error , cant see where the extra number is lol. Secondly to name the integers im using this : (very unsure if this is the best way) (the arrays in a global variable header) int main(int argc, char *argv[]) { srand(time(NULL)); PlayerR = rand()%51+0; cout << PlayerR << endl; cout << Deck[PlayerR] << endl; Name_Cards(); system ("PAUSE"); return 0; } //NAMING FUNCTION FOR THE INTEGER VALUES int Name_Cards() { if (PlayerR == (0) || (PlayerR == (13) || (PlayerR == (26) || (PlayerR == (39))))); { cout << "You have An Ace\n"; } if (PlayerR == (1) || (PlayerR == (14) || (PlayerR == (27) || (PlayerR == (40))))); { cout << "You have A Two\n"; } if (PlayerR == (2) || (PlayerR == (15) || (PlayerR == (28) || (PlayerR == (41))))); { cout << "You have A Three\n"; } if (PlayerR == (3) || (PlayerR == (16) || (PlayerR == (29) || (PlayerR == (42))))); { cout << "You have A Four\n"; } if (PlayerR == (4) || (PlayerR == (17) || (PlayerR == (30) || (PlayerR == (43))))); { cout << "You have A Five\n"; } if (PlayerR == (5) || (PlayerR == (18) || (PlayerR == (32) || (PlayerR == (44))))); { cout << "You have A Six\n"; } if (PlayerR == (6) || (PlayerR == (19) || (PlayerR == (32) || (PlayerR == (45))))); { cout << "You have A Seven\n"; } if (PlayerR == (7) || (PlayerR == (20) || (PlayerR == (33) || (PlayerR == (46))))); { cout << "You have A Eight\n"; } if (PlayerR == (8) || (PlayerR == (21) || (PlayerR == (34) || (PlayerR == (47))))); { cout << "You have A Nine\n"; } if (PlayerR == (9) || (PlayerR == (22) || (PlayerR == (35) || (PlayerR == (48))))); { cout << "You have A Ten\n"; } if (PlayerR == (10) || (PlayerR == (23) || (PlayerR == (36) || (PlayerR == (49))))); { cout << "You have A Jack\n"; } if (PlayerR == (11) || (PlayerR == (24) || (PlayerR == (37) || (PlayerR == (50))))); { cout << "You have A Queen\n"; } if (PlayerR == (12) || (PlayerR == (25) || (PlayerR == (38) || (PlayerR == (51))))); { cout << "You have A King\n"; } return 0; } Thanks in advance for help :)
Advertisement
Hey,

A deck of cards has 52 cards in it...which i'm sure your aware of as you have 52 entries in the deck array. You have set the size of the deck array to 51 though. This means there are 51 available slots in the array. This is where the problem is. You just need to set the array size to 52.

Regards,
Darren
As to the second part of the question, this is where two things help:

1) the modulo operator (%) which helps you reduce the number of or'ed comparisons (0 modulo 13 == 13 modulo 13 == 26 modulo 13 etc)

2) arrays which let you get rid of the entire if...else tree

#include <iostream>#include <ctime>#include <cstdlib>void name_card(int card);int main(){    srand(time(NULL));    //unused so far    //int deck[52] = {1,2,3,4,5,6,7,8,9,10,10,10,10,1,2,3,4,5,6,7,8,9,10,10,10,10,    //                1,2,3,4,5,6,7,8,9,10,10,10,10,1,2,3,4,5,6,7,8,9,10,10,10,10};    int player_card = rand() % 52;    name_card(player_card);}void name_card(int card){    static const char* face_names[13] = { "Ace", "Two", "Three", "Four", "Five", "Seven",                                          "Eight", "Nine", "Ten", "Jack", "Queen", "King" };    std::cout << "You have a " << face_names[card % 13] << '\n';}
An array of size 51 can hold only 51 elements. There are 52 cards in a set.

As for naming cards, there is a different way to do it:
#include <vector>#include <string>enum Suit{    DIAMOND,    CLUB,    SPADE,    HEART};enum Rank{    ACE,    TWO,    THREE,    FOUR,    FIVE,    SIX,    SEVEN,    EIGHT,    NINE,    TEN,    JACK,    QUEEN,    KING};struct Card{    Suit suit;    Rank rank;        Card(Suit s, Rank r) : suit(s), rank(r) {}  };std::ostream &operator<<(std::ostream &out, const Card &c){    static const std::string suits[] =     {        "Diamond",        "Club",        "Spade",        "Heart"    };    static const std::string ranks[] =    {        "Ace",        "Two",        "Three",        "Four",        "Five",        "Six",        "Seven",        "Eight",        "Nine",        "Ten",        "Jack",        "Queen",        "King"    };    out << ranks[c.rank] << " of " << suits[c.suit];    return out;}typedef std::vector<Card> Deck;Deck makeDeck(){   Deck deck;   for(int suit = 0 ; suit < 4 ; ++suit)   {       for(int rank = 0 ; rank < 13 ; ++rank)       {            deck.push_back(Card(Suit(suit),Rank(rank)));       }   }   return deck;}int main(){    Deck deck = makeDeck();    Card card = deck[rand() % deck.size()];    std::cout << "You have a " << card << '\n';}

If you think that is a lot to digest, I agree [smile].

Why is this way "better" than yours?

- In most card games, cards are dealt from a central deck to the players. Therefore we need a way of dealing with decks of various sizes. This is why I use std::vector, it allows us to create a dynamic array, whose exact size can be determined at runtime.

- We can have multiple Decks. For example, we can have the dealers deck, and a mini-deck for each player. As the dealer deals out cards, we simply take a card from the main deck and insert it to the players deck.

We no longer have a global. This is a good thing, but it is hard to explain why to someone not experienced in programming.

- The cards are represented as instances of a Card type. This allows us to use the C++ type system to its full. While a card can be encoded in a single integer, this gives a lot of scope for errors the compiler cannot catch.

An example of where we use this above is in the operator<<() function. If you are quite new I wouldn't expect you to be able to come up with that, don't worry. Put simply, it tells your compiler how to handle you feeding a card to std::cout (or some other standard stream object). I created two internal arrays containing the text representing each card. The function looks up the values in the array using the enumeration values as indices.


Finally, if I am writing an if statement with a lot of logical ors, I would write:
if( (value1 == 0) || (value2 == 1) || (value3 == 3)) {   // ...}

Notice two things:

1 - the placement of the parentheses

You didn't need the parentheses around the integer constants. Note also how I have 1 pair of parentheses in each or block. In your code, the parentheses all had to be closed at the end. That is a style issues, and a minor one. Technically, I didn't even need the inner parentheses. But it helps to be explicit, especially when you are doing more complex conditionals. In particular, the bitwise operators have a low priority, so one has to be careful when using them with ==.

2 - no semicolon after the closing parentheses. This is a problem in your code.

C++ allows us to write a semicolon almost anywhere, as a kind of "empty" statement. It also allows us to place a scope (a pair of curly braces) anywhere inside a function. These can come in handy.

Your code was equivalent to this:
if( /* conditions */ ){   ; // do nothing}// note: this next bit will be executed unconditionally{   cout << "You have an Ace!\n";}

Quote:Original post by Catkill
Firstly on creating my Array for my deck of cards i have:
int Deck [51] = {1,2,3,4,5,6,7,8,9,10,10,10,10,1,2,3,4,5,6,7,8,9,10,10,10,10,1,2,3,4,5,6,7,8,9,10,10,10,10,1,2,3,4,5,6,7,8,9,10,10,10,10};

but that gives a to many initializers error , cant see where the extra number is lol.
A deck has 52 cards, not 51. Only indexing is zero-based, the 1st card is at Deck[0] and the 52th card is at Deck[51], but you must still allocate space for 52 elements to begin with. You can just omit the size of the array and just have empty square brackets, the compiler will work out the necessary size.

Quote:Secondly to name the integers im using this : (very unsure if this is the best way)
Never duplicate code, at the moment you're writing the same thing out 13 times with only slight difference, you should refactor out those differences in some way:

(untested)
const char * cardname(int deck_index){    const unsigned int cards_size = 13;    const char * cards[] = { "Ace", "Two", "Three", "Four", "Five", "Six", "Seven",                              "Eight", "Nine", "Ten", "Jack", "Queen", "King" };    return cards[ deck_index % cards_size ];}bool vowel(char c){    std::string v("AEIOUaeiou");    return v.find(c) != std::string::npos;}void Name_Cards(){    const char * card = cardname(PlayerR);    std::cout << "You have A";    if (vowel(card[0])) std::cout << "n";    std::cout << " " << card;    // no need to return 0}


Thanks everyone for all your help - always nice when your learning to have a friendly comunity :)
Quote:Original post by dmatter
const char * cardname(int deck_index){    const unsigned int cards_size = 13;    const char * cards[] = { "Ace", "Two", "Three", "Four", "Five", "Six", "Seven",                              "Eight", "Nine", "Ten", "Jack", "Queen", "King" };    return cards[ deck_index % cards_size ];}



Isn't this returning a pointer to a local variable?
Quote:Original post by Gage64
Quote:Original post by dmatter
const char * cardname(int deck_index){    const unsigned int cards_size = 13;    const char * cards[] = { "Ace", "Two", "Three", "Four", "Five", "Six", "Seven",                              "Eight", "Nine", "Ten", "Jack", "Queen", "King" };    return cards[ deck_index % cards_size ];}



Isn't this returning a pointer to a local variable?


String literals aren't local, pointers to them never go out of scope.

It would be a problem if it were something like this:
const char *foo(){   const char str[] = "foo";   return str;}

In this case, the string literal foo initialises a local char array. A pointer to this array is returned, which is a problem.
Quote:Original post by rip-off
We no longer have a global. This is a good thing, but it is hard to explain why to someone not experienced in programming.


I'll try to explain this:
Global variables are more confusing for people reading your code, are disorganized, and can be difficult to debug.

They're confusing because you might have
    void f() { globalVar++ }
and anyone reading this is going to have to search for that. The first place they'll is in the function! The worst scenario is when you have defined it at the top of the file, very far from the function.

They are disorganized for the same reason. When altering a function, if you have to change the global it uses, you have to find the function, change whatever will no longer work, then find and change the variable.

They are difficult to debug because if you have five functions that alter it, and it turns out one of them changed the value in a bad way, you don't know which of the five did it!

By declaring the variable in the function as static, you are practically making a global, except this is the only function that knows about it.
    int glob = 0;    void f() { glob++ }
works just like
    void f()     {         static int glob = 0;        glob++;    }

If the variable is required by several functions, simply passing it back and forth will solve the problem.

This topic is closed to new replies.

Advertisement