Confusion using threads and thread techniques - c++

There's a construction zone. 100m long for cars going East/West....10m across for pedestrians crossing N/S. Here are the rules that must be followed:
neither car nor pedestrians should wait if the intersection is empty;
cars cannot go in the opposite directions simultaneously on the one-lane section;
a pedestrian cannot cross the street while there is a car in the one-lane section, but
multiple pedestrians can cross the street at the same time;
a car may enter the one lane section if there is a car there going in the same direction, however, a car is not allowed to pass another car;
a car does not wait for more than two cars going in the opposite direction;
a pedestrian has to yield to cars BUT a pedestrian should not wait for more than two cars (in either direction).
The example file being used is as follows: (Each row is a separate entity. E means "car going east" and W is west. P is for pedestrians. The first column is the number of seconds after the previous entity arrived at which now the new entity is arriving. The third column is the speed (meters per second):
Example being used:
0 E1 10
1 P1 1
4 E2 15
5 W1 10
Currently my code is printing out E1 enters.... (next line) E1 exits.... This is repeated on and on. I'm pretty confused on using threads and the techniques included so at this moment I'm stuck. What do I need to change to get this to print out the correct order at which the entities should be arriving and leaving the construction zone? All help is appreciated.
#include <iostream>
#include <vector>
#include <fstream>
#include <chrono>
#include <thread>
#include <random>
#include <ctime>
#include <mutex>
#include <string>
#include <condition_variable>
using namespace std;
class Traffic{
public:
void set_time(int a) {prevArrival = a;}
void set_name(string a) {name = a;}
void set_speed(int a) {carSpeed = a;}
int get_time() {return prevArrival;}
string get_name() {return name;}
int get_speed() {return carSpeed;}
private:
int prevArrival;
string name;
int carSpeed;
};
condition_variable_any cE, cW, ped;
mutex mtx;
int east=0; //number of cars traveling East currently in the zone
int west=0; //...traveling West...
int peds=0; //# of pedestrians crossing the street
void sleep(int secs);
void carWest(int time, string name, int speed);
void carEast(int time, string name, int speed);
void pedestrian(int time, string name, int speed);
int main(void){
srand(time(NULL));
ifstream ifs;
ofstream ofs;
string info, title, temp;
int i=0, e=0, w=0, p=0, time, speed;
Traffic crossers[50];
vector <thread> eastCars, westCars, pedestrians;
ifs.open("traffic.txt");
while (!ifs.eof()){
ifs >> time; crossers[i].set_time(time);
ifs >> title; crossers[i].set_name(title);
temp = crossers[i].get_name();
if(temp[0] == 'E' || temp[0] == 'e') {e++;}
else if(temp[0] == 'W' || temp[0] == 'w') {w++;}
else {p++;}
ifs >> speed; crossers[i].set_speed(speed);
i++;
}
ifs.close();
for (int i=0; i < e; i++) eastCars.push_back(thread(carEast, crossers[i].get_time(), crossers[i].get_name(), crossers[i].get_speed())); //creating threads
for (int i=0; i < p; i++) pedestrians.push_back(thread(pedestrian, crossers[i].get_time(), crossers[i].get_name(), crossers[i].get_speed()));
for (int i=0; i < w; i++) westCars.push_back(thread(carWest, crossers[i].get_time(), crossers[i].get_name(), crossers[i].get_speed()));
for (thread& t: eastCars) t.join(); // waiting for eastCars, westCars, and pedestrians to finish
for (thread& t: pedestrians) t.join();
for (thread& t: westCars) t.join();
}
void pedestrian(int time, string name, int speed) {
while(true){
if(name[0] == 'P' || name[0] == 'p'){
if(time == 0 || (east == 0 && west == 0 && peds == 0))
mtx.lock();
cout << name << " entering construction" << endl;
while(peds>0 || west>0 || east>0)
ped.wait(mtx);
peds++;
mtx.unlock();
sleep(10/speed);
cout << name << " exiting construction" << endl;
mtx.lock();
peds--;
ped.notify_one();
cE.notify_all();
cW.notify_all();
mtx.unlock();
}
}
}
void carWest(int time, string name, int speed) {
while(true){
if(name[0] == 'W' || name[0] == 'w'){
if(time == 0 || (east == 0 && west == 0 && peds == 0))
mtx.lock();
cout << name << " entering construction" << endl;
while(peds>0 || west>0 || east>0)
cW.wait(mtx);
west++;
mtx.unlock();
sleep(100/speed);
cout << name << " exiting construction" << endl;
mtx.lock();
west--;
cW.notify_one();
ped.notify_all();
cE.notify_all();
mtx.unlock();
}
}
}
void carEast(int time, string name, int speed) {
while(true){
if(name[0] == 'E' || name[0] == 'e'){
if(time == 0 || (east == 0 && west == 0 && peds == 0))
mtx.lock();
cout << name << " entering construction" << endl;
while(peds>0 || west>0 || east>0)
cE.wait(mtx);
east++;
mtx.unlock();
sleep(100/speed);
cout << name << " exiting construction" << endl;
mtx.lock();
east--;
cE.notify_one();
cW.notify_all();
ped.notify_all();
mtx.unlock();
}
}
}
void sleep(int secs){
this_thread::sleep_for(chrono::milliseconds(rand()%secs*1000));
}

Your problem is here:
for (int i=0; i < e; i++) eastCars.push_back(thread(carEast, crossers[i].get_time(), crossers[i].get_name(), crossers[i].get_speed())); //creating threads
for (int i=0; i < p; i++) pedestrians.push_back(thread(pedestrian, crossers[i].get_time(), crossers[i].get_name(), crossers[i].get_speed()));
for (int i=0; i < w; i++) westCars.push_back(thread(carWest, crossers[i].get_time(), crossers[i].get_name(), crossers[i].get_speed()));
In your small data file, you've got 2 east cars, 1 west car, and one pedestrian. So e will be 2, and p and w will be 1. crosser[0] is an east car
Now, look at those push_back loops. You're adding crossers[0] to all lists! Thus your first (and only) westCar is really an east car. But your carWest refuses to do any work except on west cars, so it does no work.
You probably want to keep completely separate lists, rather than putting everyone into a single crossers list. Either that or you can loop once over your crossers list, and examine the name to see what list/thread it goes in.

Related

C++ Vector of structs, read access violation

Edit: For loop didn't have a ending condition. Newbie mistakes.
I'm doing a school assignment for school, using MS VS, which has very specific
requirements. We're reading shape names and dimensions from a txt file, creating a struct for each shape with only the dimensions as members, and using a supporting function to calculate area/volume and output the results. Have to have 4 Loops:
The First will parse a txt file line by line,
check the type of shape, create a dynamic object and put it into a
generic bag.
The Second will process the bag and output the type of shape,
dimensions, and calculations to console.
The Third will do the same but output to another txt file.
The Last loop will delete all dynamic objects.
My program with only the code for squares:
#include <iterator>
#include <string>
#include <sstream>
#include <vector>
#include <iostream>
#include <fstream>
#include <map>
#include <cmath>
#include <cstdlib>
using namespace std;
int main()
{
string line, str;
double d1, d2, d3;
map < string, int > shapes;
vector<void*> myBag;
vector<char> myBagType;
shapes.insert(pair<string, int>("SQUARE", 1));
ifstream shapesin("TextFile1.txt");
ofstream shapesout("TextFile2.txt");
if (!shapesin || !shapesout)
{
cout << "Unable to open file\n";
}
while (getline(shapesin, line))
{
d1 = d2 = d3 = 0;
vector<string> token = parseString(line);
if (token.size() >= 1)
{
str = token[0];
switch (shapes[str])
{
case 1: //Square
{
Square* s = new Square;
myBag.push_back(s);
myBagType.push_back('S');
if (token.size() < 2)
{
s->side = 0;
break;
}
else
{
str = token[1];
d1 = atof(str.c_str());
s->side = d1;
}
break;
}
}
}
for (unsigned int i = 0; myBag.size(); i++)
{
if (myBagType[i] == 'S')
{
Square* aSquare = reinterpret_cast<Square*>(myBag[i]);
Square& bSquare = *aSquare;
outputSquare(cout, bSquare);
}
}
for (unsigned int i = 0; myBag.size(); i++)
{
if (myBagType[i] == 'S')
{
Square* aSquare = reinterpret_cast<Square*>(myBag[i]);
Square& bSquare = *aSquare;
outputSquare(shapesout, bSquare);
}
}
for (unsigned int i = 0; myBag.size(); i++)
{
if (myBagType[i] == 'S')
{
Square* aSquare = reinterpret_cast<Square*>(myBag[i]);
delete aSquare;
}
}
shapesin.close();
shapesout.close();
}
}
vector<string> parseString(string str)
{
stringstream s(str);
istream_iterator<string> begin(s), end;
return vector<string>(begin, end);
}
void outputSquare(ostream& shapesout, const Square& x)
{
double perim, area;
perim = (x.side * 4); //exception thrown here
area = (x.side * x.side);
shapesout << "SQUARE " << "side=" << x.side;
shapesout.setf(ios::fixed);
shapesout.precision(2);
shapesout << " area=" << area << " perimeter=" << perim << endl;
shapesout.unsetf(ios::fixed);
shapesout.precision(6);
}
Txt file input is:
SQUARE 14.5 344
SQUARE
RECTANGLE 14.5 4.65
DIMENSIONS
CIRCLE 14.5
BOX x 2 9
CUBE 13
BOX 1 2 3
CYLINDER 2.3 4 56
CANDY
SPHERE 2.4
CYLINDER 1.23
CYLINDER 50 1.23
TRIANGLE 1.2 3.2
PRISM 2.199 5
EOF
I know I have a problem with the way I'm accessing the struct member x.side but every other way I've tried won't compile, where as this will at least output the first line. I've read other, similar, questions but couldn't find one quite like this. I would really appreciate some assistance.
for (unsigned int i = 0; myBag.size(); i++)
No terminating condition
for (unsigned int i = 0; i < myBag.size(); i++)
Fixed

Stack Program in C++

I want to implement a code in my program where it guesses whether or not the given series of operations and corresponding return values from an input file are operating as a stack. In other words, the output is fully determined by the input file.
The text file (StackTest.txt):
4
INSERT 2
INSERT 1
REMOVE 1
REMOVE 2
6
INSERT 5
INSERT 10
INSERT 12
REMOVE 10
REMOVE 5
REMOVE 12
2
INSERT 8
REMOVE 8
Expected Output(from input file):
stack
not stack
stack
However, I'm stuck on how to implement this feature into my current code.
If anyone can help me out on how to achieve the expected output above or give me some hints, I would really appreciate it!
Code in progress...
#include <iostream>
#include <fstream>
#include <string>
#include <stack>
using namespace std;
// Function to check validity of stack sequence
bool validStackSeq(string input, int len)
{
// maintain count of popped elements
int j = 0;
// an empty stack
stack <int> s;
for(int i = 0; i < len; i++)
{
s.push(input[i]);
// check if appended value is next to be popped out
while (!s.empty() && j < len && s.top() == input[j])
{
s.pop();
j++;
}
}
return j == len;
}
int main()
{
ifstream inFile;
string data;
string command;
int num;
inFile.open("StackTest.txt");
//cout << "Reading file..." << endl;
stack <int> s;
while(getline(inFile, data))
{
if(command == "INSERT")
{
s.push(num);
}
else if(command == "REMOVE")
{
s.pop();
}
num = sizeof(data)/sizeof(data[0]);
cout << (validStackSeq(data, num) ? "Stack" : "Not Stack") << endl;
}
inFile.close();
}
Current Output
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack
Stack Validation Program (without input file)
#include <iostream>
#include <stack>
using namespace std;
bool validStackSeq(int pushed[], int popped[], int len)
{
int j = 0;
stack <int> pt;
for(int i = 0; i < len; i++)
{
pt.push(pushed[i]);
while (!pt.empty() && j < len && pt.top() == popped[j])
{
pt.pop();
j++;
}
}
return j == len;
}
// Driver code
int main()
{
int pushed[] = {2, 1};
int popped[] = {1, 2};
int len = sizeof(pushed)/sizeof(pushed[0]);
int pushed1[] = {5, 10, 12};
int popped1[] = {12, 5, 10};
int len1 = sizeof(pushed1)/sizeof(pushed1[0]);
int pushed2[] = {8};
int popped2[] = {8};
int len2 = sizeof(pushed2)/sizeof(pushed2[0]);
int pushed3[] = {1, 4};
int popped3[] = {4};
int len3 = sizeof(pushed3)/sizeof(pushed3[0]);
cout << (validStackSeq(pushed, popped, len) ? "Stack" : "Not Stack") << endl;
cout << (validStackSeq(pushed1, popped1, len1) ? "Stack" : "Not Stack") << endl;
cout << (validStackSeq(pushed2, popped2, len2) ? "Stack" : "Not Stack") << endl;
cout << (validStackSeq(pushed3, popped3, len3) ? "Stack" : "Not Stack") << endl;
return 0;
}
Perform the INSERT and REMOVE operations as specified in the text file. The result is 'stack' if all remove operations are possible (do not occur when the stack is empty) and the operand of each remove operation equals the actual value poped from your stack.
UPDATED 2020-02-29
You cannot create two separate arrays for INSERT and REMOVE operations and process them independently, because the result depends on how the operations are interleaved. For example
INSERT 1
INSERT 2
REMOVE 2
REMOVE 1
should result in stack, but if we move one REMOVE operation up:
INSERT 1
REMOVE 2
INSERT 2
REMOVE 1
the result will become not stack. It means that you need to process the operations in exactly same order as they appear in the file.
The general structure of the code:
ifstream inFile;
inFile.open("StackTest.txt");
int num;
while (inFile >> num) { // Read number of INSERT/REMOVE in each test set
stack<int> s;
bool isStack = true;
for (int i = 0; i < num; i++) {
// Read and process all INSERT/REMOVE operations in this test set
// Set isStack to false if not stack behavior detected
}
cout << (isStack ? "stack" : "not stack") << endl;
}
inFile.close();
As we read the operations from the input file we try to perform them. The INSERT operations should be performed as-is, no checks are required.
if (operation == "INSERT") {
s.push(argument);
}
The REMOVE operations require two checks to be performed: whether the stack is empty and whether the top of stack contains the same number as the argument of the REMOVE operation. If either of the checks fail then we set isStack to false.
if (operation == "REMOVE") {
if (s.empty() || s.top() != argument) {
isStack = false;
}
if (!s.empty()) {
s.pop ();
}
}
Combining this together we get:
#include <iostream>
#include <fstream>
#include <string>
#include <stack>
using namespace std;
int main () {
ifstream inFile;
inFile.open("StackTest.txt");
int num;
while (inFile >> num) {
stack<int> s;
bool isStack = true;
for (int i = 0; i < num; i++) {
string operation;
int argument;
inFile >> operation >> argument;
if (!isStack)
continue;
if (operation == "INSERT") {
s.push(argument);
}
else if (operation == "REMOVE") {
if (s.empty() || s.top() != argument) {
isStack = false;
}
if (!s.empty()) {
s.pop ();
}
}
}
cout << (isStack ? "stack" : "not stack") << endl;
}
inFile.close();
}
There is just one more thing to mention: after we read the operation name and its argument from the file we do a check
if (!isStack)
continue;
This statement is not required, it's there just for a performance improvement. If we already detected that the given test set does not correspond to a stack then we don't need to perform all the remaining operations in this test set, we just skip them. You can safely remove this statement without influencing the result.

whitecards (lowercase) skips other players in PlayerVector but the first - why?

I have tried for several days to find a solution to this but I am at a loss.
I am creating a Cards Against Humanity game that makes use of a Player class. This player class has functions to add white cards, retrieve a white card, get rid of a white card, get name, set name, and others.
The program goes:
Main menu (goes to gameplay, how to play, credits, and quit)
Chooses number of players.
Creates vector of players
Sets player names.
Assigns eight cards to each player's whitecards vector.
Begins game.
The main issue I have is with the assigning eight cards to each player's whitecards vector.
This is the code for the part of the program that distributes cards.
for (i = 0; i < playercount; i++) /* Adds eight white cards to each player's vector of cards */
{
PlayerVector.at(i).addwhitecard();
PlayerVector.at(i).addwhitecard();
PlayerVector.at(i).addwhitecard();
PlayerVector.at(i).addwhitecard();
PlayerVector.at(i).addwhitecard();
PlayerVector.at(i).addwhitecard();
PlayerVector.at(i).addwhitecard();
PlayerVector.at(i).addwhitecard();
}
This is the section of Player.cpp that assigns a white card.
bool Player::addwhitecard()`{
whitecard_ptr = fopen("WhiteCards.txt", "r");
if (whitecard_ptr != NULL)
{
std::cout << "This Part Worked" << std::endl;
if (maxwhitecardcount >= whitecards.size())
{
std::cout << "Yes Continue Pls" << std::endl;
srand(time(0));
randomnumber = rand() % 398;
randomnumber2 = rand() % 398;
randomnumber3 = rand() % 398;
randomnumber4 = rand() % 398;
randomnumber5 = rand() % 398;
randomnumber6 = rand() % 398;
randomnumber7 = rand() % 398;
randomnumber8 = rand() % 398;
while (getline(WhiteCards, line))
{
++LineNo;
if (LineNo == randomnumber || LineNo == randomnumber2 || LineNo == randomnumber3 || LineNo == randomnumber4 || LineNo == randomnumber5 || LineNo == randomnumber6 || LineNo == randomnumber7 || LineNo == randomnumber8)
{
whitecards.push_back(line);
std::cout << "Yes! This size is: " << whitecards.size() << std::endl;
return true;
}
}
}
else
{
std::cout << name << "has too many white cards!";
return false;
}
}
}
This is the constructor for the player class.
Player::Player(std::string name, int points)
{
this->points = 0;
this->name = "PlayerName";
this->whitecards;
}
And this is the player class itself in the header file
class Player
{
public:
Player();
Player(std::string name, int points);
void drawwhitecard();
std::string getwhitecard();
void ridwhitecard();
void addpoint();
int getpoints();
bool addwhitecard();
void namechar();
bool showwhitecards();
bool setname();
std::string getname();
protected:
int points;
std::vector<std::string> whitecards;
std::string name;
int whitecardcount;
int maxwhitecardcount = 8;
int whitecardchoicenumber = 0;
};
I tried to put the whitecards vector as an argument in player, but I wasn't able to use the vector in the arguments list when creating a player object in the main code.
Can someone tell me where I went wrong? Whether I do actually need to put the vector in the arguments and how to use it in an arguments list.
Thanks in advance.
I found a solution to this, though it's somewhat long.
I had to add 56 other randomnumber variables that all the other players can use.
Sorry I couldn't provide a MCVE due to time constraints.

Linking/Integration Issue. I need a direction of where to go?

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;
}
}
}
}
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).

C++ Member Function Clearing Matrix

I have had a similar issue with quite a few projects I have worked on involving classes containing arrays. I have a class that is supposed to handle a 2 dimensional matrix representing a TicTacToe game. There is an enumeration in the class for the status of the current game and one member function that has an enumeration return type. I cant seem to figure out why I can create the class set values in the matrix and as soon as I call the member function with the enumerated return type the whole array is reinitialized to 0. I think it has something to do with the constructor being called again or something along those lines but I have not been able to find anything after searching for the past few hours. Any help would be greatly appreciated.
Here is my header file containing the class information:
#ifndef TTT_H
#define TTT_H
#include <cstdlib>
#include <iostream>
using namespace std;
class TicTacToe
{
private:
enum Status{WinX, WinO, Continue, Draw};
int **board;
public:
TicTacToe();
~TicTacToe();
void PrintBoard();
bool ValidMove(int, int);
bool PlayerMove(int, int, int);
Status GameStatus(); //this one causes the problem
void Debug();
};
#endif
Here is the code for CPP file with the member function definitions:
#include "TicTacToe.h"
#include <iostream>
#include <cstdlib>
#include <iomanip>
#include <cassert>
using namespace std;
TicTacToe::TicTacToe()
{
board = new int*[3];
assert(board != 0);
for(int i=0;i<3;i++)
{
cout << "Constructor Ran again" << endl; //for testing
board[i] = new int[3];
assert(board[i] != 0);
for(int j=0;j<3;j++)
board[i][j] = 9;
}
}
TicTacToe::TicTacToe(TicTacToe &copy)
{
board = new int*[3];
assert(board != 0);
}
TicTacToe::~TicTacToe()
{
if(board)
delete[] board;
}
void TicTacToe::PrintBoard()
{
for(int i=0;i<3;++i)
{
for(int j=0;j<3;++j)
{
cout << "| ";
switch(board[i][j]){
case 0:
cout << "O ";
break;
case 1:
cout << "X ";
break;
case 9:
cout << " ";
break;
}
}
cout << "|" << endl;
cout << "------------" << endl;
}
}
bool TicTacToe::ValidMove(int row, int col)
{
bool valid = false;
if(row < 3 && col < 3)
{
if(board[row][col] == 9)
valid = true;
}
return valid;
}
bool TicTacToe::PlayerMove(int player, int row, int col)
{
bool done = false;
if(ValidMove(row,col) == true)
{
if(player == 1)
board[row][col] = 1;
else
board[row][col] = 0;
done = true;
}
return done;
}
TicTacToe::Status TicTacToe::GameStatus() //This function is the problem
{
int check, empty = 0;
bool done = false;
for(int i=0;i<3;++i)
{
for(int j=0;j<3;++j)
{
check += board[i][j];
if(board[i][j] = 9)
empty++;
}
if(check == 0)
return WinO;
else if(check == 3)
return WinX;
check = 0;
}
if(empty == 0)
return Draw;
for(int i=0;i<3;++i)
{
for(int j=0;j<3;++j)
check += board[j][i];
if(check == 0)
return WinO;
else if(check == 3)
return WinX;
check = 0;
}
check = board[0][0] + board[1][1] + board[2][2];
if(check == 0)
return WinO;
else if(check == 3)
return WinX;
check = 0;
check = board[0][2] + board[1][1] + board[2][0];
if(check == 0)
return WinO;
else if(check == 3)
return WinX;
check = 0;
return Continue;
}
void TicTacToe::Debug()
{
//cout << &board[0][0] << endl;
for(int i=0;i<3;++i)
{
for(int j=0;j<3;++j)
cout << board[i][j];
cout << endl;
}
}
Here is the driver file I am using to test:
#include "TicTacToe.h"
#include <iostream>
#include <cassert>
using namespace std;
int main()
{
int row, col;
bool valid;
enum Status{WinX, WinO, Continue, Draw};
TicTacToe * T;
T = new TicTacToe;
assert(T != 0);
cout << "There are 2 players. P1 is x P2 is o" << endl;
do
{
T->PrintBoard();
valid = false;
while(valid == false)
{
cout << "\nP1 choose a cell" ;
cin >> row >> col;
if(T->ValidMove(row, col) == true)
{
T->PlayerMove(1, row, col);
valid = true;
}
else
{
cout << "Not a valid choice" << endl;
valid = false;
}
}
T->PrintBoard();
cout << endl;
T->GameStatus(); //<<<<<this is the pain in my butt
T->PrintBoard();
valid = false;
while(valid == false)
{
cout << "\nP2 choose a cell" ;
cin >> row >> col;
if(T->ValidMove(row, col) == true)
{
T->PlayerMove(2, row, col);
valid = true;
}
else
{
cout << "Not a valid choice" << endl;
valid = false;
}
}
}
while(/*T->GameStatus() == Continue*/ 1==1);
//the call to GameStatus was commented out of the
//while statement for testing
return 0;
}
I know the code inside of the GameStatus function is far from pretty but the array is messed up before any of those lines are processed.
I left all of the other functions just to show that they work properly without issue.
Thanks in advance for any help you may be able to give.
You've got a simple typo in your code..
if(board[i][j] = 9) // will always return true (refp)
empty++;
Other remarks
When looking at your code a bit more thoroughly I see that you have a few other miss-happens, intentional or unintentional.. that I don't know:
int check is not initialized in TicTacToe::GameStatus
You are not freeing the allocated memory properly, you'll
need to free all entries in board, ie. delete board[i])
I don't like bugs, how can I get rid of the operator= vs operator== problem?
A quite common method to circumvent the problem of making a typo and writing = when you mean to compare (==) two variables is to flip the operands around (if one of them is a constant value, such as 9.
if(9 = board[i][j]) will not compile and such a bug would've never appeared in your code.
I'll have to say that I don't like writing my statements that way.. though it's a quite common method, especially in the "beginner" segment.
check is not initialized in GameStatus() .
if (board[i][j] = 9)
Isn't the above line of code resetting the array contents? You probably want to use == here, instead.
You have a serious issue in memory management. Look:
Your constructor performs 4 allocations (an array of pointers and 3 arrays of ints, to emulate a 2D arrray),
Your destructor performs 1 deallocation (= memory leak),
You have a custom destructor but you don't define (or block) operator= (you need to, see What is The Rule of Three?)
Your copy constructor is incomplete and doesn't create a "valid" object.
Basically the above is likely to cause you some memory problems. I suggest to:
Rewrite the destructor to first free all the arrays of ints, then the array of pointers,
Make the class TicTacToe uncopiable by declaring the copy constructor and the operator= as private.
Also some minor details on that matter:
board = new int*[3];
assert(board != 0);
The assertion is unnecessary. If the allocation fails, the operator new will throw an exception.
if(board)
delete[] board;
Operators delete and delete[] don't do anything if their argument is a null pointer, so that condition is redundant. Also you have designed your object with the invariant that the board exists as long as the TicTacToe object exists, so that check is totally unnecessary, right?
Keep it safe and simple!