Create player objects based on the number of players from user input? - c++

Background:
I have created a player class and I want to ask the user how many players are going to play the game? Based on the user input I am trying to create that many instances of the player class. However, I used the following links to help me:
Create object using user input
http://www.cplusplus.com/forum/beginner/197342/
So, I tried their solution:
#include "Player.h"
int main() {
int totalPlayers = -1;
cout << "Enter total number of players: ";
while ((totalPlayers < 1) && (totalPlayers > 5)) {
cout << "How many players will be playing? (1-5): ";
cin >> totalPlayers;
}
vector<Player> players(totalPlayers);
system("pause");
}
I get the error: Unhandled exception at 0x763F40B2 in 16LinearChess.exe: Microsoft C++ exception: std::length_error at memory location 0x003BF690.
So, I googled this exact error and found this link: Error : std::length_error at memory location
So, firstly his code was no-where related to mine, but the error was same. I did not understand the answer, but I thought that I had to create the instances using heap memory. So I tried that:
#include "Player.h"
int main() {
int totalPlayers = -1;
cout << "Enter total number of players: ";
while ((totalPlayers < 1) && (totalPlayers > 5)) {
cout << "How many players will be playing? (1-5): ";
cin >> totalPlayers;
}
vector<Player> *players = new Player(totalPlayers);
delete[] players;
system("pause");
}
I got two errors: Severity Code Description Project File Line Suppression State
Error (active) E0144 a value of type "Player *" cannot be used to initialize an entity of type "std::vector<Player, std::allocator> *" 16LinearChess D:\Keshav\Programming Languages\C++\Beginner\01 Michael Dawson\16LinearChess\LinearChess.cpp 64
Severity Code Description Project File Line Suppression State
Error (active) E0289 no instance of constructor "Player::Player" matches the argument list 16LinearChess D:\Keshav\Programming Languages\C++\Beginner\01 Michael Dawson\16LinearChess\LinearChess.cpp
This is my Player class:
#include <iostream>
class Player : public Board {
protected:
int m_position;
Log logger;
int m_playerNumber;
public:
static int m_numberOfPlayers;
Player() :m_position(0) {
++m_numberOfPlayers;
m_playerNumber = m_numberOfPlayers;
}
void m_SetPlayerPosition(int &position) {
if ((position < 0) || (position > 100)) {
m_position = 0;
logger.Error("Position cannot be less than or greater than 100. Your position has been reset to 0 because you fell out of the map.");
}
else {
m_position = position;
}
m_SetBoardPosition(m_position, m_numberOfPlayers); // update the position on the board.
}
friend ostream &operator << (ostream &os, Player &player) {
os << "Player position on board: " << player.m_position << "\nPlayer Number: " << player.m_playerNumber << '\n';
return os;
}
};
int Player::m_numberOfPlayers = 0; // initializing total number of players.
Thank You!

The problem is with your while loop:
int totalPlayers = -1;
cout << "Enter total number of players: ";
while ((totalPlayers < 1) && (totalPlayers > 5)) {
cout << "How many players will be playing? (1-5): ";
cin >> totalPlayers;
}
The while loop will only run if the condition is true, but the condition can never be true since no number can be smaller than 1, but also greater than 5. And since the condition is not true, the while loop will never run, and totalPlayers will be equal to -1, which is something you never want if you're trying to access an array index.
Change it to this: totalPlayers < 1 || totalPlayers > 5 with an || instead, and you should be fine.
And for the error:
Unhandled exception at 0x763F40B2 in 16LinearChess.exe: Microsoft C++ exception: std::length_error at memory location 0x003BF690.
Your code threw an exception because totalPlayers is equal to -1. So you basically did this:
vector<Player> players(-1);
Which makes no sense since you are creating an array that hold -1 elements? So the code threw an exception telling you that something is wrong. The std::length_error should give a hint about what's wrong.
Also like many of the comments have stated, don't do this:
vector<Player> *players = new Player(totalPlayers);
The whole purpose of a vector is so you don't do that. Your first example works fine:
vector<Player> players(totalPlayers);

