Hi, I am currently working on a UNO Game using C++ and I am facing some troubles. I need to use Vectors and Pointers (for the main bulk for this assignment). I am wondering if anyone could kindly help me because the deadline is on Wednesday and I feel very screwed now. Thank you in advance.

The problem I am having now is:
1. Get the remaining cards (after distribution) and get the top card
2. Read the option from the hand and remove it accordingly to the option

I have not done the advanced features yet, such as skip/reverse/draw.

I have only done till here.

For my codes:
I have 3 header class, and 4 .cpp classes used for this assignment

**UnoDeck.txt **- to read in the card information based on the following attributes (value, type, score, and dealer score)

0 Blue 0 0
0 Red 0 0
0 Yellow 0 0
0 Green 0 0
1 Blue 1 1
1 Blue 1 1
1 Red 1 1
1 Red 1 1
1 Yellow 1 1
1 Yellow 1 1
1 Green 1 1
1 Green 1 1
2 Blue 2 2
2 Blue 2 2
2 Red 2 2
2 Red 2 2
2 Yellow 2 2
2 Yellow 2 2
2 Green 2 2
2 Green 2 2
3 Blue 3 3
3 Blue 3 3
3 Red 3 3
3 Red 3 3
3 Yellow 3 3
3 Yellow 3 3
3 Green 3 3
3 Green 3 3
4 Blue 4 4
4 Blue 4 4
4 Red 4 4
4 Red 4 4
4 Yellow 4 4
4 Yellow 4 4
4 Green 4 4
4 Green 4 4
5 Blue 5 5
5 Blue 5 5
5 Red 5 5
5 Red 5 5
5 Yellow 5 5
5 Yellow 5 5
5 Green 5 5
5 Green 5 5
6 Blue 6 6
6 Blue 6 6
6 Red 6 6
6 Red 6 6
6 Yellow 6 6
6 Yellow 6 6
6 Green 6 6
6 Green 6 6
7 Blue 7 7
7 Blue 7 7
7 Red 7 7
7 Red 7 7 7
7 Yellow 7 7
7 Yellow 7 7
7 Green 7 7
7 Green 7 7
8 Blue 8 8
8 Blue 8 8
8 Red 8 8
8 Red 8 8
8 Yellow 8 8
8 Yellow 8 8
8 Green 8 8
8 Green 8 8
9 Blue 9 9
9 Blue 9 9
9 Red 9 9
9 Red 9 9
9 Yellow 9 9
9 Yellow 9 9
9 Green 9 9
9 Green 9 9
DrawTwo Blue 20 0
DrawTwo Blue 20 0
DrawTwo Red 20 0
DrawTwo Red 20 0
DrawTwo Yellow 20 0
DrawTwo Yellow 20 0
DrawTwo Green 20 0
DrawTwo Green 20 0
Reverse Blue 20 0
Reverse Blue 20 0
Reverse Red 20 0
Reverse Red 20 0
Reverse Yellow 20 0
Reverse Yellow 20 0
Reverse Green 20 0
Reverse Green 20 0
Skip Blue 20 0
Skip Blue 20 0
Skip Red 20 0
Skip Red 20 0
Skip Yellow 20 0
Skip Yellow 20 0
Skip Green 20 0
Skip Green 20 0
WildCard Wild 50 0
WildCard Wild 50 0
WildCard Wild 50 0
WildCard Wild 50 0
WildDrawFour Wild 50 0
WildDrawFour Wild 50 0
WildDrawFour Wild 50 0
WildDrawFour Wild 50 0

Card.h

#ifndef Card_h
#define Card_h
#include <string>
#include <ctime>
#include <sstream>
#include <vector>

using namespace std;

class Card {

private:
    string m_value;
    string m_type;
    int m_score;
    int m_dealerScore;
    bool m_inHand;

public:
    Card(string value, string type, int score, int dealerScore);
    ~Card();
    string getValue();
    string getType();
    int getScore();
    int getDealerScore();
    void setValue(string username);
    void setType(string password);
    void setScore(int score);
    void setDealerScore(int dealerScore);
    void setInHand(bool isInHand);
    bool getInHand();
};

