1

So I have a 2 games running side by side.

proj2/main.cpp (main that gets run that then can call either game)
proj2/spades/display.cpp
proj2/spades/gameplay.cpp
proj2/spades/otherFiles.cpp
proj2/hearts/display.cpp (this is the same display class as in the spades game with the same functions just sightly altered.)
proj2/hearts/hearts.cpp
proj2/hearts/otherFiles.cpp

What basically happened is I am trying to integrate 2 games, 1 I created and another that uses the same display.cpp and display.h files slightly altered. In order to do this I created 2 seperate namespaces, hearts and spades. Everything I have compiles fine but a few of the .cpp files do not link correctly and I get errors such as the following.

./gamePlay.o: In function `__static_initialization_and_destruction_0(int, int)':
gamePlay.cpp:(.text+0x396): undefined reference to `spades::display::display()'
./gamePlay.o: In function `__tcf_3':
gamePlay.cpp:(.text+0x480): undefined reference to `spades::display::~display()'
./gamePlay.o: In function `spades::gamePlay::storeBid(std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >&)':
gamePlay.cpp:(.text+0x1507): undefined reference to `spades::display::captureInput()'

What am I doing wrong here?

Below are some of the files I am using. Let me know if there are anymore I should add. (Dont want to bloat this post anymore than I have to)

gameplay.cpp

#include <iostream>
#include <sys/ioctl.h>
#include <cstdio>
#include <unistd.h>
#include <locale.h>
#include <ncursesw/ncurses.h>
#include <fstream>