std::length_error occurs if std::vector attempts to resize to a size that is above the max_size(). Most often, this happens from a bad input -- such as -1 which will be converted to the largest unsigned value for std::vector<T>::size_type.
Judging by the code you shared, the problem is actually due to your while loop condition:
while ((totalPlayers < 1) && (totalPlayers > 5))
It is not possible for totalPlayers to be both simultaneously less than 1 and greater than 5 -- so this loop is never entered. Since the default value you assign to totalPlayers is -1, the size you resize to overflows and becomes the largest unsigned value -- which triggers the std::length_error.
Fixing this condition should fix your error.

Related

Program outputs incorrect numbers from array

Jeopardy point adding code.
The program is supposed to get the number of players with a dynamic array.
From there, you can enter players names and it will insert the names into a two-dimensional array.
Then you can choose to call on a player to start adding points to.
After looping a certain amount of times and pressing 0, the while loop will cease to run and will skip down to the for loop outputting players name and then points.
Problem: If I input "1" for playerNumber, and I start adding points to index [0][1] and outputs numbers quite different from the original numbers I put in. If there are more than 2 players, 2 of the 3 players have random numbers while one remains an accurate point count.
#include <iostream>
#include <string>
using namespace std;
int main(void)
{
//GLOBAL SCOPE, all loops and other boxes can use these
//Declaration variables w cout & cin.
int playerNumber;
string playerNames;
bool flag = true;
cout << "How many players are there: ";
cin >> playerNumber;
cout << endl;
//Array Declaration.
string playerList[playerNumber][2]; //Dynamic array. changes during program runtime.
int points[playerNumber]; //Dynamic array. Changes during runtime
//GLOBAL SCOPE, all loops and other boxes can use these
//Assigning values to arrays now.
cout << "Enter the players names: " << endl;
//Assigns player name to each row.
for(int i = 0; i < playerNumber; i++){
cin >> playerNames;
playerList[i][0] = playerNames; //Assigns players name to the array
cout << "Player " << i + 1 << ": " << playerList[i][0] << endl;
}
while(flag){
//LOCAL VARIABLES
int choice = 0; //Always reverts back to zero to prevent addition error.
int pointsValue = 0; //Always reverts back to zero to prevent addition error.
//LOCAL VARIALES
cout << "Press 0 to end game, if not, enter player number: " << endl;
cin >> choice;
if(choice == 0){ //Exit out of the while loop
flag = false;
}
else if(cin.fail()){
cout << "Not a number. Try again." << endl;
cin.clear();
cin.ignore(256, '\n');
}
else if(choice < 0 || choice > playerNumber){
cout << "Choice is less than 0 or greater than player count. Try again." << endl;
}
else{
cout << "Enter points: " << endl;
cin >> pointsValue;
cout << endl;
points[choice - 1] += pointsValue; //Assigns points to points array
playerList[choice - 1][1] = (to_string(points[choice - 1])); //Assigns points to playerNumber.
}
}
cout << endl;
cout << "END OF JEOPARDY. HERE ARE THE POINTS!!!" << endl;
cout << endl;
//Current points for each player
//Shows their name and points
for(int i = 0; i < playerNumber; i++){ //Loops so that player name and points are displayed
string playerName = playerList[i][0];
string totalPoints = playerList[i][1];
cout << playerName << " points: " << totalPoints << endl;
}
return 0;
}
Technically, this is illegal:
string playerList[playerNumber][2];
int points[playerNumber];
The size of the array must be known at compile time (not runtime). Though some compilers allow this as an extension to the language, it is not part of the standard language.
Better choice would have been to use std::vector.
Lets assume your compiler allows this:
The next issue is that you don't initialize the values in the array.
int points[playerNumber]; // This is an array of random numbers.
// Sure if you are doing a debug build the
// compiler may be nice and just set all the
// values to zero as speed is not important
// during debugging. But on a release build
// these could be any value.
Also if we want to be technical its actually undefined behavior to read from the array unless you first write a value. Though usally this is not going to cause the program to crash on most architectures and unfortuantely the code just runs like nothing bad is happening.
You can normall initialize the array like this:
int points[10] = {0}; // but your variable size stuff
// will stop working for that.
-------
// so you will have to manually initialize the members
int points[playerNumber];
for(int loop = 0; loop < playerNumber; ++loop) {
points[loop] = 0;
}
Or if you upgrade to vector:
std::vector<int> points(playerNumber,0); // Size and initial value.
// Though you don't need the
// initial value as vector will
// zero init members.
The last issue I will mention is that you are not consistent on checking if the read worked.
cin >> pointsValue;
What do you think happens to pointsValue if the read fails (ie. the user puts in an illegal value). The proper way to check the user input is to put the read inside an if statement.
if ( std::cin >> pointValue) {
// The read worked `pointsValue` has valid user input
}
else {
// The read faild.
// We don't know what the user input should be
}
The other thing to think about is that user input is line based. They add values hit return. So it is a good tactic to exploit that and read user input by the line. Once you have the line parse that and validate input. That way the standard input stream does not go into a bad state.
std::string line;
if (std::get(std::cin, line)) {
// We have a line of user input.
std::stringstream lineStream(std::move(line));
line.clear();
int choice;
if (lineStream >> choice) {
// We have a valid choice from the user.
// or do we. If the user entered `2x` is that valid input?
// because `choice` is 2; but there is still `x` on the
// input stream.
// That's a choice for you as the developer to make.
// if you don't care, then you have valid input. If you do
// care then you need to check there is no bad data on
// the line.
}
else {
// invalid choice
}
}
else {
// The user input stream just ended.
// This could mean the user entered the end-of-stream character
// or if the user had connected some other stream to the
// standard input on the command line and there is no more
// data to read.
}
I would note that this will probably never work for invalid input.
if(choice == 0){ //Exit out of the while loop
flag = false;
}
else if(cin.fail()){
// If you failed to read data from the std::cin
// the the value of `choice` is probably zero
// (if I remember my standard correctly) so the
// first branch of the if tree will be entered
// and you will never enter this branch.
cout << "Not a number. Try again." << endl;
cin.clear();
cin.ignore(256, '\n');
}