#endif Card_h

Card.cpp

#include "Card.h"
#include <iostream>    // using IO functions
#include <string>      // using string

using namespace std;

Card::Card(string value, string type, int score, int dealerScore) {
    m_value = value;
    m_type = type;
    m_score = score;
    m_dealerScore = dealerScore;
    m_inHand = false;
}

string Card::getValue() {   // Member function (Getter)
    return m_value;
}

string Card::getType() {   // Member function (Getter)
    return m_type;
}

int Card::getScore(){
    return m_score;
}

int Card::getDealerScore(){
    return m_dealerScore;
}

void Card::setValue(string value){
    m_value = value;
}

void Card::setType(string type){
    m_type = type;
}

void Card::setScore(int score){
    m_score = score;
}

void Card::setDealerScore(int dealerScore){
    m_dealerScore = dealerScore;
};

void Card::setInHand(bool isInHand){
    m_inHand = isInHand;
}

bool Card::getInHand(){
    return m_inHand;
}

// need to end the class declaration with a semi-colon

Game.h

#ifndef Game_h
#define Game_h

#include "Player.h"
#include "Card.h"
#include <iostream>

using namespace std;

class Game{

private:
    vector<Player*> m_gamePlayers;
    vector<Card*> m_discardPile;
    vector<Card*> m_drawPile;

public:
    Game();
    ~Game();
    void addCardFromDrawPile(Card* card);
    void discardCardToDiscardPile(Card* card);
    void shuffleCards();
    Player* getPlayer(int playerIndex);
    void displayPlayerHand();
    void displayTopCardInDiscardPile();
    void displayTopCardInDrawPile();
    void processLoadCards();
    void setupPlayers();
    void processMenu();
    void processGamePlay();
};

#endif Game_h

Game.cpp

#include "Game.h"
#include "Player.h"
#include "Card.h"
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>  
#include <vector>

using namespace std;

void processLoadCards();
void processGamePlay();
void setupPlayers();
void processMenu();
string displayPlayerHand();

vector<Card*> cards;
vector<Card*> drawPile;
string value;
string type;
int score;
int dealerScore;
vector<Player*> players;

Game::Game(){

}

Game::~Game(){

}

void Game::shuffleCards(){

}

void Game::addCardFromDrawPile(Card* card){
    m_discardPile.push_back(card);
}

void Game::discardCardToDiscardPile(Card* card){
    m_drawPile.push_back(card);
}

Player* Game::getPlayer(int playerIndex){
    if (playerIndex >= 0 && playerIndex < m_gamePlayers.size()){
        return m_gamePlayers[playerIndex];
    }
    else{
        return nullptr;
    }
}

void Game::displayPlayerHand(){
    for (auto it = m_gamePlayers.begin(); it != m_gamePlayers.end(); ++it){
        Player* player = *it;
        //cout << player->displayPlayerHand() << endl; //deference the pointer address to grab the function
    }
}

void Game::displayTopCardInDiscardPile(){
    while (m_discardPile.back() != 0)
    {
        m_discardPile.push_back(m_discardPile.back() - 1);
    }
    cout << "Top Card in Discard Pile:";
    for (unsigned i = 0; i < m_discardPile.size(); i++){
        cout << ' ' << m_discardPile[0];
    }
}

void Game::displayTopCardInDrawPile(){
    while (m_drawPile.back() != 0)
    {
        m_drawPile.push_back(m_drawPile.back() - 1);
    }
    cout << "Top Card in Draw Pile:";
    for (unsigned i = 0; i < m_drawPile.size(); i++){
        cout << ' ' << m_drawPile[0];
    }
}

void Game::processLoadCards(){
    ifstream file("UnoDeck.txt");

    cout << "\n-- LET'S PLAY UNO --" << endl << "\n";

    if (file.is_open()){
        string line;

        while (getline(file, line)){

            file >> ws;

            stringstream ss;
            ss << line;

            Card* card = nullptr;

            ss >> value;
            ss >> type;
            ss >> score;
            ss >> dealerScore;

            srand(time(NULL));

            // Make the new card and put it in the cards vector
            card = new Card(value, type, score, dealerScore);
            cards.push_back(card);
        }
        // Scramble the vector
        random_shuffle(cards.begin(), cards.end());
    }
    setupPlayers();
}