#include "gamePlay.h"
#include <cstdlib>
#include <sstream>
#include <ctime>
namespace spades {
vector <spades::player> players(4);
vector <spades::card> deck(52);
spades::display monitor;
spades::card center[4];

vector <spades::card> gamePlay::getDeck(){return deck;}
vector <spades::player> gamePlay::getplayers(){return players;}

//sorts the cards in the players hand into diamonds, clubs, hearts, spades
void gamePlay::handSort(){
    int spades[13];
    int hearts[13];
    int clubs[13];
    int diamonds[13];
    int index;
    int i;
    for(i=0; i<13; i++){ //determines the card number and places them into corresponding indexes
        index = (players.at(0).hand.at(i).getCardNum()+11)%13; //cause the cards to be placed based on their number with 2 being index 0 and 1(ace) being index 12
        switch(players.at(0).hand.at(i).getSuit())
        {
        case 1: spades[index] = 1;
        break;
        case 2: hearts[index] = 1;
        break;
        case 3: clubs[index] = 1;
        break;
        case 4: diamonds[index] = 1;
        break;
        default: mvprintw(3,2,"!!!!!!!we have a problem!!!!!!!!!!");
        break;
        }
    }
    i = 0;
    while(i<13){ //being placing the ordered cards back into the players hand
        int j = 0;
        while(j < 13){
            if(diamonds[j] == 1){ //if a card has been placed in this index for the diamonds only array
                if(j+2 == 14) //if the card is an ace
                    players.at(0).hand.at(i).setCardNum(1);
                else
                    players.at(0).hand.at(i).setCardNum(j+2); //add 2 to each index to get the card number
                players.at(0).hand.at(i).setSuit(4);
                i++;
            }
            j++;
        }
        j = 0;
        while(j < 13){
            if(clubs[j] == 1){
                if(j+2 == 14)
                    players.at(0).hand.at(i).setCardNum(1);
                else
                    players.at(0).hand.at(i).setCardNum(j+2);
                players.at(0).hand.at(i).setSuit(3);
                i++;
            }
            j++;
        }
        j = 0;
        while(j < 13){
            if(hearts[j] == 1){
                if(j+2 == 14)
                    players.at(0).hand.at(i).setCardNum(1);
                else
                    players.at(0).hand.at(i).setCardNum(j+2);
                players.at(0).hand.at(i).setSuit(2);
                i++;
            }
            j++;
        }
        j = 0;
        while(j < 13){
            if(spades[j] == 1){
                if(j+2 == 14)
                    players.at(0).hand.at(i).setCardNum(1);
                else
                    players.at(0).hand.at(i).setCardNum(j+2);
                players.at(0).hand.at(i).setSuit(1);
                i++;
            }
            j++;
        }
    }
}

//compares the center pile of 4 played cards to determine who wins the trick
int gamePlay::compareCenter(int leadplayer){
    int highest = center[leadplayer].getCardNum();
    if(center[leadplayer].getCardNum() == 1)
        highest = 14;
    int suit = center[leadplayer].getSuit();
    int player = leadplayer;

    for(int i = leadplayer+1; i < leadplayer+4; i++)
    {
        if(center[i%4].getSuit() == 1)
            setSpadesBroken(true);
        if((suit != 1) && (center[i%4].getSuit() == 1))
        {
            player = i%4;
            suit = 1;
            highest = center[i%4].getCardNum();
        }
        if(suit == center[i%4].getSuit()){
            if(center[i%4].getCardNum() == 1){
                player = i % 4;
                highest = 14;
            }
            if(highest < center[i%4].getCardNum())
            {
                player = i%4;
                highest = center[i%4].getCardNum();
            }
        }
    }
    players.at(player).setTricksTaken(players.at(player).getTricksTaken()+1); //increments the trick count of the winning player
    return player; //return the player who won to determine who goes first next turn
}

//Create the deck of 52 cards by setting the suit and number of each card to a nonzero integer
void gamePlay::createDeck() {
    for(int j = 0; j < 52; j++)
    {
        deck.at(j).setCardNum((j%13)+1);
        deck.at(j).setSuit((j/13)+1);
    }
    random_shuffle(deck.begin(), deck.end());
}

//deal out 13 cards to each player by setting the
void gamePlay::deal(vector <spades::card> &newdeck, vector <spades::player> &newplayers){
    for(int i = 0; i<52; i++){
        newplayers.at(i/13).addCard(newdeck.at(i));
        newdeck.at(i).setSuit(0);
        newdeck.at(i).setCardNum(0);
    }
}

//determines if the player still has a card of the same suit in their hand as the leading card played
bool gamePlay::containSuit(spades::card lead, spades::player players){
    bool suit = false;
    for(int i = 0; i < players.getHand().size(); i++){
        if(lead.getSuit() == players.getHand().at(i).getSuit())
            suit = true;
    }
    return suit;
}

//determines if the player has only spades cards left in their hand
bool gamePlay::onlySpade(spades::player play){
    for(int i = 0; i<play.getHand().size(); i++){
        if(play.getHand().at(i).getSuit()!=1)
            return false;
    }
    return true;
}

//determines if the position the player is clicking on the screen actually points to a playable card
//and then returns the position of that card based on the player's hand vector of type <card>
int gamePlay::handCheck(int xevent, int yevent, vector <spades::player> players, int trickStart){
    int i = xevent/6;
    //first check to find the card on the display
    if(i>=0 && i<players.at(0).getHand().size() && yevent>17 && yevent<23 &&
            players.at(0).getHand().at(i).getSuit() != 0 &&
            players.at(0).getHand().at(i).getCardNum() != 0)
    {
        spades::card playedCard = players.at(0).getHand().at(i);
        //check to see if leading the current round or not and if spades are "broken"
        if(trickStart==0 && !getSpadesBroken()){
            if(onlySpade(players.at(0)))
                return i;
            else if(playedCard.getSuit() != 1)
                return i;
            else
                return (-1);
        }
        if(trickStart == 0 && getSpadesBroken())
            return i;
        //if not leading, then call the containsuit method to see if your hand contains one of similar suit
        if(trickStart > 0 && containSuit(center[trickStart],players.at(0))){
            if(playedCard.getSuit()==center[trickStart].getSuit())
                return i;
        }
        if(trickStart > 0 && !containSuit(center[trickStart],players.at(0)))
            return i;
        else
            return (-1);
    }
    mvprintw(4,3,"invalid card");
    return (-1);
}

//draws the cards in the player's hand if it contains a valid card, erase the card otherwise if invalid
void gamePlay::displayHand(){
    int offset = 0;
    for(int i =0; i<players.at(0).getHand().size(); i++){
        if(players.at(0).getHand().at(i).getSuit() != 0)
            monitor.displayCard(offset, 17, players.at(0).getHand().at(i).getSuit(), players.at(0).getHand().at(i).getCardNum(), 0);
        else
            monitor.eraseBox(offset, 17, 6, 5);
        offset+=6;
    }
}
void gamePlay::displayAdd(){
    vector <string> lines;
    int count12 = 0;
    string line;
    ifstream myfile ("advertisement.txt");
    if ( myfile.is_open() )
    {
         while ( ! myfile.eof() )
         {
               getline (myfile, line);
               lines.push_back(line);
               count12++;
         }
         myfile.close();
        int random1 = rand() % 6;
        monitor.bannerBottom(lines.at(random1));
    }
    else{
          monitor.bannerBottom("Unable to open Advertisement file.");
    }

}
//determins the position of a mouse click and sends that to handcheck(), and then sends the card to the center pile array to be scored
void gamePlay::humanPlay(int trickStart){
    int xevent, yevent;
    for(;;){
        mvprintw(3,2,"Please choose a card to play.");
        int key = monitor.captureInput();
        // if a mouse click occurred
        if (key == -1) {
            xevent = monitor.getMouseEventX();
            yevent = monitor.getMouseEventY();
            int handCh = handCheck(xevent, yevent, players, trickStart);
            if(handCh != (-1)){ //if the card is valid
                spades::card played = players.at(0).getHand().at(handCh);
                players.at(0).hand.at(handCh).setCardNum(0);
                players.at(0).hand.at(handCh).setSuit(0);
                center[0]= played;
                monitor.displayCard(39, 12, center[0].getSuit(), center[0].getCardNum(), 0);
                displayHand();
                //playedCards++; //Update Global Variable---- NEED TO HAVE THIS INITIATED GLOBALLY.
                break;
            }
            else
                mvprintw(4,3,"invalid card");
        }
    }
}

//loops through a computer players hand and checks to see if the random card is playable within the rules of the game
void gamePlay::CPUplay(int trickStart, int CPU){
    bool goodCard = false;
    spades::card playedCard =players.at(CPU).getHand().at(0);
    int i;
    for(i = 0; i < players.at(CPU).getHand().size(); i++){
        if(players.at(CPU).getHand().at(i).getSuit() != 0 &&
                players.at(CPU).getHand().at(i).getCardNum() != 0){
            playedCard = players.at(CPU).getHand().at(i);
            //check to see if leading or not
            if(trickStart==CPU && !getSpadesBroken()){
                if(onlySpade(players.at(CPU)))
                    break;
                if(playedCard.getSuit()!=1)
                    break;
            }
            if(trickStart == CPU && getSpadesBroken())
                break;
            //if not leading use contains suit function
            if(trickStart != CPU && containSuit(center[trickStart], players.at(CPU)))
                if(playedCard.getSuit()==center[trickStart].getSuit())
                    break;
            if(trickStart != CPU && !containSuit(center[trickStart], players.at(CPU)))
                break;
        }
    }
    players.at(CPU).hand.at(i).setCardNum(0);
    players.at(CPU).hand.at(i).setSuit(0);
    center[CPU]= playedCard;
    if(CPU==1)
        monitor.displayCard(29, 7, center[CPU].getSuit(), center[CPU].getCardNum(), 0);
    if(CPU==2)
        monitor.displayCard(39, 2, center[CPU].getSuit(), center[CPU].getCardNum(), 0);
    if(CPU==3)
        monitor.displayCard(49, 7, center[CPU].getSuit(), center[CPU].getCardNum(), 0);
}

//scores each team with the player being teamed with CPU 2 and the other team being CPU 1 and CPU 3
void gamePlay::score(spades::player &play, spades::player &play2){
    int trickOver = play.getTricksTaken()-play.getBid(); // Calculate the difference between bid and actually taken.
    if(play.getBid() == NULL)
        trickOver = 0;
    int trickOver2 = play2.getTricksTaken()-play2.getBid(); // Calculate the difference between bid and actually taken.
    int totalBid = play.getBid()+play2.getBid();
    int totalTrick = trickOver+trickOver2;
    //Bidding Double Nil (if gets it 200 points other wise -200 points)
    if(play.getDoubleNil()){
        if(play.getTricksTaken()==0) //player did get Double Nil successfully
            play.setScore(play.getScore()+200); // add 200 points
        else
            play.setScore(play.getScore()-200);
    }
    else if(play.getBid()==0){ //Bidding Nil (if gets it 100 points other wise -100 points)
        if(play.getTricksTaken()==0) //player did get Nil successfully
            play.setScore(play.getScore()+100); //add 100 points
        else //player didnt get Nil
            play.setScore(play.getScore()-100); //take away 100 points
    }
    if(totalTrick < 0){ //player bids more than number of tricks won
        play.setScore(play.getScore()+(totalBid*(-10)));  //decrease score by 10 poitns for every overtrick
    }
    else if(totalTrick >= 0){ //player bids less then number of tricks won
        play.setSandBag(play.getSandBag() + totalTrick); //increase sandbag by 1
        play.setScore(play.getScore()+totalTrick+(10*totalBid)); //increase 10 points per trick bid on and 1 point per trick over
    }
    if(play.getSandBag()>10){ //check for sandbagging
        play.setScore(play.getScore()-100);
        play.setSandBag(play.getSandBag()-10);
    }
    play.setBid(NULL); //reset players bid to NULL
    play2.setBid(NULL); //reset players bid to NULL
    play.setTricksTaken(0);
    play2.setTricksTaken(0);
    play.setDoubleNil(false); //Player has not yet bid double NILL.
}

//loops infinitely until a mouse click or key input is detected, then sets bid accordingly
void gamePlay::storeBid(stringstream &msg){
    int xevent;
    int yevent;
    for(;;){
        mvprintw(3,2,"Click the box bid Double Nil or type out your bid now. \n  Use the keys 1-9, a for 10, b for 11, c for 12, and d for 13.");
        int key = monitor.captureInput();
        monitor.drawBox(2, 5, 3, 2, 0);
        xevent = monitor.getMouseEventX();
        yevent = monitor.getMouseEventY();
        if (key == -1)
            if((xevent>=2 && xevent<4)&&(yevent>=5 && yevent <7)){
                msg.str("");
                msg << "Your bid: double nil";
                monitor.bannerTop(msg.str());
                players.at(0).setDoubleNil(true);
                break;
            }
            else{
                msg.str("");
                msg << "Your bid is horrible. Bid again!!!!";
                monitor.bannerTop(msg.str());
            }
        if ((key-48) >= 0 && (key-48) <= 9){
            msg.str("");
            msg << "Your bid: " << (key-48);
            monitor.bannerTop(msg.str());
            players.at(0).setBid(key-48);
            break;
        }
        else if(key>=97 && key<=100){
            msg.str("");
            msg << "Your bid: " << (key-87);
            monitor.bannerTop(msg.str());
            players.at(0).setBid(key-87);
            break;
        }
        else if(key!=0 && key!=-1){
            msg.str("");
            msg << "Your bid is horrible. Bid again!!!!";
            monitor.bannerTop(msg.str());
        }
    }
    monitor.eraseBox(1, 1, 62, 16);
}

//main function that runs the entire game
void gamePlay::runGame(){
    double startTime = clock();
    stringstream messageString;
    srand(time(NULL));

    int count;
    int handStart = 0;
    int trickStart;
    while(players.at(0).getScore()<500 && players.at(1).getScore()<500)
    {
        createDeck();
        deal(deck, players); //deal out 13 cards to each player
        handSort(); // sort the human players hand
        displayHand();

        storeBid(messageString); //asks for bid

        //sets the bid randomly between 2-4 for each computer player
        players.at(1).setBid(rand()%3+2);
        players.at(2).setBid(rand()%3+2);
        players.at(3).setBid(rand()%3+2);
        //displays the current bid and scores of each team
        messageString << "  Partner: " << (players.at(2).getBid())
        << "  CPU 1: " << players.at(1).getBid()
        << "  CPU 3: " << players.at(3).getBid()
        << "    Team Score = " << players.at(0).getScore()
        << "  Enemy Score = " << players.at(1).getScore();
        monitor.bannerTop(messageString.str());
        displayAdd();
        trickStart = handStart;
        count = 0;
        setSpadesBroken(false);
        while(count<13){ // simulates the different cases where the human player plays 1st, 2nd, 3rd, last
            bool success = true;
            switch (trickStart)
            {
            case 0: humanPlay(trickStart);
            CPUplay(trickStart,1);
            CPUplay(trickStart,2);
            CPUplay(trickStart,3);
            break;
            case 3: CPUplay(trickStart,3);
            humanPlay(trickStart);
            CPUplay(trickStart,1);
            CPUplay(trickStart,2);
            break;
            case 2: CPUplay(trickStart,2);
            CPUplay(trickStart,3);
            humanPlay(trickStart);
            CPUplay(trickStart,1);
            break;
            case 1: CPUplay(trickStart,1);
            CPUplay(trickStart,2);
            CPUplay(trickStart,3);
            humanPlay(trickStart);
            break;
            default: mvprintw(3,2,"!!!!we have a problem!!!!");
            success = false;
            break;
            }
            monitor.eraseBox(3, 2, 20, 2);

            for(;;){//pause to wait for an acknowledgment mouse click
                int key = monitor.captureInput();
                monitor.eraseBox(3, 2, 30, 4);
                mvprintw(3,2,"click anywhere to continue");
                if(key == -1){
                    break;
                }
            }
            //if no errors appear, display the tricks taken
            if(success){
                monitor.eraseBox(1, 1, 62, 16);
                trickStart = compareCenter(trickStart);
                messageString.str("");
                messageString << "Your Tricks= " << players.at(0).getTricksTaken()
                << "   Partner= " << players.at(2).getTricksTaken()
                << "   CPU 1= " << players.at(1).getTricksTaken()
                << "   CPU 3= " << players.at(3).getTricksTaken();
                displayAdd();
                monitor.bannerAboveBottom(messageString.str());
                count++;
            }
        }

        //score the round and tally up for each team
        for(int n =0; n<2;n++)
            score(players.at(n),players.at(n+2));
        messageString.str("");
        monitor.bannerTop(messageString.str());
        messageString << "Your Tricks = " << players.at(0).getTricksTaken()
        << "   Partner = " << players.at(2).getTricksTaken()
        << "   CPU 1 = " << players.at(1).getTricksTaken()
        << "   CPU 3 = " << players.at(3).getTricksTaken();
        handStart = (handStart +1) % 4;
    }
    double endTime = clock();

}


}