Storing dynamic array in vector

I have to display a histogram of a student's grades. I've stored the grades in a dyn. array, but my goal is to store them in a vector. What's the right way of going about this? Hope this makes sense.
Edit:
My attempt at using a vector
void displayHistogram(int minGrade, vector<int> ptrV) {
cout << endl;
for (int i = 0; i <= minGrade; i++) {
if (ptrV[i] != 0) {
cout << "Number of " << i << "'s: " << ptrV[i] << endl;
}
}
}
void histogram() {
int minGrade = 0, grade;
const int grade_max = 100;
vector<int> ptrV(grade_max, 0);
cout << "Enter the student's grades (-1 to stop entering): \n";
do {
cin >> grade;
if (grade > minGrade) {
minGrade = grade;
}
if (grade >= 0) {
ptrV.push_back(grade);
}
} while (grade != -1);
displayHistogram(minGrade, ptrV);
}
Your basic mistake is that you try to force the vector as if it was a raw array. It does stuff for you, let it. It knows it's size, for instance. You don't need
void displayHistogram(int minGrade, vector<int> ptrV) {
cout << endl;
for (int i = 0; i <= minGrade; i++) {
Instead, you can use vector::size:
void displayHistogram(vector<int> ptrV) {
cout << endl;
for (size_t i=0; i<ptrV.size(); i++) {
(Even better: void displayHistogram(const vector<int>& ptrV) to signify that ptrV is not changed here and to avoid copying it every time you call the function by using a reference.)
(If you wouldn't use the i as it is the grade and if you have a newer compiler, I'd recommend a for each loop instead. Those are usually the way to go, just happens that you have one of the rarer cases in which it isn't.)
Likewise, you first set the size of your vector and then increase it, which to me means that you do not trust it:
vector<int> ptrV(grade_max, 0);
At this point, you have a vector with a hundred entries that are all zero. You don't need to resize it later if a hundred entries is all you need. vector::push_back resizes it. But note that setting it to a size of 100 means that [100] is not a valid position, the last one is [99], as we start to count at zero. You'd need to set it to a size of 101 to have both zero and hundred as valid addresses.
I'd change your code to:
const int grade_max = 100;
vector<int> ptrV(grade_max+1, 0); //changed it to +1 here as prtV[100] should be legal
cout << "Enter the student's grades (-1 to stop entering): \n";
while (true)
{
int grade; // put stuff in the smallest scope possible
cin >> grade;
if(grade == -1) break; // doing that here means we don't have to think about it anymore - the do while does it at last, I do it at first, handling all the special cases at the start and then assume I have the regular case.
if(grade < 0 or grade > grade_max) continue; // continue jumps to the top of the most inner loop. Note that I make sure to catch illegal but possible input.
ptrV[grade] += 1; // personal preference, I use ++ only to iterate
}
displayHistogram(ptrV);
I rewrote the structure, using a while(true), I think the way I did it is more intuitive, but there will be people who disagree with that and who would also write things like
if(grade == -1)
{
break;
}
and there are some good arguments for that, mostly a good practice routine, always doing the braces to avoid errors. However, I prefer a one liner to reduce verbosity.
One improvement would also be to tell the user about bad input:
if(grade < 0 or grade > grade_max)
{
cout << "Input not in valid range. Please choose number within 0 to " << grade_max << endl;
continue;
}
Now, another big thing to do here would be to leave the procedural part, by the way.
Go for a class GradeHistogram which has all those functions as a part of it, being called like
GradeHistogram histogram;
histogram.take_input();
histogram.display();
but that is for when you get your code working.
(My answer became more of a review as found on CodeReview, but I think that this is what you need rather than small fixes. This is something I can only recommend you, by the way, putting your code on CodeReview once it works.)
but my goal is to store them in a vector.
The issue seems to be that you've already sized the vector to hold grade_max entries. However when filling the vector, you are using push_back. By using push_back, you are adding more entries to the end of the vector, which is not what you want to do.
The solution is either
Change this vector<int> ptrV(grade_max, 0); to this vector<int> ptrV; and leave the calls to push_back alone, or
Keep vector<int> ptrV(grade_max, 0); but instead merely use ptrV[i] = grade;
If what you want to show is a histogram, then the easiest thing to do would be to use a std::map from grade to count of grade.
Something like this:
#include <iostream>
#include <map>
int main() {
std::cout << "Enter the student's grades (-1 to stop entering): \n";
std::map<int, int> grades_map;
int input_grade = -1;
do {
cin >> input_grade;
if (input_grade > -1) {
grades_map[input_grade]++;
}
} while (input_grade != -1);
// Print histogram
for (const auto& [grade, count] : grades_map) {
std::cout << "Students with grade << grade << ": ";
for (int i = 0; i < count; ++i) {
std::cout << '*';
}
std::cout << '\n';
}
}

Run Time Check failure #2:

Below is my code so far. I am just doing this step by step, for a class assignment. Trying not to give the whole code I just want to know what is wrong with what I have so I can learn to fix it. Thanks for the help.
The goal is to create class driver then to get total lap times, compare them and put them in order of 1st thru 3rd place. I've made this in a simple program already. Now I am trying to make an array of the class driver.
So far this is working, It asks for input correctly, and gives an output but before the end of the program I get:
Debug Error!
Run Time Check Failure #2 - Stack around the variable 'driver' was corrupted.
None of the answers I've found here make sense to my application. I have 0 failures when I run the build before running the program.
I also know not to use single character variables, I just do this as I learn what I'm missing and later change them.
#include <iostream>
#include <string>
using namespace std;
class Driver
{
int carNumber;
public:
void setNumber(int number)
{
carNumber = number;
}
int getNumber()
{
return carNumber;
}
};
int main()
{
int x;
int i;
Driver driver[3];
for (i = 1; i <= 3; i++)
{
cout << "Enter Driver " << i << "'s car number: ";
cin >> x;
driver[i].setNumber(x);
}
for (i = 1; i <= 3; i++)
cout << "driver " << i << "'s number is " << driver[i].getNumber() << endl;
return 0;
}
start for loop as shown below because as we know that array stores n elements from location 0 to n-1.
for (i = 0; i <3; i++)
{
cout << "Enter Driver " << i << "'s car number: ";
cin >> x;
driver[i].setNumber(x);
}
Your array access indexes are wrong.
Arrays start at index 0, not 1, and end at 1 less than the number of elements. So for a declaration of driver[3], the valid indexes are 0, 1, and 2, not 1, 2 and 3. With the wrong indexes, you're writing past the end of the array, causing the stack corruption.
Your write loop should be more like
for (i = 0; i < 3; i++)
{
cout << "Enter Driver " << i << "'s car number: ";
cin >> x;
driver[i].setNumber(x);
}
You'll need to make a similar fix to the read loop.

Functions in C++ - The 13 Sone game

I am trying to write a program that requires input validation through functions. The idea behind it is much like the 21 stones only it is with 13 and the computer always wins. The game starts with 13 stones and the computer will always choose 1 on the first turn creating a multiple of 4 scenario. This means if the user takes 3 computer takes 1, user takes 2 computer takes 2 and so on until no stones remain. My problem is I am having a hard time getting my head around functions and how data is called from the parameters within so any help with this would be greatly appreciated!
This is what I have sofar.
#include <iostream>
using namespace std;
//function prototypes
bool validPick(int numStones);
int computerPick(int stones_in_pile, int player2taken);
int playerPick(int stones_in_pile);
int main()
{
int stones_left = 13, P1Taken, P2Taken;
cout << "You have shosen to play the game 13 stones against me, the MIGHTY "
<< "COMPUTER!\nThe object of the game is to take 1, 2 or 3 stones from"
<< " the pile on your turn.\nThe player that removes the last stone "
<< "or stones from the pile wins the game.\nGood Luck... You will need"
<< " it! I NEVER LOOSE!!"
<< endl << endl;
computerPick(stones_left, P2Taken);
playerPick(P1Taken);
validPick(stones_left);
//game logic here -- This is far from done.
stones_left -= P1Taken;
stones_left -= P2Taken;
return 0;
}
/******************************************************************************\
* Validate the picked number 1-3 are only valid numbers to choose from. *
\******************************************************************************/
bool validPick(int numStones)
{
if((numStones < 1) || (numStones >3))
cout << "Invalid Selection. 1-3 is all you can have!";
else
return numStones;
}
/******************************************************************************\
* Computer's function calls. Should start with 1. We always want the computer *
* to win the game. *
\******************************************************************************/
int computerPick(int stones_in_pile, int player2taken)
{
if(player2taken == 0)
stones_in_pile -= 1;
else
{
if(player2taken == 1)
stones_in_pile -= 3;
else
if(player2taken == 2)
stones_in_pile -= 2;
else
stones_in_pile -=1;
}
return stones_in_pile;
}
/******************************************************************************\
* Player's Pick function call goes here. The player goes second *
\******************************************************************************/
int playerPick(int stones_in_pile)
{
cout << "Please choose the ammount of stones. 1-3 only! : ";
cin >> stones_in_pile;
return stones_in_pile;
}
Despite the fact that you should better read a beginners book than trying to understand C++ by asking such questions, I will try to explain what is wrong in your code by an example:
bool validPick(int numStones) {
if((numStones < 1) || (numStones >3))
cout << "Invalid Selection. 1-3 is all you can have!";
else
return numStones;
}
This function is declared to return a bool value. However, if the condition in the if-clause turns out to be true, the function does not return anything, that is a mistake. Second, numStones is an int, so when you return it as a bool it will get converted (from int to bool) which is probably not what you want. Honestly, I didnt even try to understand the logic of your program but a valid version of this function could look like this:
bool validPick(int numStones) {
if((numStones < 1) || (numStones >3)) {
cout << "Invalid Selection. 1-3 is all you can have!";
return false;
}
return true;
}
There are many philosophies with functions that produce values and how those values are passed back.
Functions can pass values back to the caller by either modifying the parameter or by returning a value.
The playerPick function can either modify the passed value (by passing by reference):
void playerPick(int& stones_in_pile)
{
cout << "Please choose the ammount of stones. 1-3 only! : ";
cin >> stones_in_pile;
}
Or by returning a value:
int playerPick(void)
{
// Local variable to temporarily hold a value.
int stones_in_pile = - 1;
cout << "Please choose the ammount of stones. 1-3 only! : ";
cin >> stones_in_pile;
return stones_in_pile;
}
Note that the latter version uses a local, temporary, variable and the compiler will return a copy of the value upon end of the function.
I'm using void in the parameter for emphasis here.

Error dealing with uninitialized array of pointers C++

I have five files: class T, class M (an abstract class), class MC (a container), class AC (creates a particular object that is added into the MC container) and my Main file.
I have these functions to add an object (for this case, AC) and to retrieve a data member that you find in AC (a title).
The program compiles and it appears that I can create and add an AC object. However when I try to use my GetTitle function, the program crashes and I get the following error
“Unhandled exception at 0x00b938e6 in TLab 5.exe: 0xC0000005: Access
violation reading location 0xcccccce4.”
From what I looked up, this means I have a pointer that is bad/uninitialized. The only pointer in my program is this:
M *C[MCSize] //Found in MC.h
The constructor for MC looks like this:
MC::MC()
{
cout << "Enter Name: ";
getline(cin, CName);
cout << "Enter size of collection: ";
cin >> CurrentMCSize;
if (CurrentMCSize < 0 || CurrentMCSize > MCSize)
{
cout << "Size is invalid. Please re-enter: ";
cin >> CurrentMCSize;
}; //MCSize is defined in the header of MC.
The function to call the Title that is entered is here:
void MC::ListMTitles()
{
for (int i = 0; i < CurrentMCSize; i++)
{
cout << i << ". " << Collection[i]->GetTitle();
}
};
//GetTitle is defined in M.cpp
Where DMA occurs: //MC.cpp
void MC::AddM()
{
int Selection;
if(CurrentMCSize < MCSize)
{
DisplayMTypeMenu();
Selection = GetMTypeSelection();
switch(Selection)
{
case 1: Collection[CurrentMCSize] = new AC;
break;
// Other case statements
}
if (0 == Collection[CurrentMCSize])
{
cout << "Error: Memory Allocation Failed.";
exit(1);
}
else
{
cout << "New M Type added!" << endl << endl;
}
CurrentMCSize++;
}
Have I not properly initialized my pointer? Is my Add function actually lying to me and nothing is being added? I looked around but most answers I saw involved using a vector, which for the sake of this project I don’t think I’m allowed to use as the professor didn’t go over them.
You are asking the user to input the size of the collection during construction, but you never populate those elements of the collection. Then, when you call AddM, it continues from CurrentMCSize. You should instead initialize CurrentMCSize to zero in the constructor and not ask for it at all.
The problem occurs because this
for (int i = 0; i < CurrentMCSize; i++)
{
cout << i << ". " << Collection[i]->GetTitle();
}
starts at 0 but there is no guarantee that AddM() will begin adding at 0:
void MC::AddM()
{
int Selection;
if(CurrentMCSize < MCSize)
{
DisplayMTypeMenu();
Selection = GetMTypeSelection();
switch(Selection)
{
case 1: Collection[CurrentMCSize] = new AC;
Instead it will add at whatever CurrentMCSize is which could be fed into the constructor as something like 4. You have three separate values you want to track: max supported size, the size presently used and the next slot to allocate an item but you've collapsed the last two into one variable.
Related question - Any reason you don't want to simply use a std::vector and push_back?
Edit: Ah I didn't see it, Paddy beat me to it.