void Game::setupPlayers() {
    int numPlayers = 4;
    Player* player1 = new Player("Mary", "password", 0);
    Player* player2 = new Player("John", "passwordForJohn", 0);
    Player* player3 = new Player("Ben", "ben", 0);
    Player* player4 = new Player("Junior", "junior", 0);

    players.push_back(player1);
    players.push_back(player2);
    players.push_back(player3);
    players.push_back(player4);

    int playerIndex = 0;

    for (int i = 1; i <= 7 * numPlayers; ++i){
        // If the number we look at is divisible by 7, then it means we just hit the first 7 items
        if (i < cards.size()) {
            // Put first element into the cards pile 
            players[playerIndex]->addCardFromDrawPile(cards[i - 1]);
            /* Say that the card is in a hand (so when you go to draw a new card from the pile, you'll interate through till you find a card
            that has getInHand() == false and then you set it to true because it gets put in the user's hand. When you want to put a card back
            in the pile, you would search for the card in the cards vector and then when you find that card go cards[i]->setInHand(false)
            because it is no longer in someone's hand */
            cards[i]->setInHand(true);
        }
        if (i % 7 == 0){
            // Move to putting cards in the hand of the next player
            playerIndex++;
        }

        Card* remainingCards = cards[cards.size() - (player1->m_playerHand.size() * 4)];
        drawPile.push_back(remainingCards);
    }

    string displayPlayer1Cards = player1->displayPlayerHand();
    string displayPlayer2Cards = player2->displayPlayerHand();
    string displayPlayer3Cards = player3->displayPlayerHand();
    string displayPlayer4Cards = player4->displayPlayerHand();

    for (int i = 0; i < 2; --i){
        cout << displayPlayer4Cards << endl;
        player4->playGame();

        cout << displayPlayer1Cards << endl;
        player1->computerPlayerGame();

        cout << displayPlayer2Cards << endl;
        player2->computerPlayerGame();

        cout << displayPlayer3Cards << endl;
        player3->computerPlayerGame();
    }
}


void Game::processMenu(){

    // Declare other variables
    int nMenuChoice = 0;

    cout << "--- Welcome to the UNO Game! ---\n\n";

    // Keep the program running until they choose to exit
    while (nMenuChoice != 4) {

        cout << "---Main Menu---\n";
        cout << "1. Start Game\n";
        cout << "2. Tutorial\n";
        cout << "3. View Leadership Board\n";
        cout << "4. Quit\n";
        cout << "\nPick a choice: ";
        cin >> nMenuChoice;

        // Switch based on user input
        switch (nMenuChoice) {

            // Case of starting the game play
        case 1:
            processLoadCards();
            break;

            // Case of UNO Tutorial
        case 2:
            cout << "--- HOW TO PLAY UNO ---" << endl;
            cout << "Rules:" << endl
                << "1. Play a card by entering the number on the left of the card." << endl
                << "2. A card can only be played if the colour or the title of the card is same " << endl
                << "   as the card on the pile." << endl
                << "3. Draw a card if you cannot play any card, or you want to have more cards just" << endl
                << "   for fun." << endl
                << "4. The last card in your hand can be a power card." << endl
                << "5. Draw Two and Draw Four will cause the next user to be skipped." << endl
                << "6. Wild card can be played at any time without restriction, but for Draw Four," << endl
                << "   the computer players might challenge you.  Draw Four can only be played when" << endl
                << "   you have no same colour/ title card in your hand.  If you are found guilty," << endl
                << "   you will draw 4 cards as punishment, but you still can choose your colour." << endl
                << "   If challenge failed, the challenger will draw 6 cards instead of 4." << endl
                << "7. A game will end, when one of the player had finished all the cards in his " << endl
                << "   hand, or when the cards had ran out of stock." << endl << endl;
            break;

            // Case of Displaying Leadership Board  
        case 3:
            //processLeadershipBoard();
            break;

            // Quit condition   
        case 4:
            break;

            // Invalid selection    
        default:
            cout << "Invalid Menu Choice!\n";
            break;
        }
    }
};

