Receiving the "vector iterators incompatible" error - c++

I am creating an Uno game and I am stuck trying to search through an a vector of objects by specific attributes. It crashes when the program reaches the Game::player_selection() method. Other than the , everything is working.
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
class Card {
public:
string get_colour();
string get_type();
};
class Player {
public:
vector<Card*> get_hand(); //Should this be a reference?
private:
vector<Card*>current_cards;
};
int main() {
srand(time(NULL)); //Makes shuffle more random (different every time)
Game my_game;
Player new_player("Hank", "human");
Player jack_player("Jack", "human");
my_game.add_player(new_player); //Must create players before doing other tasks
my_game.add_player(jack_player); //Must create players before doing other tasks
my_game.setup_game();
my_game.display_players();
cout << "enter colour" << endl;
cin >> colour;
cout << "enter type" << endl;
cin >> type;
my_game.play_card_to_pile(my_game.player_selection("Jack", colour, type));
my_game.display_players();
Game.cpp
Card* Game::player_selection(string p_name, string colour, string type){
vector<Player>::iterator p_iter;
vector<Card*>::iterator c_iter;
p_iter = find_if (game_players.begin(), game_players.end(), [&] (Player& p) -> bool{return p.get_name() == p_name;}); //Finds correct player
c_iter = find_if(p_iter->get_hand().begin(), p_iter->get_hand().end(), [&] (Card*& c) -> bool{return c->get_colour() == colour && c->get_type() == type;}); //Finds correct card
return (*c_iter);//Should return correct card
}
Error given
Edit
Just posting here for future reference regarding find_if checks and multiple copies of vectors. So the solution was:
Card* Game::player_selection(string p_name, string colour, string type){
vector<Player>::iterator p_iter;
vector<Card*>::iterator c_iter;
p_iter = find_if (game_players.begin(), game_players.end(), [&] (Player& p) -> bool{return p.get_name() == p_name;});
if (p_iter != game_players.end()){
vector<Card*> hand = p_iter->get_hand();//Otherwise have multiple copies of the vector stored in different places in memory
c_iter = find_if(hand.begin(), hand.end(), [&] (Card*& c) -> bool{return c->get_colour() == colour && c->get_type() == type;});
if (c_iter != hand.end()){
return (*c_iter); //If found return found Card
}
}
return (get_pile_card()); //Else return a default
}

The problem is that get_hand is returning a vector by value, so the 2 calls to get_hand are creating different vectors. You could return a reference to the vector, or just call get_hand once:
vector<Card*> hand = p_iter->get_hand();
c_iter = find_if(hand.begin(), hand.end(), ...
You should also check the result of both calls to find_if to make sure they actually found an item satisfying the predicate.

Related

Using STL functions with a struct?

I've recently read up on STL functions in C++. I understand the basic uses of the functions, but I am struggling getting them to use member variables of a struct.
I have this struct:
struct Apples
{
double weight; // oz
string color; // red or green
void print() const { cout << color << ", " << weight << endl; }
};
Basically, I insert Apples into a vector storing random weights and random color. Now, I want to use a count_if function to determine how many apples are greater than a given weight. I want to convert a function like this:
int cnt = 0;
for(auto it = crate.cbegin(); it != crate.cend(); ++it)
if(it->weight > toFind)
cnt++;
to a count_if() version (this does not work):
int cnt = count_if(crate.begin(), crate,end(), isGreater())
With isGreater() being like this:
void isGreater()
{
if(it->weight > toFind)
return it->weight > toFind;
}
What I don't understand about STL functions and a struct is how to use the member variables inside of the struct with the STL functions. I'm not sure what to pass inside of the STL function, either. Would it be better to use a lambda function in this case? If so, why?
Here is all the current code, if it doesn't make sense:
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
#include <deque>
#include <string>
using namespace std;
struct Apples
{
double weight; // oz
string color; // red or green
void print() const { cout << color << ", " << weight << endl; }
};
void isGreater()
{
if(it->weight > toFind)
return it->weight > toFind;
}
int main()
{
srand(time(nullptr));
const double minWeight = 8.;
const double maxWeight = 3.;
cout << "Input crate size: ";
int size;
cin >> size;
vector <Apples> crate(size);
for(auto it = crate.begin(); it != crate.end(); ++it)
{
it->weight = minWeight + static_cast<double>(rand())/RAND_MAX*(maxWeight - minWeight);
it->color = rand() % 2 == 1 ? "green" : "red";
}
cout << "Enter weight to find: ";
double toFind;
cin >> toFind;
//this is what I want to convert to count if
int cnt = 0;
for(auto it = crate.cbegin(); it != crate.cend(); ++it)
if(it->weight > toFind)
cnt++;
std::count_if takes unary predicate as the third argument. In this case unary predicate is a function taking one object and returning true if object matches find criterion or false if not.
Since your criterion depends on toFind, it seems more laconic to use lambda capturing toFind:
int cnt = count_if(crate.begin(), crate.end(), [toFind](const Apple& apple) {
return it->weight > toFind;
});
If you want a standalone function, you can use:
bool isGreater(double toFind, const Apple& apple) {
return it->weight > toFind;
}
...
int cnt = count_if(crate.begin(), crate.end(),
std::bind(&isGreater, toFind, std::placeholders::_1));
Note, that you don't need to call function, you need to pass it:
int cnt = count_if(crate.begin(), crate,end(), isGreater())
// ^^ remove parentheses
you are not storing the apples in the vector.
you have to initialize inside a loop each apple and then store them in the vector.
crate.push_back(newApple).
so run a loop from 0 to size.
inside that loop initialize new apples and give them weights and colors
then push_back in vector:
for(int i = 0; i < size ++i)
{
apples newApple;
newApple.weight = ...;
newApple.color = ...;
crate.push_back(newApple);
}
This is usually accomplished by creating a "functor" class, a class whose objects can be called like a function. Each instance call hold the reference weight:
struct IsGreater {
double w;
IsGreater(double weight) : w{weight} {}
bool operator()(const Apples& A) const {
return A.weight > w;
}
};
Then we just need to create an instance of the class holding the reference weight and pass it to count_if:
const int count = std::count_if(crate.begin(), crate.end(), IsGreater(toFind));
You can avoid creating an explicit class using a lambda:
const int count = std::count_if(crate.begin(), crate.end(),
[=](const Apples& A) -> bool {
return A.weight > toFind;
});
Here the reference value toFind is captured by value.

How to reset number of instances of a class to 1 on every loop iteration?

I have a player class where I am storing the player's current position, the number of players in the game and a static variable to store the total number of players like so:
#ifndef PLAYER_H
#define PLAYER_H
#include <ctime>
#include <random>
#include <iostream>
using std::time;
using std::cout;
class Player
{
private:
int m_Player_currentPosition, m_Player_number;
static int m_Player_numberOfPlayers;
public:
Player::Player():m_Player_currentPosition(1) {
m_Player_number = m_Player_numberOfPlayers;
++m_Player_numberOfPlayers;
}
void m_Player_SetPosition();
int m_Player_GetPosition();
int m_Player_GetPlayerNumber() { return m_Player_number; }
void m_Player_SetNumberOfPlayers() { m_Player_numberOfPlayers = 1; }
~Player() { --m_Player_numberOfPlayers; }
};
int Player::m_Player_numberOfPlayers = 1;
#endif
I also have a game class that creates a certain number of player instances using a vector. In my game class, the plan is to create players depending on user input (between 2-4 number of players) using m_Game_SetPlayers() member function and also printing the details of the players using the m_Game_PrintPlayers() member function.
#ifndef GAME_H
#define GAME_H
#include <iostream>
#include "Board.h"
#include "Player.h"
using std::cout;
using std::cin;
using std::vector;
class Game {
private:
bool m_Game_quit;
int m_Game_choice;
Board board;
vector<Player> m_Game_players;
public:
Game();
const bool &m_Game_GetQuit() const;
void m_Game_SetPlayers()
{
int numberOfPlayers = 2;
cout << "How many players (2-4)? ";
cin >> numberOfPlayers;
if (numberOfPlayers < 2 || numberOfPlayers > 4) {
numberOfPlayers = 2;
}
m_Game_players.resize(numberOfPlayers);
}
void m_Game_PrintMenu();
void m_Game_PrintInstructions();
void m_Game_GetChoice();
void m_Game_PrintPlayers()
{
cout << '\n';
vector<Player>::iterator iter;
for (iter = m_Game_players.begin(); iter != m_Game_players.end(); ++ iter) {
cout << "Player " << iter->m_Player_GetPlayerNumber() << "'s position: " << iter-
>m_Player_GetPosition() << '\n';
}
}
void Update();
};
#endif // !GAME_H
However, in my main class, I am calling the Game class's update function under a while loop. Here is my game update member function declared in a separate implementation file that decides the control flow of the game.
void Game::Update()
{
m_Game_GetChoice();
switch (m_Game_choice) {
case 0: cout << "---Bye---\n";
m_Game_quit = true;
break;
case 1:
system("cls");
m_Game_PrintInstructions();
break;
case 2:
system("cls");
m_Game_SetPlayers();
system("cls");
board.m_Board_PrintBoard();
m_Game_PrintPlayers();
m_Game_players[0].m_Player_SetNumberOfPlayers();
break;
default:
cout << "--Invalid Option---\n";
break;
}
}
Here is my while loop in the main function:
#include "Game.h"
int main() {
Game game;
while (!game.m_Game_GetQuit()) {
system("cls");
game.m_Game_PrintMenu();
game.Update();
system("pause");
}
}
When I ran this program the first time, it worked as expected. However, imagine if I choose the play option from the menu and I enter 2 players, it creates 2 instances of the player class. On the next while loop iteration, I increase the size to 4 players which also works perfectly sometimes. Then, when I reduce the size and then again increase the size, the player number does not match. Here are the following images to help understand the problem:
Input 1: https://i.stack.imgur.com/reHjE.png
Output 1: https://i.stack.imgur.com/Dt68V.png
Input 2: https://i.stack.imgur.com/Xo83c.png
Output 2: https://i.stack.imgur.com/2Qso6.png
The expected output is:
Player's position 1: 1
Player's position 2: 1
Player's position 3: 1
So, I thought that I need to delete my instances, but since I cannot delete instances on a stack memory as long as I am in a while loop (How do I manually delete an instance of a class?, http://www.cplusplus.com/forum/beginner/107822/). I thought that I will resize the vector. It did resize the vector, but then it does not delete the instances of the player class but instead created a new instance of that class. Is there a way to destroy all the instances of the class on a stack memory even when it is inside the scope? If not, how do I solve this problem?
I may/may not have provided the code needed to debug this problem. So, I have attached my entire code on https://github.com/F3INTH34RTED/Cpp/tree/master/Beginner/16SnakesAndLadder if need be.
When you increase the size of the vector, say from 2 to 3, it only needs to create one new instance of Player, so it will create a single player with the next number.
The line
m_Game_players[0].m_Player_SetNumberOfPlayers();
on the previous loop iteration sets the global counter to 1. So this single new player gets number 1, not number 3 like you expect. You should be able to remove the above line and things will work as expected.
On a design note, it would probably be wiser to recreate the vector entirely when the number of players is changed and explicitly give each player a number via the constructor, like this:
void m_Game_SetPlayers()
{
int numberOfPlayers = 2;
cout << "How many players (2-4)? ";
cin >> numberOfPlayers;
if (numberOfPlayers < 2 || numberOfPlayers > 4) {
numberOfPlayers = 2;
}
m_Game_players.clear();
for (int i = 1; i < numberOfPlayers; i++) {
m_Game_players.push_back(Player(i));
}
}
Updating the Player constructor to match, of course.
Your issue is that std::vector::push_back will do copy/move when it needs to resize internal buffer, and your (auto generated default) copy/move constructors doesn't handle that, you might do for example:
class Player
{
private:
int m_currentPosition;
std::optional<int> m_number;
static int m_numberOfPlayers;
public:
Player() : m_currentPosition(1), m_number(++m_numberOfPlayer) {}
Player(const Player&) = delete;
Player(Player&& rhs) : m_currentPosition(rhs.m_currentPosition), m_number(rhs.m_number) { rhs.m_number = std::nullopt; }
Player& operator = (const Player&) = delete;
Player& operator = (Player&& rhs) { std::swap(m_currentPosition, rhs.m_currentPosition); std::swap(m_number, rhs.m_number); }
int GetPlayerNumber() const { return *m_number; }
~Player() { if (m_number) --m_numberOfPlayers; }
};

Terminate called after throwing an instance of 'std::bad_alloc' while using standard vector functions

I'm working on an assignment which asks us to model an 'elimination voting' procedure for a reality game. For that we use one class (Vote) for recording the name of the player to be eliminated and another (Voting) which doesn't get instanced anywhere, because its members are static (we need them to be). The votes are stored on a vector inside the Voting class.
The problem is that even when I use the push_back function to store the votes in the vector, after running the program for a few times I get an std::length_error with what(): basic_string::_M_create. After I close it and rerun, the first time I get an error like the one on the title (std::bad_alloc).
Any ideas? I'm suspecting that it has to do with the vector not deallocating memory correctly, but I'm not sure.
My code (Voting.h):
#ifndef VOTING_H_INCLUDED
#define VOTING_H_INCLUDED
#include <vector>
#include <map>
#include "Vote.h" //ignore this one
#include "Team.h" //this one too
class Voting
{
public:
static vector <Vote> votes; //votes cast during voting procedure
static map <string, int> results; //voting results (key: name, value: number of votes)
static void votingProcess(Team &team); //the whole voting procedure
};
#endif // VOTING_H_INCLUDED
Voting.cpp:
#include <vector>
#include <map>
#include <cstdlib>
#include "Vote.h"
#include "Voting.h"
using namespace std;
vector <Vote> Voting::votes(1); //initialize the vector for the votes
void voteCast(Team &team);
void Voting::votingProcess(Team &team)
{
voteCast(team);
}
void voteCast(Team &team)
{
int playerNumber = team.getNumberOfPlayers(); //number of players right now still in the team
int votingPlayer = 0; //index of the currently voting player
int votedPlayerIndex = -1; //index of the player to be voted for elimination
int reasonIndex = -1; //index of the selected reason for voting
Vote draft; //temporary empty vote
srand(time(NULL)); // initialize the RNG
Voting::votes.clear(); //clear the vector of any past votes
string reasons[4] = {"Den ta pame kala metaxi mas", "Einai ikanoteros/i apo emena kai ton/ti theoro apeili", "Den exei na prosferei kati stin omada", "Prospathei sinexeia na sampotarei tis prospatheies mas"};
//some reasons for elimination (i've tried smaller strings and even chars and it didn't work, so don't bother)
do
{
if (team.getPlayers()[votingPlayer].getAge() != 0 && team.getPlayers()[votingPlayer].getVotes() > 0)
//if the player with index votingPlayer has not been eliminated and has still votes to cast
{
do
{
votedPlayerIndex = rand() % 11; //select a random player for elimination
reasonIndex = rand() % 5; //select a random reason for it
}
while ((team.getPlayers()[votedPlayerIndex].getAge() == 0) || (votedPlayerIndex == votingPlayer) || (team.getPlayers()[votedPlayerIndex].getImmunity() == true));
// selection continues if the selected player has already been eliminated,
//if the selected player is the same as the voting player or if the selected player has immunity from elimination
team.getPlayers()[votingPlayer].setVotes(team.getPlayers()[votingPlayer].getVotes() - 1); //reduce the player's available votes by 1
draft.setVotedPlayer(team.getPlayers()[votedPlayerIndex].getName()); //write the name of the player to be voted in an empty Vote object
draft.setReason(reasons[reasonIndex]); //and the reason too
Voting::votes.push_back(draft); //push the Vote obj. in the vector
}
else
{
votingPlayer++; //ignore and get to the next player
}
}
while (votingPlayer < playerNumber); //vote casting continues until every player has casted a vote
}
Vote.h:
#ifndef VOTE_H_INCLUDED
#define VOTE_H_INCLUDED
#include <string>
#include <iostream>
using namespace std;
class Vote
{
string voted; //name of the player to be eliminated
string reason; //reason for elimination
public:
Vote() { voted = ""; reason = ""; } //constructor without parameters
Vote(string player, string reason) { voted = player; this -> reason = reason;} //constructor with parameters (this one is used)
~Vote() { cout << "Vote object destroyed" << endl; }; //destructor
string getVotedPlayer() { return voted; } //getters
string getReason() { return reason; }
void setVotedPlayer(string player) { voted = player; } //setters
void setReason(string reason) { this -> reason = reason; }
void status() { cout << "Voted player: " << voted << endl << "Reason: " << reason << endl;} //status function
};
#endif // VOTE_H_INCLUDED
The reason is that you have an array with 4 elements (so only indices 0..3 are valid) and you are allowing yourself to index it with the 5 possible values (0..4), where 4 is not a valid index.
Here is the definition of the array:
string reasons[4] = {"Den ta pame kala metaxi mas", "Einai ikanoteros/i apo emena kai ton/ti theoro apeili", "Den exei na prosferei kati stin omada", "Prospathei sinexeia na sampotarei tis prospatheies mas"};
Here is the selection of the index:
reasonIndex = rand() % 5;
Here is the use of that index:
draft.setReason(reasons[reasonIndex]);
That is why you are seeing an error from basic_string::_M_create because you are copying from a value that you have no idea is actually a string.
In the line that sets reasonIndex just change the 5 to a 4.

C++ Graph data Structure

I am writing a graph implementation in c++ where the cities are vertices, flights from one city to another city represent an edge, and the weights are the distances between those cities. The vertices,edges, and weights are stored in a file and when the program runs it would load the vertices, edges, and weights into the program. I am using an adjacency matrix where it represents the edges.
Now when the program runs it will prompt the user to either:
Choose Departure city
Exit. Option two just terminates the program.
If the user chooses option one, then it will list all the cities from the file. There a seven cities I chose. So it will look like 1.)Los Angeles 2.) New York 3.) Miami and so on until option 7. When the user chooses an option it will then list all the destination cities except the departure city the user chose. There would be three possibilities once the user chooses his destination city.
Now the first possibility would be there would be no direct or through connection between city A and city B and the program will output, No destination between [departure city] and [destination city] , press any key to return. Once the user presses any key, the menu will display again. The Second possibility would be if there is a direct connection between the city then the program would output the direct connection between [departure city]-[destination city] = [miles] and the miles between the city, or if there isn't a direct connection it will say no direct connection and the user can go back to the menu.
The third possibility would be that there would be a through connection and it will show the departure city and destination city with all the cities between them and the total miles it covers between them and the user can press any key to return to the menu.
Now the problem I"m having is getting the info from the file, I can't figure out how to get the info from the file or how to write the file so the program knows which are the vertices,edges,and weights. Also, how to display the cities and which have the direct connection, through connection, or no connection at all.
include <iostream>
#include <fstream>
#pragma once
const int NULL_EDGE = 0;
typedef std::string String;
class GraphType
{
private:
int edges[50][50];
int m_numVertices;
int m_maxVertices;
int m_distance;
String* m_vertices;
bool* marks; // marks[i] is the mark for vertices[i]
int IndexIs(String*, String);
public:
GraphType();
~GraphType();
bool isEmpty() const;
bool isFull(); //to do
int GetWeight(String, String); // to do
void ClearMarks(); // to do
void MarkVertex(String) // to do
bool isMarked(String) // to do
void addVertex(String);
void addEdge(String, String, int);
void displayCities();
};
#include "GraphType.h"
GraphType::GraphType()
{
m_maxVertices = 50;
m_distance = 0;
m_vertices = new String[m_maxVertices];
marks = new bool[50];
std::ifstream loadFile;
loadFile.open("load.txt");
if (loadFile.fail())
std::cout << " Error opening load.txt\n";
else
{
//stuck here
}
loadFile.close();
}
GraphType::~GraphType()
{
delete[] m_vertices;
delete[] marks;
}
int GraphType::IndexIs(String* vertices, String vertex)
{
int index = 0;
while (!(vertex == m_vertices[index]) == 0)
index++;
return index;
}
void GraphType::addVertex(String vertex)
{
m_vertices[m_numVertices] = vertex;
for (int i = 0; i < m_numVertices; i++)
{
edges[m_numVertices][i] = NULL_EDGE;
edges[i][m_numVertices] = NULL_EDGE;
}
m_numVertices++;
}
void GraphType::addEdge(String startVertex, String destVertex, int weight)
{
int row;
int col;
row = IndexIs(m_vertices, startVertex);
col = IndexIs(m_vertices, destVertex);
edges[row][col] = weight;
}
void GraphType::displayCities()
{
//stuck here
}
bool GraphType::isEmpty() const
{
return (m_numVertices == 0);
}
#include "GraphType.h"
int FlyMenu();
void CitiesMenu(GraphType&);
int main()
{
int choose;
GraphType gt;
do
{
choose = FlyMenu();
switch (choose)
{
case 1: CitiesMenu(gt);
break;
case 2:
break;
default: std::cout << " Invalid Input\n";
break;
}
} while (choose != 2);
return 0;
}
int FlyMenu()
{
int option;
std::cout << " 1.) Choose Depature City\n";
std::cout << " 2.) Exit\n";
std::cout << " Enter option: ";
std::cin >> option;
return option;
}
void CitiesMenu(GraphType& gt)
{
gt.displayCities();
}
I know about the depth Traversal and breadth Traversal algorithms to see if there is a connection between a city or not, but I don't know how to implement them for this scenario. I can't use the Standard Template Library, only std::vector. I was thinking about writing another class, but I don't know what what that class will help me with.
From what I got you have two questions:
read / write from file: the simplest solution would be freopen.
freopen("input","r",stdin)
freopen("output","w",stdout)
now every cin & cout operation will be done on the files you defined on freopen
implementing DFS / BFS: I will code the simplest type of DFS for you and you have to edit it to suite you program.
bool visit[MAX_CITY + 10];
void DFS(int x){
visit[x] = 1;
for (int i=1;i<=MAX_CITY;i++){
int nx = i;
if (adj[x][i] == -1) continue; // a cost of -1 means no flight
if (visit[nx]) continue;
DFS(nx);
}
}

C++ Passing map from object

I am building a simple program to 'learn as I go', this program takes a couple of text files and processors them, each line of the first text file is information about a person, and for each line an Object (of type Student (see below)) is created and added to a vector.
Within the Student objects is a map that stores that students marks.
I have a function within the student class that returns the map when called (or at least thats what im trying to do).
Currently this function is:
marksType Student::printMarks(){
return marks;
}
(Where marksType = std::map<string, float>)
and marks is the map of type marksType.
Then in my main function I have:
Student a = *qw;
studmarks = a.printMarks();
for (std::map<string, float>::iterator iter = studmarks.begin(); iter != studmarks.end(); iter++){
cout << "TEST" << endl;
}
Where qw is a pointer to a student object and studmarks is of type map<string, float>
The issue is that the cout doesn't get called so the iterator seems to skip (but the student object does have items in the marks map).
Heres the complete Student class
#include "Student.h"
using namespace std;
typedef std::map<string, float> marksType;
Student::Student(const string &name, int regNo) : Person(name){
marksType marks;
this->regNo = regNo;
}
int Student::getRegNo() const{
return regNo;
}
void Student::addMark(const string& module, float mark){
pair<marksType::iterator,bool> check;
check = marks.insert (pair<string,float>(module,mark));
if (check.second==false){
marks[module]=mark;
}
}
float Student::getMark(const string &module) const throw (NoMarkException){
if (marks.find(module) != marks.end()){
return marks.find(module)->second;
}
else throw NoMarkException();
}
float Student::getAverageMark() const throw (NoMarkException){
if (!marks.empty()){
float avgmark = 0;
for (marksType::const_iterator avgit=marks.begin(); avgit!=marks.end(); ++avgit){
avgmark = avgmark + avgit->second;
}
avgmark = avgmark/marks.size();
return avgmark;
}
else throw NoMarkException();
}
marksType Student::printMarks(){
return marks;
}
Oh and below is the part of the main function that adds marks to the students,
for (vector<Student>::iterator it = students.begin(); it != students.end(); ++it){
Student b = *it;
if (regno == b.getRegNo()){
found = true;
b.addMark(module, mark);
}
}
I know this works because when I use the getMark function it does work.
You are "adding marks" to copies of the students stored in vector students. Each of these copies only lives during one iteration of the loop and the result is that you are not modifying the vector's elements at all:
for (vector<Student>::iterator it = students.begin(); it != students.end(); ++it){
Student b = *it; // b IS A COPY
if (regno == b.getRegNo()){
found = true;
b.addMark(module, mark); // modify local copy of Student
}
}
To add them to the elements of the vector, use
for (vector<Student>::iterator it = students.begin(); it != students.end(); ++it){
if (regno == it->getRegNo()){
found = true;
it->addMark(module, mark);
}
}