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
I'm getting the following linker error,
./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()'
in my gamePlay.cpp file I define the following and I think this is where my error is,
display monitor;
if I add the extern identifier to the line such as
extern display monitor;
the error will disappear only to produce the following issue,
./gamePlay.o: In function `spades::gamePlay::storeBid(std::basic_stringstream<char, std::char_traits<char>, std::allocator<char> >&)':
gamePlay.cpp:(.text+0x14ee): undefined reference to `spades::monitor'
gamePlay.cpp:(.text+0x14f3): undefined reference to `spades::display::drawBox(int, int, int, int, int)'
I think this error is this coming from these following lines, but I have no idea how to fix this error. Feeling quite lost at the moment. (I don't understand why I would need the extern as an identifier for monitor either). Also all the includes should be alright.
int key = monitor.captureInput();
monitor.drawBox(2, 5, 3, 2, 0);
Below is my display.h file
/*
* Description: Defines constants and class prototypes and variables
*/
// These are include files they are used for accessing functions and
// variables in external libraries.
#include <ncursesw/ncurses.h>
#include <iostream>
#include <cstdio>
// This is the namespace std, without this you would need to add "std::"
// infront of every variable and function from the std library.
//using namespace std;
// These Mouse Mask Definitions are for some of the common mouse buttons
// that are used in this project.
#define LEFT_UP 1
#define LEFT_DOWN 2
#define LEFT_CLICK 4
#define LEFT_DOUBLECLICK 24
#define RIGHT_UP 4096
#define RIGHT_DOWN 8192
#define RIGHT_CLICK 16384
#define RIGHT_DOUBLECLICK 32768
#define MIDDLE_ROLLUP 1048576
#define MIDDLE_ROLLDOWN 256
namespace spades {
// Some extended character definitions for showing the special symbols
// in the extended UTF-8 character map.
const char joker[] = {0xE2, 0x98, 0xBA, 0};
const char clubs[] = {0xE2, 0x99, 0xA3, 0};
const char diamonds[] = {0xE2, 0x99, 0xA6, 0};
const char hearts[] = {0xE2, 0x99, 0xA5, 0};
const char spades[] = {0xE2, 0x99, 0xA0, 0};
/*
* This is the display class definitions
*/
class display {
public:
/* "constructor"
* This function is called whenever a new object of class display is first
* created. Put initialization functions in here.
*/
display(void){};
/* "destructor"
* This function is called just before a object is deleted. Put clean up
* functions in here.
*/
~display(){}; // destructor
// captures user input
int captureInput(void);
// stores new screensize on update
void handleResize(int sig);
/*
* Drawing commands
*/
// display a card on the screen
void displayCard(int x, int y, int suit, int number, int printAtt);
// erase in the shape of a box on the screen
void eraseBox(int x, int y, int sizeX, int sizeY);
// draw a box on the screen
void drawBox(int x, int y, int sizeX, int sizeY, int printAtt);
// display banner text at the bottom of the screen
void bannerBottom(std::string bannerText);
void bannerAboveBottom(std::string bannerText);
// display banner text at the top of the screen
void bannerTop(std::string bannerText);
// get information about the display
int getCols(void) {return cols;}
int getLines(void) {return lines;}
int getMouseEventX(void) {return mouseEventX;}
int getMouseEventY(void) {return mouseEventY;}
int getMouseEventButton(void) {return mouseEventButton;}
// Updates the screen after you finish drawing
void updateScreen(void) {refresh();}
// sets an offset for when cards clip the bottom of the screen
void setBottomOffset(int offset) {lineBoundaryOffset=offset;}
private:
// These are private functions and variables used inside of display.
// You should not try to access these from outside the display class.
void printFace(int suit, int number, int line, int printAtt);
void printSuit(int suit);
void printNumber(int number);
int cols;
int lines;
int mouseEventX;
int mouseEventY;
int mouseEventButton;
int lineBoundaryOffset;
};
}
Below is my display.cpp file
/*
* Description: Defines the functionality of the user interface.
*
* NOTES:
* * Requires the terminal (Putty) to be set to UTF-8.
* * Does not function when running a screen session.
*/
#include <iostream>
#include <sys/ioctl.h>
#include <cstdio>
#include <unistd.h>
#include <locale.h>
#include <ncursesw/ncurses.h>
#include "display.h"
namespace spades {
//using namespace std;
/* Function: This is the constructor.
* Description: It is called whenever an object of class display is initialized
*/
display::display(void) {
/* Initilization of Terminal */
// required to get card suits displaying, combined with UTF-8 set in terminal
setlocale(LC_ALL,"en_US.utf-8");
// initialize ncurses screen
initscr();
// allow for color settings
start_color();
// clear the screen
clear();
// turn off the keyboard echo (reqiured while drawing)
noecho();
// Change to character mode (so individual characters are being read at a
// time rather than waiting for a carriage return).
cbreak();
// Allows for function keys to be used (also nessacary for getting the mouse
// movement working).
keypad(stdscr, TRUE);
// set which mouse events are captured
mousemask(ALL_MOUSE_EVENTS, NULL);
// Setting the timeout for the capture input values are in 1/10ths of a second.
halfdelay(5);
// setup the screen size settings.
cols = 80;
lines = 24;
// this updates the locally stored col and line variables
handleResize(0);
// set a no card draw offset of 1 so the bottom banner is not overwritten
lineBoundaryOffset = 1;
// Settings for card colors (these can be set outside of the display class)
init_pair(1, COLOR_CYAN, COLOR_BLACK); // for card outline
init_pair(2, COLOR_BLUE, COLOR_BLACK); // for spades and clubs
init_pair(3, COLOR_RED, COLOR_BLACK); // for hearts and diamonds
init_pair(4, COLOR_GREEN, COLOR_BLACK); // for turned over card
init_pair(5, COLOR_GREEN, COLOR_BLACK); // for box drawing
init_pair(6, COLOR_GREEN, COLOR_BLACK); // for banner display
}
/* Function: This is the destructor.
* Description: This is called just before an object is deleted.
*/
display::~display() {
// this is turns off all the special settings and returns the terminal to normal
endwin();
// insert deletion of dynamically created objects here too
}
/*
* Function: This captures all the userinput.
* Description: It captures mouse and keyboard events.
* Returns "Positive Number"
* - for user keypress
* - this is a character code typed
* Returns "0" - for no user input
* - this is when nothing is typed for half a second
* - allows for other timed operations to occur
* Returns "-1" - for mouse event
* - details of the mouse event must be fetched from this class
* - use getMouseEventX, getMouseEventY and getMouseEventButton
*/
int display::captureInput(void) {
// obtain one mouse event or keypress
int ch=getch();
// this is a switch statement for the result of getch
switch (ch) {
case KEY_MOUSE: // this occurs when an mouse event occurs
{
MEVENT mevent; // this is a variable declared of type MEVENT
getmouse(&mevent); // this gets the mouse event from ncurses (library)
mouseEventX = mevent.x; // get the column location of the event
mouseEventY = mevent.y; // get the row location of the event
mouseEventButton = mevent.bstate; // get the button state of the mouse
return -1; // -1 is for a mouse event
}
break;
case ERR: // this occurs when there is a timeout
{
return 0; // 0 is when nothing occurs
}
break;
default: // this occurs when a key is pressed
return ch; // a character is when the user types something
}
return 0; // this is never called, but is used to stop compiler complaints
}
/*
* Function: Updates all the information in the display class on window resize
* Description: This function just updates information, it requires the call
* from a static singal handler. Signal handlers service interrupts and require
* a static function to be called, because they are not part of the main
* program control flow. The signal handler should be declared in the main
* class, because your game should redraw the display on a resize.
*/
void display::handleResize(int sig) {
#ifdef TIOCGSIZE // operating system dependant differences
struct ttysize ts;
ioctl(STDIN_FILENO, TIOCGSIZE, &ts); // get the information of the terminal
cols = ts.ts_cols;
lines = ts.ts_lines;
#elif defined(TIOCGWINSZ)
struct winsize ts;
ioctl(STDIN_FILENO, TIOCGWINSZ, &ts); // get the information of the terminal
cols = ts.ws_col;
lines = ts.ws_row;
#endif /* TIOCGSIZE */
resizeterm(lines, cols); // sets the ncurses window size correctly
}
/*
* Function: Displays various cards on the game screen
* Description: This function displays various playing cards on the screen.
* The first two arguments are the x and y coordinates of the top left corner
* of the card.
* The suit values are: 1=spades, 2=hearts, 3=clubs, 4=diamonds
* The numbers are: 1=Ace, 2-10=2-10, 11=Jack, 12=Queen, 13=King, 14=Joker
* Any suit and number that do not match the valid numberrs generates a face down
* card.
* The printAtt allows for one or more any of the following display settings:
* A_NORMAL Normal display (no highlight)
* A_STANDOUT Best highlighting mode of the terminal.
* A_UNDERLINE Underlining
* A_REVERSE Reverse video
* A_BLINK Blinking
* A_DIM Half bright
* A_BOLD Extra bright or bold
* A_PROTECT Protected mode
* A_INVIS Invisible or blank mode
* A_ALTCHARSET Alternate character set
* A_CHARTEXT Bit-mask to extract a character
* COLOR_PAIR(n) Color-pair number n
*/
void display::displayCard(int x, int y, int suit, int number, int printAtt) {
// Ncurses drawing settings
attron(COLOR_PAIR(1) | printAtt);
// prevent draw if it off the screen
if (x>=0 && y>=0 && x<cols-6 && y<lines-lineBoundaryOffset) {
// print the top lines of the card
mvprintw(y,x,"\u250c\u2500\u2500\u2500\u2500\u2510");
// the next 4 if statements prevent draw if it is over the bottom of the screen
if (y<lines-1-lineBoundaryOffset) {
move(y+1,x); // move command
printFace(suit,number,0, printAtt); // call function to print card face
}
if (y<lines-2-lineBoundaryOffset) {
move(y+2,x); // move command
printFace(suit,number,1, printAtt); // call function to print card face
}
if (y<lines-3-lineBoundaryOffset) {
move(y+3,x); // move command
printFace(suit,number,2, printAtt); // call function to print card face
}
if (y<lines-4-lineBoundaryOffset) {
// prints the bottom lines of the card
mvprintw(y+4,x,"\u2514\u2500\u2500\u2500\u2500\u2518");
}
}
// Ncurses turn off the drawing settings
attroff(COLOR_PAIR(1) | printAtt);
}
/*
* Function: Print a single line of what is written on the card.
* Description: This copies suit, number and printAtt from the calling function.
* Also includes what line of the card face is being drawn.
*/
void display::printFace(int suit, int number, int line, int printAtt) {
// draw left edge of the card
printw("\u2502");
if (suit==2 || suit==4) { // Red for Hearts and Diamonds
attron(COLOR_PAIR(3) | printAtt);
} else { // Black for Spades and Clover
attron(COLOR_PAIR(2) | printAtt);
}
// this the display of the joker
if (number==14) {
if (line==0)
printw("J%s ", joker);
if (line==1)
printw("oker");
if (line==2)
printw(" J%s", joker);
// this is the display for the cards with suits and numbers
} else if (suit>=1 && suit <=4 && number>=1 && number<=13) {
if (line==0) {
printSuit(suit); // function to draw suit
printNumber(number); // function to draw number
if (number!=10)
printw(" ");
printw(" ");
} else if (line==2) {
if (number!=10)
printw(" ");
printw(" ");
printNumber(number); // function to draw number
printSuit(suit); // function to draw suit
} else {
printw(" ");
}
// this is for a face down card
} else {
// the face down cards have a special color
attron(COLOR_PAIR(4) | printAtt);
if (line==0)
printw("%s %s", spades, hearts);
if (line==1)
printw("Play");
if (line==2)
printw("%s %s", diamonds, clubs);
attroff(COLOR_PAIR(1) | printAtt);
}
// turn on the card edge color settings
attron(COLOR_PAIR(1) | printAtt);
// print the right edge of the card
printw("\u2502");
}
/*
* Function: Print the suit of the card
* Description: This is just a look up table.
*/
void display::printSuit(int suit) {
switch (suit) {
case 1:
printw("%s",spades);
break;
case 2:
printw("%s",hearts);
break;
case 3:
printw("%s",clubs);
break;
case 4:
printw("%s",diamonds);
break;
default:
printw(" ");
break;
}
}
/*
* Function: Prints the number on the card
* Description: This is just a look up table.
*/
void display::printNumber(int number) {
switch (number) {
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
printw("%i",number);
break;
case 1:
printw("%c",'A');
break;
case 11:
printw("%c",'J');
break;
case 12:
printw("%c",'Q');
break;
case 13:
printw("%c",'K');
break;
default:
printw(" ");
break;
}
}
/*
* Function: Erases a rectangle on the screen
* Description: x,y is for the top left corner, sizeX and sizeY set
* how big the square is.
*/
void display::eraseBox(int x, int y, int sizeX, int sizeY) {
std::string strDraw;
int yCount;
int maxSizeX;
// this limits the column size of the draw when it is over the edge
// of the drawing area
if (sizeX+x > cols)
maxSizeX=cols-x;
else
maxSizeX=sizeX;
// for the number of rows that need to be drawn
for (yCount=0; yCount<sizeY;yCount++) {
// if the box goes over the edge of the drawable screen
// stop drawing by breaking the loop
if (yCount+y > lines-lineBoundaryOffset || y < 0)
break;
// reset the line to be drawn
strDraw = "";
// check that x is not off the screen
if (x<=cols && x >= 0) {
// make a string needed for box width
strDraw.append(maxSizeX,' ');
// print the line of the box
mvprintw(y+yCount,x,"%s",strDraw.c_str());
}
}
}
/*
* Function: Draws a box on the screen
* Description: x,y is for the top left corner, sizeX and sizeY set
* how big the square is. printAtt allows for changes in the
* display settings.
*/
void display::drawBox(int x, int y, int sizeX, int sizeY, int printAtt) {
std::string strDraw;
int ii;
int yCount;
// set the box setting colors on
attron(COLOR_PAIR(5) | printAtt);
// for the box height being drawn loop
for (yCount=0; yCount<sizeY;yCount++) {
// break loop if the drawing is offscreen
if (yCount+y > lines-lineBoundaryOffset || y < 0)
break;
// if x is on the screen
if (x<=cols) {
strDraw = "";
// for the box width loop
for (ii=0;ii<sizeX;ii++){
// stop drawing if the x is offscreen
if (ii+x > cols || x < 0)
break;
// first line
if (yCount==0) {
if (ii==0) {
strDraw.append("\u250c"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2510"); // right
} else {
strDraw.append("\u2500"); // middle
}
// last line
} else if (yCount==sizeY-1) {
if (ii==0) {
strDraw.append("\u2514"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2518"); // right
} else {
strDraw.append("\u2500"); // middle
}
// other lines
} else {
if (ii==0) {
strDraw.append("\u2502"); // left
} else if (ii==sizeX-1) {
strDraw.append("\u2502"); // right
} else {
strDraw.append(" "); // middle
}
}
}
// print the line that was created
mvprintw(y+yCount,x,"%s",strDraw.c_str());
}
}
// turn off the attribute colors
attroff(COLOR_PAIR(5) | printAtt);
}
/*
* Function: Draws a banner of text at the bottom right of the screen
* Description: Inverts the color and draws the banner at the bottom
* of the screen. Does not handle carriage returns on the string.
*/
void display::bannerBottom(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(lines-1,0);
// fill in extra space to the banner text is right adjusted
hline(' ',cols - bannerText.size());
// prints out the banner text
mvprintw(lines-1,cols-bannerText.size(),"%s", bannerText.c_str());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(lines-1,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
}
void display::bannerAboveBottom(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(1) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(lines-2,0);
// fill in extra space to the banner text is right adjusted
hline(' ',cols - bannerText.size());
// prints out the banner text
mvprintw(lines-2,cols-bannerText.size(),"%s", bannerText.c_str());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(lines-2,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(1) | A_REVERSE | A_BOLD);
}
/*
* Function: Draws a banner of text at the top left of the screen
* Description: Inverts the color and draws the banner at the top
* of the screen. Does not handle carriage returns on the string.
*/
void display::bannerTop(std::string bannerText) {
// change to the banner draw settings
attron(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
// checks if the banner string size is smaller than the width of the screen
if((unsigned)cols > bannerText.size()) {
// moves the cursor to the bottom of the screen
move(0,0);
// prints out the banner text
printw("%s", bannerText.c_str());
// fill in extra space after the banner text
hline(' ',cols - bannerText.size());
// if banner string size is larger than width of screen
} else {
// clip the banner text so it doesn't wrap over to the next line
mvprintw(0,0,"%s", (bannerText.substr(0,cols)).c_str());
}
// turn off the draw colors
attroff(COLOR_PAIR(6) | A_REVERSE | A_BOLD);
}
}
Below is my gameplay.h file
#include "player.h"
#include "display.h"
#ifndef gamePlay_H
#define gamePlay_H
namespace spades {
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
Below is my gameplay.cpp file
#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);
display monitor;
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).get