Player.h

#ifndef Player_h
#define Player_h
#include "Card.h"
#include <string>
#include <ctime>
#include <sstream>
#include <vector>

using namespace std;

class Player{

private:
    string m_username;
    string m_password;
    int m_score;
    vector<Card*> m_drawPile;
    void discardCardToDiscardPile(Card* playerCard);

public:
    Player(string username, string password, int score);
    ~Player();
    string getUsername();
    string getPassword();
    int getScore();
    void setUsername(string username);
    void setPassword(string password);
    void setScore(int score);
    string displayPlayerHand();
    void addCardFromDrawPile(Card* playerCard);
    void retrieveCardsFromDrawPile(Card* playerCard);
    void playGame();
    void computerPlayerGame();
    vector<Card*> m_playerHand;
};

#endif Player_h

Player.cpp

#include "Player.h"
#include "Card.h"
#include "Game.h"
#include <iostream>    // using IO functions
#include <string>      // using string
#include <algorithm>

using namespace std;

// Declare other variables
int nUserNum = 0;
int numToDelete = 0;

Player::Player(string username, string password, int score) {
    m_username = username;
    m_password = password;
    m_score = score;
}

string Player::getUsername() {   // Member function (Getter)
    return m_username;
}

string Player::getPassword() {   // Member function (Getter)
    return m_password;
}

int Player::getScore(){
    return m_score;
}

void Player::setUsername(string username){
    m_username = username;
}

void Player::setPassword(string password){
    m_password = password;
}

void Player::setScore(int score){
    m_score = score;
}

void Player::addCardFromDrawPile(Card* card){
    m_playerHand.push_back(card);
}


void Player::retrieveCardsFromDrawPile(Card* card){
    m_drawPile.push_back(card);
}

void Player::discardCardToDiscardPile(Card* card){
    vector<Card*>::iterator found = find(m_playerHand.begin(), m_playerHand.end(), card);
    if (found != m_playerHand.end()){
        m_playerHand.erase(found);
    }
}

string Player::displayPlayerHand(){
    stringstream sPlayerHand;
    sPlayerHand << "Player's " << m_username << endl;

    for (int i = 0; i < m_playerHand.size(); i++){
        sPlayerHand << i + 1 << ". " << m_playerHand[i]->getValue() << " - " << m_playerHand[i]->getType() << endl;
        m_playerHand[i]->setInHand(false);
    }
    return sPlayerHand.str();
}

void Player::playGame(){

    stringstream sPlayerHand;
    sPlayerHand << "Player's " << m_username << endl;

    int option = 0;
    for (int i = 0; i < m_playerHand.size(); i++){
        m_playerHand[i]->setInHand(false);
        sPlayerHand << option  << ". " << m_playerHand[i]->getValue() << " - " << m_playerHand[i]->getType() << endl;
    }

    cout << "Enter a choice for " << m_username << ": " ;
    cin >> option;

    cout << "You have selected the card: (" << m_playerHand[option-1]->getValue() << " - " << m_playerHand[option-1]->getType() << ").\n\n";

    //if value and type matches with top card, then remove, else, return error message
    clock_t start;
    int pause = 1000;
    for (int i = 0; i < 1; i++){
        cout << "Loading next player's hands..." << flush << "\n\n";
        start = clock();
        while (clock() < start + pause);
    }
}

void Player::computerPlayerGame(){
    Card* card = m_playerHand[rand() % m_playerHand.size()];

    stringstream sPlayerHand;
    sPlayerHand << "Player " << m_username << "\n\n";

    int option = 0;
    for (int i = 0; i < m_playerHand.size(); i++){
        sPlayerHand << option << ". " << m_playerHand[i]->getValue() << " - " << m_playerHand[i]->getType() << endl;
    }

    cout << m_username << " have selected the card: (" << card->getValue() << " - " << card->getType() <<  ").\n\n"; 

    clock_t start;
    int pause = 2000;
    for (int i = 0; i < 1; i++){
        cout << "Loading next player's hands..." << flush << "\n\n";
        start = clock();
        while (clock() < start + pause);
    }
    //if only pick rand card with value and type matches with top card, then remove, else, return error message
};