gameplay.h

#include "player.h"
#include "display.h"

#ifndef gamePlay_H
#define gamePlay_H
namespace spades {
using namespace std;

class gamePlay{

    bool spadesBroken;

public:
    vector <spades::card> getDeck();
    vector <spades::player>getplayers();
    bool getSpadesBroken() {return spadesBroken;}
    void setSpadesBroken(bool b){spadesBroken = b;}

    int compareCenter(int leadplayer);
    void createDeck();
    void deal(vector <spades::card> &deck, vector <spades::player> &players);
    void handSort();
    bool containSuit(spades::card lead, spades::player players);
    bool onlySpade(spades::player play);

    int handCheck(int xevent, int yevent, vector <spades::player> players, int trickStart);

    void displayHand();
    void displayAdd();
    void humanPlay(int trickStart);
    void CPUplay(int trickStart, int CPU);
    void score(spades::player &play, spades::player &play2);
    void storeBid(stringstream &msg);
    void runGame();

};
}
#endif

player.h

#include <iostream> // Stream declarations
#include <vector> //Vectors used to store deck and players hands
#include <string> //String declarations
#include <algorithm> //Shuffle Method
#include "card.h"

#ifndef PLAYER_H
#define PLAYER_H
namespace spades {
using namespace std;

class player {