Main.cpp

#include "Game.h"
#include "Player.h"
#include "Card.h"
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include <algorithm>  
#include <vector>

using namespace std;

int main(){
    //create instances of files we've created
    Game* unoGame = new Game;

    unoGame->processMenu();

    delete unoGame;

    system("pause");
    return 0;
};

I would appreciate any help! Please and thank you.

Rae

Edited 2 Years Ago by pinkamech

Ok -- it compiles and that is great! Well done...
However, that is were the good stuff stops [Sorry]
I am not going to post a working and viable solution. But I am going to point out some of the problems that are leading to segmentation faults/undefined behaviour, and things that probabiliy are giving you logic errors etc [or at least will unless you are lucky]

Copy Constructors/ Assignment operator

First off : MOST serious. All classes have a copy constructor and an assignment operator. There are two cases (a) the programmer defines it and writes it, or (b) the compiler writes it for you. BUT the compiler writes a simple byte copy version. In the case that any of the members of your class do their own memory allocation it is 99% certain that you are not going to get what you want.

There is a solution : [Something we enforce at my work]. You ALWAYS write a copy constructor and an assignment operator. ALWAYS, NO EXCEPTION. If you don't want it called by any function declare it private.

In your case you have class Card for example, it has std::string members, therse will not be copied correctly. Worst still is Player!!
You have defined your own memory pointers in a vector and you still don't write a copy constructor/assignment operator!!

Loop issues

 for (int i = 0; i < 2; --i){
        cout << displayPlayer4Cards << endl;
        player4->playGame();

      // ..
    }

99.9999% certain you didn't mean this! You loop a VERY large number o f time.

Errors with the loop above that one::

for (int i = 1; i <= 7 * numPlayers; ++i)
{
   if (i < cards.size()) 
      {
         players[playerIndex]->addCardFromDrawPile(cards[i - 1]);            
         cards[i]->setInHand(true);
      }
}   

Here you use i-1 and i. Not as intended.

The game is impossible to play as a player because the slightest typing error is going to cause a problem:

cout << "Enter a choice for " << m_username << ": " ;
cin >> option;

cout << "You have selected the card: (" << 
    m_playerHand[option-1]->getValue() << 
    " - " << m_playerHand[option-1]->getType() << 
    ").\n\n";

Accidentally type 0, or 8 and you are in segfault territory .

Obviously, playGame() doesn't actually do anything yet, it just displays the card you have selected.

Care with checking vector lengths

In several places you have writen code like this:

while (m_discardPile.back() != 0)
{
    m_discardPile.push_back(m_discardPile.back() - 1);
}

The first thing that is wrong here is that IF the vector is empty, then the while loop is completely undefined. Second, you then subtract 1 from a pointer that can be at the front of a vector of cards and thus points to undefined memory.

This pattern is repeated. I actually cannot work out what this funciton should do, but I guess that you need something like this test

if (!m_discardPile.empty()) { } 

but I have no real idea.

Const / References

There are no use of const or references in the code (but pointers!).
Both of these things would aid in debugging etc. For example, does a card change! No!! So make std::string m_value, const. Also there are many places were you are passing complex values e.g.
void addCardFromDrawPile(Card* card) { ... } were a simple reference would help.

What needs to be done:

In short, you are having a memory nightmare with card. So forget it, just use Card, not Card*. Keep a simple vector of the main deck something like std::vector<Card> MainDeck;, Then each player can have a std::vector<Card> Hand; and you probabily need a discard pile std::vector<Card> Discard;. Then as you play a card, simply push it back from one vector and removed it from its corresponding vector. That way you don't need to have things like inHand and such, as the Card doesn't need to know were it is.

Hi StuXYZ, thanks for your constructive feedback. I will take them into account and work from there.
Best rgds.

This article has been dead for over six months. Start a new discussion instead.