    int score; //total score
    int bid; //bid for that round
    int tricksTaken; //score for thast round
    int sandBag; //the number of points you win, more than what you bid, every 10th bag = -100
    bool doubleNil;
    //vector <card> hand;

public:
    player();
    vector <card> hand;

    int getSandBag(){return sandBag;}
    bool getDoubleNil() {return doubleNil;}
    int getScore() {return score;}
    int getBid() {return bid;}
    int getTricksTaken() {return tricksTaken;}
    vector<card> getHand() {return hand;}

    void setSandBag(int i) {sandBag = i;}
    void setBid(int i) {bid = i;}
    void setTricksTaken(int i) {tricksTaken=i;}
    void setScore(int i) {score = i;}
    void setDoubleNil(bool i) {doubleNil = i;}

    //add card to end of vector
    void addCard(card b);
    void removeCard(card a);

};
}
#endif

player.cpp

#include <iostream> // Stream declarations
#include <vector> //Vectors used to store deck and players hands
#include <string> //String declarations
#include <algorithm> //Shuffle Method
#include <sys/ioctl.h>
#include <cstdio>
#include <unistd.h>
#include <locale.h>
#include <ncursesw/ncurses.h>
#include "player.h"
namespace spades {
using namespace std;


player::player() {
    score =0; //total score
    bid = NULL; //bid for that round
    tricksTaken = 0; //score for thast round
    sandBag = 0; //the number of points you win, more than what you bid, every 10th bag = -100
    doubleNil = false;
    for(int i=0; i<13; i++)
        hand.push_back(card());
}

void player::addCard(spades::card b){
    for (int i=0; i<hand.size(); i++){
        //compare card being played to the ones in your hand to search and determine which one to erase
        if((hand.at(i).getCardNum() == 0) &&
            (hand.at(i).getSuit() == 0))
        {
            hand.at(i).setCardNum(b.getCardNum());
            hand.at(i).setSuit(b.getSuit());
            return;
        }
    }
}

void player::removeCard(spades::card a) {
    for (int i=0; i<hand.size(); i++){
        //compare card being played to the ones in your hand to search and determine which one to erase
        if((hand.at(i).getCardNum() == a.getCardNum()) &&
            (hand.at(i).getSuit() == a.getSuit()))
        {
            hand.at(i).setCardNum(0);
            hand.at(i).setSuit(0);
            return;
        }
    }
}
}
Peter Blum
  • 131
  • 11
  • 1
    tl;dr your answer is probably [here](http://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix) – Luchian Grigore Nov 28 '12 at 20:08

1 Answers1

1

What does your link line look like? It looks like when you're linking, you're missing spades.o?

If that's not it, use the nm command to see what symbols spades.o is providing - it's always possible you've typo'd a namespace name or something:

nm -C spades.o | grep 'display::.*display'

(The -C option should put nm into C++/demangled mode - if it doesn't for your nm, add a "c++filt" or "dem" step to the pipe chain before grep).

Mike G.
  • 1,670
  • 11
  • 17
  • Here's my Makefile `CC = g++
    all: compile
    find . -name '*.o' -print0 | xargs -0 $(CC) -lncursesw -o myExecutable
    compile:
    find . -name '*.cpp' -print0 | xargs -0 $(CC) -c
    clean:
    find . -iname '*.o' -print0 | xargs -0 rm`
    – Peter Blum Nov 28 '12 at 20:18
  • Ok, that looks like it should work, I think, although it's a bit too complex for what you need. So please try using nm and see what symbols are in your spades.o, and compare that with what your linker is telling you is missing... – Mike G. Nov 29 '12 at 00:22