What is wrong with this code? It detects whether I've reached the coordinates of a monster but only one of them, or the closest one at least. If I travel to other coordinates it doesn't tell me the monster has appeared. Don't know why because I am looping through the monster vector every time I type 'north'.
Here is the code.
Monster Class:
class Monster
{
public:
std::vector<std::string> names;
std::vector<double> posx; // North - South
std::vector<double> posy; // East - West
bool compareCoords(double monsterPosX, double monsterPosY);
void cMonster(std::string monsterName, double monsterPosX, double monsterPosY);
void randomSpawn();
protected:
private:
};
compareCoords to detect whether a monster already exists at those coordinates and the cMonster (create monster) functions:
void Monster::cMonster(std::string monsterName, double monsterPosX, double monsterPosY)
{
if (compareCoords(monsterPosX, monsterPosY) == false)
{
names.push_back(monsterName);
posx.push_back(monsterPosX);
posy.push_back(monsterPosY);
std::cout << "Monster " << monsterName << " has been created at X: " << monsterPosX << " Y: " << monsterPosY << std::endl;
}
}
bool Monster::compareCoords(double monsterPosX, double monsterPosY)
{
for (unsigned int i = 0; i < posx.size(); i++)
{
if (monsterPosX == posx[i] && monsterPosY == posy[i])
{
return true;
}
}
return false;
}
Main:
int main()
{
srand(time(0));
Monster newenemy;
Character newplayer;
newenemy.cMonster("Weezo", 1, 0);
newenemy.cMonster("Weezo", 2, 0);
newplayer.posx.push_back(0);
newplayer.posy.push_back(0);
home:
std::cout << "-->> ";
std::string userInput;
std::cin.clear();
getline(std::cin, userInput);
if (!userInput.compare("north"))
{
newplayer.headNorth();
for (unsigned int i = 0; i < newenemy.names.size(); i++)
{
if (newplayer.posx[i] == newenemy.posx[i] && newplayer.posy[i] == newenemy.posy[i])
{
std::cout << "A " << newenemy.names[i] << " has appeared." << std::endl;
}
}
}
else if (!userInput.compare("south"))
{
newplayer.headSouth();
for (unsigned int i = 0; i < newenemy.posx.size(); i++)
{
if (newplayer.posx[i] == newenemy.posx[i] && newplayer.posy[i] == newenemy.posy[i])
{
std::cout << "A " << newenemy.names[i] << " has appeared." << std::endl;
}
}
}
else
{
std::cout << "You have entered an invalid command." << std::endl;
}
}
^ As you can see here, it shows when I'm at the coordinates of Gorilla but not the second monster, Donald Trump. It just ignores the second one.
I've been stuck here for hours, I don't understand what could be wrong. Thank you!
Related
I am trying to build a simple dungeon crawl and am stuck at the battle. The damage taking is working but I cannot return the new health value so that it decreases past the initialized value. Every time the l;oop repeats it returns to the initial value. The same with treasure. What gives? How can I return a value from a member function to main?
#include <iostream>
#include <conio.h>
#include <string.h>
#include <stdlib.h>
using namespace std;
class monster
{
public:
int fight()
{
}
};
class chest
{
int loot()
{
}
};
class movement
{
public:
char wasd()
{
char mv;
char up = 'w';
char left = 'a';
char right = 'd';
char down = 's';
cout << "\nMove using w for (up), a for (left), d for (right), and s for (down).\n\n";
mv = _getch();
if (mv == up)
{
cout << "You have moved up 1 space.\n";
}
else if (mv == left)
{
cout << "You have moved left 1 space.\n";
}
else if (mv == right)
{
cout << "You have moved right 1 space.\n";
}
else if (mv == down)
{
cout << "You have moved down 1 space.\n";
}
else
{
cout << "it didn't work.";
}
return 0;
}
};
class random_enc
{ public:
int treasure;
int health = 12;
public:
int encounter(int)
{
int randnumber = rand() % 5 + 1;
treasure = 0;
if (randnumber == 1)
{
cout << "You have been attacked!!! You lose 1 hitpoint." << endl;
health = --health;
cout << "Hitpoints remaining: " << health << endl;
return health;
}
else if (randnumber == 2)
{
cout << "You found treasure!" << endl;
treasure = ++treasure;
cout << "Treasure collected: " << treasure << endl;;
return random_enc::treasure;
}
else if (randnumber == 3)
{
return health;
}
else if (randnumber == 4)
{
cout << "You step on a trap and take damage!! You lose 1 hit point.\n" << "Good bye." << endl;
health = --health;
cout << "Hitpoints remaining: " << health << endl;
}
return health;
}
};
int main()
{
string name;
cout << "Welcome to the dungeon, enter your name:\n";
cin >> name;
cout << "\nGood luck in the dungeon " << name << "\n";
int health = 12;
while (health != 0)
{
movement mvmt;
random_enc random;
mvmt.wasd();
random.encounter(health);
}
cout << "You have been killed. Goodbye.\n";
return 0;
}
I replaced the argument health on the encounter function in random_enc. I replaced it with a pointer: void encounter (int& health)
This passes the reference rather than the value. Then health is defined in the member function.
#include <iostream>
using namespace std;
void encounter(int& health) // note the ampersand
{
--health; // this will change health in main
}
int main()
{
int health = 12;
encounter(health);
cout << health << '\n'; // prints 11
}
I asked this question a couple of hours ago; I want to see if someone can now explain the problem.
One code is about separating items in a grocery; in the end you'll have two(2) bags; a fragileBag and a normalBag.
Other code separates passengers depending on the office they go for pickup; in the end you'll have three(3) types of passengers; ones that go to rio, ones that go to maya, and ones that request elsewhere.
Both codes use the same logic but the passenger code gives an error on a line that works perfectly on the grocery code.
Just to be clear, BOTH CODES RETURN VALUES OF STRING.
ERROR FROM THE PASSENGER CODE:
Error (active) E0304 no instance of overloaded function "std::vector<_Ty,_Alloc>::push_back [with _Ty=trans, _Alloc=std::allocator<trans>]" matches the argument list dataPractice2 C:\Users\javye\source\repos\dataPractice2\dataPractice2\main.cpp 82
and also:
Error C2664 'void std::vector<trans,std::allocator<_Ty>>::push_back(_Ty &&)': cannot convert argument 1 from 'std::string' to 'const _Ty &' dataPractice2 c:\users\javye\source\repos\datapractice2\datapractice2\main.cpp 82
//GROCERY FUNCTION
//separate function
void separateItems(vector<myBag>& newMyVector) {
for (int x = newMyVector.size() - 1; x >= 0; --x) {
if (newMyVector[x].getItem() == "eggs" || newMyVector[x].getItem() == "bread") {
fragileBag.push_back(newMyVector[x].getItem()); //NO PROBLEM HERE
newMyVector.pop_back();
}
else {
normalBag.push_back(newMyVector[x].getItem()); //OR HERE
newMyVector.pop_back();
}
}
}
//PASSENGER FUNCTION
//separate function
void separateP(vector<trans>& newMyVector) {
for (int x = newMyVector.size() - 1; x >= 0; --x) {
if (newMyVector[x].getXoLoc() == "rio") {
rioLoc.push_back(newMyVector[x].getXoLoc()); //PROBLEM HERE
newMyVector.pop_back();
}
else
if (newMyVector[x].getXoLoc() == "maya") {
mayaLoc.push_back(newMyVector[x].getXoLoc()); //HERE
newMyVector.pop_back();
}
else
elseLoc.push_back(newMyVector[x].getXoLoc()); //HERE
newMyVector.pop_back();
}
}
//GROCERY FULL CODE
//HEADER
#pragma once
#include<iostream>
#include<vector>
#include<string>
using namespace std;
#ifndef BAG_H
#define BAG_H
class myBag {
public:
myBag(); //default constructor
myBag(string anItemName); //overload constructor
void addItem(string anItemName); //mutator
string getItem();//accessor
private:
string itemName;
};
#endif
//SOURCE
#include"bag.h"
myBag::myBag() {
addItem("");
}
myBag::myBag(string anItemName) {
addItem(anItemName);
}
void myBag::addItem(string anItemName) {
itemName = anItemName;
}
string myBag::getItem() {
return itemName;
}
//MAIN
#include"bag.h"
void inputItems(vector<myBag>&); //input data function prototype
void displayQuantity(vector<myBag>&); //display data function prototype
void separateItems(vector<myBag>&); //function that separates items; func prototype
void fragBag(vector<myBag>&); //fragile bag function prototype
void norBag(vector<myBag>&); //normal bag function prototype
vector<myBag> myVector; //main vector
vector<myBag> fragileBag, normalBag; //seconday vectors
string item; //global item variable
int main() {
int option;
try {
do {
cout << "\tMENU"
<< endl << "1) Input Items"
<< endl << "2) Display Quantity"
<< endl << "3) Separate (IMPORTANT)"
<< endl << "4) Display Items in Fragile Bag"
<< endl << "5) Display Items in Normal Bag"
<< endl << "6) Exit Program"
<< endl << endl << "Choose: ";
cin >> option;
if (option > 6) {
throw 404;
}
switch (option) {
case 1: //input
system("cls");
inputItems(myVector);
system("pause");
system("cls");
break;
case 2://display
system("cls");
displayQuantity(myVector);
system("pause");
system("cls");
break;
case 3: //separate
system("cls");
separateItems(myVector);
system("pause");
system("cls");
break;
case 4: //fragile
system("cls");
fragBag(myVector);
system("pause");
system("cls");
break;
case 5: //normal
system("cls");
norBag(myVector);
system("pause");
system("cls");
break;
case 6: //exit
exit(0);
}
} while (option != 6);
}
catch(int x){
cout << "ERROR, OPTION DOESN'T EXITS" << endl;
system("pause");
}
}
//input function
void inputItems(vector<myBag>& newMyVector) {
do {
cout << "Enter grocery items || enter letter X to stop: ";
cin >> item;
if (item != "x")
newMyVector.push_back(myBag(item));
} while (item != "x");
}
//display function
void displayQuantity(vector<myBag>& newMyVector) {
try {
for (int x = 0; x < newMyVector.size(); ++x) {
if (x == 0) {
cout << "Store bag has " << newMyVector.size() << " items in it. These are: " << endl;
}
cout << newMyVector[x].getItem() << endl;
}
if (newMyVector.empty())
throw 404;
}
catch (int x) {
cout << "ERROR " << x << " ,QUANTITY NOT FOUND" << endl;
}
}
//separate function
void separateItems(vector<myBag>& newMyVector) {
for (int x = newMyVector.size() - 1; x >= 0; --x) {
if (newMyVector[x].getItem() == "eggs" || newMyVector[x].getItem() == "bread") {
fragileBag.push_back(newMyVector[x].getItem()); //PROBLEM WOULD APPEAR HERE, BUT DOESN'T, UNLIKE THE OTHER CODE
newMyVector.pop_back();
}
else {
normalBag.push_back(newMyVector[x].getItem());
newMyVector.pop_back();
}
}
}
//fragile bag function
void fragBag(vector<myBag>& newMyVector) {
try {
for (int x = 0; x < fragileBag.size(); ++x) {
if (x == 0) {
cout << "The fragile bag has " << fragileBag.size() << " items in it. These are: " << endl;
}
cout << fragileBag[x].getItem() << endl;
}
if (fragileBag.empty()) {
throw 404;
}
}
catch (int x) {
cout << "ERROR " << x << " ,FRAGILE BAG EMPTY" << endl;
}
}
//normal bag function
void norBag(vector<myBag>& newMyVector) {
try {
for (int x = 0; x < normalBag.size(); ++x) {
if (x == 0) {
cout << "The normal bag has " << normalBag.size() << " items in it. These are: " << endl;
}
cout << normalBag[x].getItem() << endl;
}
if (normalBag.empty()) {
throw 404;
}
}
catch (int x) {
cout << "ERROR " << x <<" , NORMAL BAG EMPTY" << endl;
}
}
//PASSENGER FULL CODE
//HEADER
#pragma once
#include<iostream>
#include<vector>
#include<string>
using namespace std;
#ifndef TRANSPORT_H
#define TRANSPORT_H
class trans {
public:
trans();
trans(string aName, string anXoLoc, string anXfLoc, string aTime, string aCellNum);
void setName(string aName);
void setXoLoc(string anXoLoc);
void setXfLoc(string anXfLoc);
void setTime(string aTime);
void setCellNum(string aCellNum);
string getName();
string getXoLoc();
string getXfLoc();
string getTime();
string getCellNum();
private:
string name;
string xoLoc; //offices
string xfLoc; //destination
string time;
string cellNum;
};
//SOURCE
#include"transport.h"
trans::trans() {
setName("");
setXoLoc("");
setXfLoc("");
setTime("");
setCellNum("");
}
trans::trans(string aName, string anXoLoc, string anXfLoc, string aTime, string aCellNum) {
setName(aName);
setXoLoc(anXoLoc);
setXfLoc(anXfLoc);
setTime(aTime);
setCellNum(aCellNum);
}
void trans::setName(string aName) {
name = aName;
}
void trans::setXoLoc(string anXoLoc) {
xoLoc = anXoLoc;
}
void trans::setXfLoc(string anXfLoc) {
xfLoc = anXfLoc;
}
void trans::setTime(string aTime) {
time = aTime;
}
void trans::setCellNum(string aCellNum) {
cellNum = aCellNum;
}
string trans::getName() {
return name;
}
string trans::getXoLoc() {
return xoLoc;
}
string trans::getXfLoc() {
return xfLoc;
}
string trans::getTime() {
return time;
}
string trans::getCellNum() {
return cellNum;
}
#endif
//MAIN
#include"transport.h"
void inputInfo(vector<trans> &);
void displayInput(vector<trans>&);
void separateP(vector<trans>&);
void rio(vector<trans>&);
void maya(vector<trans>&);
void elsewhere(vector<trans>&);
vector<trans> myVector;
vector<trans> rioLoc, mayaLoc, elseLoc;
string newName;
string newXoLoc; //offices
string newXfLoc; //destination
string newTime;
string newCellNum;
//main not ready. Creating each function one by one to them make it look nice
int main() {
int option;
do {
cout << "MENU"
<< endl << "1) input "
<< endl << "2) output "
<< endl << "3) separate"
<< endl << "4) rio passengers"
<< endl << "5) maya passengers"
<< endl << "6) elsewhere passengers";
cin >> option;
switch(option){
case 1:
inputInfo(myVector);
break;
case 2:
displayInput(myVector);
break;
case 3:
separateP(myVector);
break;
case 4:
rio(myVector);
break;
case 5:
maya(myVector);
break;
case 6:
elsewhere(myVector);
break;
case 7:
exit(0);
}
} while (option != 7);
system("pause");
}
void inputInfo(vector<trans> &newMyVector) {
int charSize;
cout << "How many passangers to register: ";
cin >> charSize;
for (int x = 0; x < charSize; ++x) {
cout << "Name of passanger: ";
cin >> newName;
cout << "Office: ";
cin >> newXoLoc;
cout << "Destination: ";
cin >> newXfLoc;
cout << "Time of pickup: ";
cin >> newTime;
cout << "Cellphone: ";
cin >> newCellNum;
if (charSize != 0)
newMyVector.push_back(trans(newName, newXoLoc, newXfLoc, newTime, newCellNum));
}
}
void displayInput(vector<trans>& newMyVector) {
for (int x = 0; x < newMyVector.size(); ++x) {
if (x == 0) {
cout << "There are " << newMyVector.size() << " passengers. These are: " << endl;
}
cout << "-----------------------------Passenger #" << x + 1 << endl;
cout << newMyVector[x].getName() << endl;
cout << newMyVector[x].getXoLoc() << endl;
cout << newMyVector[x].getXfLoc() << endl;
cout << newMyVector[x].getTime() << endl;
cout << newMyVector[x].getCellNum() << endl;
}
}
void separateP(vector<trans>& newMyVector) {
for (int x = newMyVector.size() - 1; x >= 0; --x) {
if (newMyVector[x].getXoLoc() == "rio") {
rioLoc.push_back(newMyVector[x]);
newMyVector.pop_back();
}
else
if (newMyVector[x].getXoLoc() == "maya") {
mayaLoc.push_back(newMyVector[x]);
newMyVector.pop_back();
}
else
elseLoc.push_back(newMyVector[x]);
newMyVector.pop_back();
}
}
void rio(vector<trans>& newMyVector) {
for (int x = 0; x < rioLoc.size(); ++x) {
if (x == 0) {
cout << "Num. of passangers to pickup in Rio Piedras is " << rioLoc.size() << " , these are: " << endl;
}
cout << rioLoc[x].getName() << endl;
cout << rioLoc[x].getXoLoc() << endl;
cout << rioLoc[x].getXfLoc() << endl;
cout << rioLoc[x].getTime() << endl;
cout << rioLoc[x].getCellNum() << endl;
}
}
void maya(vector<trans>& newMyVector) {
for (int x = 0; x < mayaLoc.size(); ++x) {
if (x == 0) {
cout << "Num. of passangers to pickup in Mayaguez is " << mayaLoc.size() << " , these are: " << endl;
}
cout << mayaLoc[x].getName() << endl;
cout << mayaLoc[x].getXoLoc() << endl;
cout << mayaLoc[x].getXfLoc() << endl;
cout << mayaLoc[x].getTime() << endl;
cout << mayaLoc[x].getCellNum() << endl;
}
}
void elsewhere(vector<trans>& newMyVector) {
for (int x = 0; x < elseLoc.size(); ++x) {
if (x == 0) {
cout << "Num. of passangers to pickup in elsewhere is " << elseLoc.size() << " , these are: " << endl;
}
cout << elseLoc[x].getName() << endl;
cout << elseLoc[x].getXoLoc() << endl;
cout << elseLoc[x].getXfLoc() << endl;
cout << elseLoc[x].getTime() << endl;
cout << elseLoc[x].getCellNum() << endl;
}
}
To explain why the second code does not work I first have to explain why the first code appears to work.
myBag::myBag(string anItemName)
can make a bag out of a string. It is a Conversion Constructor. So when
fragileBag.push_back(newMyVector[x].getItem());
is compiled, the compiler quietly inserts a call to the myBag(string) constructor and you get something more like
fragileBag.push_back(myBag(newMyVector[x].getItem()));
which makes no sense logically. It says turn an item in a bag into a bag with one item and insert this new bag into still another bag, fragileBag.
When you look more closely at myBag, you see that it isn't a bag at all. It is a single item and should be renamed to myItem or discarded all together in favour of an all-new all-different myBag that is a wrapper around a vector of string where the strings represent items. This makes
myBag fragileBag;
the real bag.
In other words, the only reason the working code works is it doesn't actually do what the naming implies it does. The code compiles and produces the expected result, but is semantically troubled.
This leads to the confusion with
rioLoc.push_back(newMyVector[x].getXoLoc());
rioLoc is a vector<trans> and can only hold trans. There is no trans::trans(string) to convert a string to a trans so the faulty logic of the grocery code is exposed. As bag and item have been intertwined in grocery, passenger and transport are combined here.
The fix for grocery described above is relatively straight forward. Passenger will need a slightly different solution with both a passenger class to describe the passengers and a transport class to describe the means of transport. transport will have a vector<passenger> member to contain its passengers as well as methods to add and remove the passengers and possibly book-keeping to track the location of the transport, details incompletely specified by the question.
Both codes are pushing string values into a vector that does not hold string values.
Your grocery code uses a vector of myBag objects. The code works because myBag has a non-explicit constructor that takes a single string as input, so the compiler is able to implicitly construct a temporary myBag object to push into the vector.
Your passenger code uses a vector of trans objects. The code fails because trans does not have a constructor that takes a single string as input, so the compiler cannot construct a temporary trans to push into the vector.
So I am making a textbased RPG and I wanted to have multiple enemy encounter at once. So I modified my function that determines whether an object of the class Monster, to fill in the Monster(s) into an array of the class monster and set the objects bool to true, as you can see here:
Monster * Map::checkRandomEncounter()
{
Monster* monster = new Monster[3];
for (int i = 0; i < 3; i++)
{
int roll = Random(0, 20);
if (roll <= 5)
{
//No encounter
return 0;
}
else if (roll > 6 && roll < 10)
{
monster[i] = Monster();
monster[i].giveID("Orc", 10, 8, 200, 100, 1, "Short Sword", 2, 7);
monster[i].isFilled();
std::cout << "You encounter an Orc!" << std::endl;
std::cout << "Prepare for battle!" << std::endl;
std::cout << std::endl;
}
else if (roll >= 11 && roll <= 15)
{
monster[i] = Monster();
monster[i].giveID("Goblin", 6, 6, 100, 75, 0, "Dagger", 1, 5);
monster[i].isFilled();
std::cout << "You encounter a Goblin!" << std::endl;
std::cout << "Prepare for battle!" << std::endl;
std::cout << std::endl;
}
else if (roll >= 16 && roll <= 19)
{
monster[i] = Monster();
monster[i].giveID("Ogre", 20, 12, 500, 200, 2, "Club", 3, 8);
monster[i].isFilled();
std::cout << "You encounter an Ogre!" << std::endl;
std::cout << "Prepare for battle!" << std::endl;
std::cout << std::endl;
}
else if (roll == 20)
{
monster[i] = Monster();
monster[i].giveID("Orc Lord",
25,
15,
2000,
1000,
5,
"Two Handed Sword",
5,
20);
monster[i].isFilled();
std::cout << "You encounter an Orc Lord!" << std::endl;
std::cout << "Prepare for battle!" << std::endl;
std::cout << std::endl;
}
}
return monster;
}
The function above will be called in my main function, which looks like this:
int main()
{
srand(time(0));
Map gameMap;
Player mainPlayer;
mainPlayer.createClass();
//Beginn adventure
bool done = false;
while (done == false)
{
// Each Loop Cycle outputs player pos and selection menu
gameMap.printPlayerPos();
int selection = 1;
std::cout << "1) Move 2) Rest 3) View Stats 4) Quit: ";
std::cin >> selection;
Monster* monster = 0;
switch (selection)
{
case 1: // Move the player
gameMap.movePlayer();
if (gameMap.getPlayerXPos() == 2
&& gameMap.getPlayerYPos() == 3)
{
std::cout << "You see a store nearby !" << std::endl;
}
if (gameMap.getPlayerXPos() == 2
&& gameMap.getPlayerYPos() == 4)
{
Store store;
store.enter();
store.showInventory(mainPlayer);
}
//Check for a random encounter
//returns a null pointer if no encounter happened
monster = gameMap.checkRandomEncounter();
//'monster' not null, start battle script
if (monster != 0)
{
//Loop until a break statement
for (int i = 0; i < 3; i++)
{
//Display Hitpoints
mainPlayer.displayHitPoints();
monster[i].displayHitPoints();
std::cout << std::endl;
//Players turn to attack first
**bool runAway = mainPlayer.attack(monster, mainPlayer);** // Crash happening in this area
if (runAway) // Player flees
{
break;
}
if (monster[i].isDead())
{
mainPlayer.victory(monster->getXPReward(),
monster->getGoldReward());
mainPlayer.levelUp(mainPlayer);
}
break;
//Monster attacks
monster[i].attack(mainPlayer);
if (mainPlayer.isDead())
{
mainPlayer.gameOver();
done = true;
break;
}
}
//Pointer to Monster must destroy created instance of Monster
//to make sure that there is no Memory leak
}
delete monster;
monster = 0;
break;
case 2: // resting
mainPlayer.rest();
monster = gameMap.checkRandomEncounter();
//'monster' not null, start battle script
monster = gameMap.checkRandomEncounter();
//'monster' not null, start battle script
if (monster != 0)
{
//Loop until a break statement
for (int i = 0; i < 3; i++)
{
//Display Hitpoints
mainPlayer.displayHitPoints();
monster[i].displayHitPoints();
std::cout << std::endl;
//Players turn to attack first
bool runAway = mainPlayer.attack(monster, mainPlayer);
if (runAway) // Player flees
{
break;
}
if (monster[i].isDead())
{
mainPlayer.victory(monster->getXPReward(),
monster->getGoldReward());
mainPlayer.levelUp(mainPlayer);
}
break;
//Monster attacks
monster[i].attack(mainPlayer);
if (mainPlayer.isDead())
{
mainPlayer.gameOver();
done = true;
break;
}
}
//Pointer to Monster must destroy created instance of Monster
//to make sure that there is no Memory leak
}
delete monster;
monster = 0;
break;
case 3: // viewing stats
mainPlayer.viewStats();
break;
case 4: // quitting
done = true;
break;
}
}
return 0;
}
and finally the last puzzle piece, the function where the player attacks the Monster(s):
bool Player::attack(Monster Monster[], Player& Player)
{
int ArmorBefore = 0;
int Roll = 0;
int selection = 1;
int i;
if (Monster[0].isFilled() == true)
{
i = 0;
}
else if (Monster[1].isFilled() == true)
{
i = 1;
}
else if (Monster[2].isFilled() == true)
{
i = 2;
}
if (Monster[i].isFilled() == true)
{
std::cout << "1) Attack 2) Run 3) Cast Spell 4) Use Item: ";
std::cin >> selection;
std::cout << std::endl;
switch (selection)
{
case 1: // Player fights
std::cout << " You attack an " << Monster[i].getName()
<< " with a " << mWeapon.mName << std::endl;
if (Random(0, 20) < mAccuracy) // Player hits Monster
{
int damage = Random(mWeapon.mDamageRange);
int totalDamage = damage - Monster[i].getArmor();
if (totalDamage <= 0) // Armor is equal or higher than player atk
{
std::cout << "Your attack failed to penetrate "
<< Monster[i].getName() << "'s armor !"
<< std::endl;
return false;
}
else // Attack is higher than Monsters armor
{
std::cout << "You hit " << Monster[i].getName()
<< " for " << totalDamage << " damage !"
<< std::endl;
// Subtract dmg from Monsters hp
Monster[i].takeDamage(totalDamage);
return false;
}
}
else // Player Misses
{
std::cout << "You miss !" << std::endl;
}
std::cout << std::endl;
return false;
break;
case 2: // Player runs with a 25% chance
Roll = Random(1, 4);
if (Roll == 1) // Success
{
std::cout << "You run away !" << std::endl;
return true; // <- Return out of the function
}
else
{
std::cout << "You failed to escape !" << std::endl;
return false;
}
case 3: // Casting Spell
{
int SpellSelect;
// Spells for the Fighter
if (Player.mClassName == "Fighter")
{
std::cout << std::endl;
std::cout << "1) Shield 2) Mighty Blow: ";
std::cin >> SpellSelect;
if (SpellSelect == 1)
{
if (Player.mMagicPoints >= 10) // checks for player mana
{
std::cout << "You cast a mighty shield!"
<< std::endl;
ArmorBefore = Player.mArmor;
Player.shield(Player);
Player.mMagicPoints -= 10;
}
else
{
std::cout << "Not enough Mana" << std::endl;
break;
}
}
else
{
if (Player.mMagicPoints >= 5) // casting Mighty Blow
{
int damage = Random(mMightyBlow.mDamageRange);
std::cout
<< "You strike with all your might ! and Deal "
<< damage << " damage !" << std::endl;
Monster[i].takeDamage(damage);
Player.mMagicPoints -= 5;
return false;
}
else
{
std::cout << "Not enough Mana" << std::endl;
return false;
}
}
}
//Spells for the Wizard
else if (Player.mClassName == "Wizard")
{
std::cout << "1) Fireball";
std::cin >> SpellSelect;
if (Player.mMagicPoints >= 45)
{
int damage = Random(mFireball.mDamageRange);
std::cout << "You cast a Fireball and deal " << damage
<< " damage !" << std::endl;
Monster[i].takeDamage(damage);
Player.mMagicPoints -= 45;
return false;
}
else
{
std::cout << "Not enough Mana" << std::endl;
return false;
}
}
// Spells for the Cleric
else if (Player.mClassName == "Cleric")
{
std::cout << "1) Magic Missile";
std::cin >> SpellSelect;
if (Player.mMagicPoints >= 35)
{
int damage = Random(mMagicMissile.mDamageRange);
std::cout << "You cast a Magic Missile and deal "
<< damage << " damage !" << std::endl;
Monster[i].takeDamage(damage);
Player.mMagicPoints -= 35;
}
else
{
std::cout << "Not enough Mana" << std::endl;
return false;
}
}
}
case 4: // using Item
int invSlot;
std::cout << "1) HP Potion: ";
std::cin >> invSlot;
if (invSlot == 1) // Potion slot
{
if (mHPot.mAmount.size() > 0)
{
std::cout << "You used a Potion and healed for 5 HP !"
<< std::endl;
int currentSize = mHPot.mAmount.size();
mHPot.mAmount.resize(currentSize - 1);
Player.mHitPoints += 5;
if (Player.mHitPoints > Player.mMaxHitPoints)
{
Player.mHitPoints = Player.mMaxHitPoints;
}
return false;
}
else
{
std::cout << "You have no Potions!" << std::endl;
return false;
}
}
else // wrong slot
{
std::cout << "No Item found!" << std::endl;
return false;
}
}
// Clearing stat boosts
if (Player.shield(Player) == true)
{
Player.mArmor = ArmorBefore;
}
}
return false;
}
When I run the game, I sometimes have the problem, that after filling in a Monster in a slot of the array, no battle will be triggered. And if a battle will be triggered, I get a crash with an error report every time, which says:
_CrtlValidHeadPointer(block)
I guess that something with my pointer is not functioning well.... but since I am a beginner I am pretty much stuck. I would be very grateful for some enlightenment :)
This place can potentially call undefined behavior and crash:
int i;
if (Monster[0].isFilled() == true)
{
i = 0;
}
else if (Monster[1].isFilled() == true)
{
i = 1;
}
else if (Monster[2].isFilled() == true)
{
i = 2;
}
/*else // one of solutions
break;*/
//"i" can be unset! and can have any value from INT_MIN to INT_MAX!
if (Monster[i].isFilled() == true) //potentially index over array
{
Also there are memory leaks and undefined behavior with memory management:
Monster* monster = new Monster[3];
...
delete monster
must be delete [] monster
but it is recommended to use smart pointers, vector, array, etc, for memory management
I have a problem in my OOP homework. I have to code a 1 v 1 battle.
This is a Survival Game where in the player fights continuously with enemies until the player dies. The Player gets stronger as he advances through. The enemy also grows stronger every round.
I have created two Classes, The Unit and the Spawner.
We cannot use Inheritance nor Polymorphism.
We Have to use only the simple definition of the class.
My problem is that i cannot think of a SIMPLE way to make the class Spawner spawn a stronger enemy every round.
I have thought of overloading the Unit's Contructor but i have trouble manipulating that.
Would anyone please help?
Here's my Unit Header file (Unit.h):
#pragma once
#include <string>
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
// Class Types
const string CLASS_WARRIOR = "Warrior" ;
const string CLASS_ASSASSIN = "Assassin";
const string CLASS_MAGE = "Mage" ;
// Class Choices
const int CHOICE_WARRIOR = 1;
const int CHOICE_ASSASSIN = 2;
const int CHOICE_MAGE = 3;
// Multipliers
const double MULTIPLIER_DAMAGE = 0.50f;
const double MULTIPLER_HEAL = 0.30f;
// Advantage
const bool ADVANTAGE = true;
// Win Bonus
const int BONUS_THREE = 3;
const int BONUS_FIVE = 5;
// Minimum and maximum values
const int HIT_RATE_MAX = 80;
const int HIT_RATE_MIN = 20;
const int DAMAGE_MIN = 1;
// Hit or miss
const bool ATTACK_HIT = true;
const bool ATTACK_MISS = false;
class Unit
{
public:
// Constructors
Unit(string name, int classChoice);
// Getters and setters
string getName();
string getClass();
int getHp();
int getMaxHp();
int getPower();
int getVitality();
int getAgility();
int getDexterity();
int getDamage();
int getHeal();
int getBonusDamage();
bool getFirstToAttack();
bool getHit();
void setClassStats(const int classChoice);
// Primary Actions
void attack(Unit* target);
// Secondary Actions
void check(const Unit* target); // Inspects the enemy
void lvlUp(); // Grants this Unit Bonus Stats on Victory
private:
// Info
string mName;
string mClass;
// Basic Stats
int mHp;
int mMaxHp;
int mPower;
int mVitality;
int mAgility;
int mDexterity;
// Derived Stats
int mDamage;
int mHitrate;
int mHeal;
int mBonusDamage;
// Miscellaneous
bool mAdvantage; // If the player has the advantage
string mTargetClass;// This Unit keeps in mind the class of this Unit's Opponent for reference
int mChanceOfHit; // The random chance if the attack will Hit/Miss
bool mFirstToAttack;//if this unit will attack first
bool mHit; // if the attack is a hit or miss
};
Here's my Unit.cpp file (Unit.cpp):
#include "Unit.h"
Unit::Unit(string name, int classChoice)
{
mName = name;
setClassStats(classChoice);
}
string Unit::getName()
{
return mName;
}
string Unit::getClass()
{
return mClass;
}
int Unit::getHp()
{
return mHp;
}
int Unit::getMaxHp()
{
return mMaxHp;
}
int Unit::getPower()
{
return mPower;
}
int Unit::getVitality()
{
return mVitality;
}
int Unit::getAgility()
{
return mAgility;
}
int Unit::getDexterity()
{
return mDexterity;
}
int Unit::getDamage()
{
return mDamage;
}
int Unit::getHeal()
{
return mHeal;
}
int Unit::getBonusDamage()
{
return mBonusDamage;
}
bool Unit::getFirstToAttack()
{
return mFirstToAttack;
}
bool Unit::getHit()
{
return mHit;
}
void Unit::setClassStats(const int classChoice)
{
if (classChoice == CHOICE_WARRIOR) {
mClass = CLASS_WARRIOR;
mMaxHp = 20;
mHp = mMaxHp;
mPower = 15;
mVitality = 10;
mAgility = 7;
mDexterity = 7;
return;
}
else if (classChoice == CHOICE_ASSASSIN) {
mClass = CLASS_ASSASSIN;
mMaxHp = 15;
mHp = mMaxHp;
mPower = 17;
mVitality = 8;
mAgility = 12;
mDexterity = 10;
return;
}
else if (classChoice == CHOICE_MAGE) {
mClass = CLASS_MAGE;
mMaxHp = 30;
mHp = mMaxHp;
mPower = 12;
mVitality = 12;
mAgility = 9;
mDexterity = 8;
return;
}
throw exception("Error! Class not part of this game. ");
}
void Unit::attack(Unit * target)
{
// Determine Whether the attack will Hit/Miss Based on the hit rate
mChanceOfHit = rrand() % 100 + 1;
if (mChanceOfHit > mHitrate) {
mHit = ATTACK_MISS;
return;
}
// Deducts the targets Hp by the Damage given by this Unit (if the attack didn't miss)
target->mHp -= mDamage;
mHit = ATTACK_HIT;
// if target HP is negative, set it to zero
if (target->mHp < 0) target->mHp = 0;
}
void Unit::check(const Unit * target)
{
// The Unit Keeps in Mind his target's Class
if (target->mClass == CLASS_WARRIOR) mTargetClass = CLASS_WARRIOR;
else if (target->mClass == CLASS_ASSASSIN) mTargetClass = CLASS_ASSASSIN;
else if (target->mClass == CLASS_MAGE) mTargetClass = CLASS_MAGE;
// Checks if the Unit is Advantageous Against his Target
if (mClass == CLASS_WARRIOR) {
if (target->mClass == CLASS_ASSASSIN) mAdvantage = ADVANTAGE;
else mAdvantage = false;
}
else if (mClass == CLASS_ASSASSIN) {
if (target->mClass == CLASS_MAGE) mAdvantage = ADVANTAGE;
else mAdvantage = false;
}
else if (mClass == CLASS_MAGE) {
if (target->mClass == CLASS_WARRIOR) mAdvantage = ADVANTAGE;
else mAdvantage = false;
}
// Determine if this Unit Will Attack first
if (mAgility >= target->mAgility) mFirstToAttack = true;
else mFirstToAttack = false;
// Determines Damage, Bonus Damage ( If Applicable ), and Hit Rate Based on targets stats
mDamage = mPower - target->mVitality;
if (mDamage < DAMAGE_MIN) mDamage = DAMAGE_MIN;
mBonusDamage = mDamage * MULTIPLIER_DAMAGE;
// Evaluates Damage Based on advantage
if (mAdvantage) mDamage += mBonusDamage;
mHitrate = ((float)mDexterity / (float)target->mAgility) * 100;
// Clamps the Hit Rate within the Hit Rate Range
if (mHitrate > HIT_RATE_MAX) mHitrate = HIT_RATE_MAX;
else if (mHitrate < HIT_RATE_MIN) mHitrate = HIT_RATE_MIN;
}
void Unit::lvlUp()
{
// Determine Win Bonus Based on the target that he kept in mind
if (mTargetClass == CLASS_WARRIOR) {
mMaxHp += BONUS_THREE;
mVitality += BONUS_THREE;
}
else if (mTargetClass == CLASS_ASSASSIN) {
mAgility += BONUS_THREE;
mDexterity += BONUS_THREE;
}
else if (mTargetClass == CLASS_MAGE) {
mPower += BONUS_FIVE;
}
// Heals the player 30% of his Maximum HP
mHeal = mMaxHp * MULTIPLER_HEAL;
mHp += mHeal;
}
Here's my Spawner Header file (Spawner.h):
#pragma once
#include "Unit.h"
class Spawner
{
public:
Spawner();
~Spawner();
void spawn(Unit*& unit);
private:
Unit* mUnit;
int mRandomClass;
};
Here's my Spawner.cpp file (Spawner.cpp):
#include "Spawner.h"
Spawner::Spawner()
{
}
Spawner::~Spawner()
{
delete mUnit;
mUnit = NULL;
}
void Spawner::spawn(Unit *& unit)
{
mRandomClass = rand() % 3 + 1;
unit = new Unit("Enemy", mRandomClass);
mUnit = unit;
}
And finally here is my main.cpp:
#include "Unit.h"
#include "Spawner.h"
// Create player
Unit* createPlayer();
// Display Stats
void displayStats(Unit* unit);
// Play 1 round
void playRound(Unit*player, Unit* enemy);
// Simulate 1 attack (implement Higher agility gets to atttack first)
void combat(Unit* player, Unit* enemy);
void main()
{
srand(time(NULL));
Unit* player = createPlayer();
Unit* enemy = NULL;
Spawner *spawner = new Spawner();
int round = 1;
while (true) {
cout << "Round " << round << endl;
spawner->spawn(enemy);
player->check(enemy);
enemy->check(player);
playRound(player, enemy);
// if player is dead, end the game
if (player->getHp() == 0) break;
// else, add stats
player->lvlUp();
delete enemy;
enemy = NULL;
cout << "You defeted an enemy" << endl;
system("pause>nul");
system("cls");
round++;
}
cout << "You were defeted! " << endl;
cout << "You survived until Round " << round << endl;
displayStats(player);
delete player;
player = NULL;
delete spawner;
spawner = NULL;
system("pause>nul");
system("cls");
}
Unit* createPlayer()
{
Unit* unit = NULL;
string name;
int classChoice;
cout << "Enter your Name: ";
getline(cin, name);
system("cls");
cout << "Pick a class: " << endl
<< "\t [ 1 ] Warrior" << endl
<< "\t [ 2 ] Assassin" << endl
<< "\t [ 3 ] Mage" << endl
<< endl;
cin >> classChoice;
while (classChoice > 3 || classChoice <= 0) {
cout << "INVALID INPUT! Please try again" << endl;
cout << "Pick a class: " << endl
<< "\t [ 1 ] Warrior" << endl
<< "\t [ 2 ] Assassin" << endl
<< "\t [ 3 ] Mage" << endl
<< endl;
cin >> classChoice;
}
unit = new Unit(name, classChoice);
return unit;
}
void displayStats(Unit* unit)
{
cout << "=========================================================================================" << endl
<< "Name: " << unit->getName() << " \t \t \t HP: " << unit->getHp() << " / " << unit->getMaxHp() << endl
<< "Class: " << unit->getClass() << endl
<< "==========================================================================================" << endl
<< "POW: " << unit->getPower() << endl
<< "VIT: " << unit->getVitality() << endl
<< "AGI: " << unit->getAgility() << endl
<< "DEX: " << unit->getDexterity() << endl
<< endl;
}
void playRound(Unit*player, Unit* enemy)
{
while (player->getHp() > 0 && enemy->getHp() > 0) {
displayStats(player);
displayStats(enemy);
system("pause>nul");
combat(player, enemy);
system("cls");
}
}
void combat(Unit* player, Unit* enemy)
{
if (player->getFirstToAttack()) {
player->attack(enemy);
if (player->getHit() == ATTACK_MISS) cout << player->getName() << "'s Attack Missed! " << endl;
else if (player->getHit() == ATTACK_HIT) cout << player->getName() << " dealt " << player->getDamage() << " Damage" << endl;
system("pause>nul");
if (enemy->getHp() == 0) return;
enemy->attack(player);
if (enemy->getHit() == ATTACK_MISS) cout << enemy->getName() << "'s Attack Missed! " << endl;
else if (enemy->getHit() == ATTACK_HIT) cout << enemy->getName() << " dealt " << enemy->getDamage() << " Damage" << endl;
system("pause>nul");
return;
}
else if (enemy->getFirstToAttack()) {
enemy->attack(player);
if (enemy->getHit() == ATTACK_MISS) cout << enemy->getName() << "'s Attack Missed! " << endl;
else if (enemy->getHit() == ATTACK_HIT) cout << enemy->getName() << " dealt " << enemy->getDamage() << " Damage" << endl;
system("pause>nul");
if (player->getHp() == 0) return;
player->attack(enemy);
if (player->getHit() == ATTACK_MISS) cout << player->getName() << "'s Attack Missed! " << endl;
else if (player->getHit() == ATTACK_HIT) cout << player->getName() << " dealt " << player->getDamage() << " Damage" << endl;
system("pause>nul");
return;
}
}
Add a "level" to the Unit class, and use the level to increase the stats of the unit (for example level 2 could mean the stats are multiplied by 1.5 or some such). Pass the level as an argument to the constructor similar to the units class.
You need to make your spawner aware of how many enemies it has spawned:
// this belongs into your class definition, it should start at zero
int mCurrentSpawn;
So set it to zero in your constructor:
Spawner::Spawner()
{
mCurrentSpawn = 0;
}
Then reference it when you spawn an enemy:
void Spawner::spawn(Unit *& unit)
{
mRandomClass = rand() % 3 + 1;
unit = new Unit("Enemy", mRandomClass);
mUnit = unit;
mCurrentSpawn++;
int levelUps = mCurrentSpawn - 1;
while(levelUps > 0)
{
mUnit->lvlUp();
levelUps--;
}
}
I am working on a very simple feed forward neural network to practice my programming skills. There are 3 classes :
Neural::Net ; builds the network, feeds forward input values (no backpropagation for the moment)
Neural::Neuron ; has characteristics of the neuron (index, output, weight etc)
Neural::Connection ; a structure-like class that randomizes the weights and hold the output, delta weight etc..
The program is very basic: I build the network with 2 hidden layers and randomized weights, then ask it to feed forward the same input values.
My problem is: It is expected that the program ends up with different output values after every run, yet the output are always the same. I tried placing markers everywhere to understand why it is calculating the same thing over and over again but I can't put my finger on the error.
Here is the code:
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <vector>
#include "ConsoleColor.hpp"
using namespace std;
namespace Neural {
class Neuron;
typedef vector<Neuron> Layer;
// ******************** Class: Connection ******************** //
class Connection {
public:
Connection();
void setOutput(const double& outputVal) { myOutputVal = outputVal; }
void setWeight(const double& weight) { myDeltaWeight = myWeight - weight; myWeight = weight; }
double getOutput(void) const { return myOutputVal; }
double getWeight(void) const { return myWeight; }
private:
static double randomizeWeight(void) { return rand() / double(RAND_MAX); }
double myOutputVal;
double myWeight;
double myDeltaWeight;
};
Connection::Connection() {
myOutputVal = 0;
myWeight = Connection::randomizeWeight();
myDeltaWeight = myWeight;
cout << "Weight: " << myWeight << endl;
}
// ******************** Class: Neuron ************************ //
class Neuron {
public:
Neuron();
void setIndex(const unsigned int& index) { myIndex = index; }
void setOutput(const double& output) { myConnection.setOutput(output); }
unsigned int getIndex(void) const { return myIndex; }
double getOutput(void) const { return myConnection.getOutput(); }
void feedForward(const Layer& prevLayer);
void printOutput(void) const;
private:
inline static double transfer(const double& weightedSum);
Connection myConnection;
unsigned int myIndex;
};
Neuron::Neuron() : myIndex(0), myConnection() { }
double Neuron::transfer(const double& weightedSum) { return 1 / double((1 + exp(-weightedSum))); }
void Neuron::printOutput(void) const { cout << "Neuron " << myIndex << ':' << myConnection.getOutput() << endl; }
void Neuron::feedForward(const Layer& prevLayer) {
// Weight sum of the previous layer's output values
double weightedSum = 0;
for (unsigned int i = 0; i < prevLayer.size(); ++i) {
weightedSum += prevLayer[i].getOutput()*myConnection.getWeight();
cout << "Neuron " << i << " from prevLayer has output: " << prevLayer[i].getOutput() << endl;
cout << "Weighted sum: " << weightedSum << endl;
}
// Transfer function
myConnection.setOutput(Neuron::transfer(weightedSum));
cout << "Transfer: " << myConnection.getOutput() << endl;
}
// ******************** Class: Net *************************** //
class Net {
public:
Net(const vector<unsigned int>& topology);
void setTarget(const vector<double>& targetVals);
void feedForward(const vector<double>& inputVals);
void backPropagate(void);
void printOutput(void) const;
private:
vector<Layer> myLayers;
vector<double> myTargetVals;
};
Net::Net(const vector<unsigned int>& topology) : myTargetVals() {
assert(topology.size() > 0);
for (unsigned int i = 0; i < topology.size(); ++i) { // Creating the layers
myLayers.push_back(Layer(((i + 1) == topology.size()) ? topology[i] : topology[i] + 1)); // +1 is for bias neuron
// Setting each neurons index inside layer
for (unsigned int j = 0; j < myLayers[i].size(); ++j) {
myLayers[i][j].setIndex(j);
}
// Console log
cout << red;
if (i == 0) {
cout << "Input layer (" << myLayers[i].size() << " neurons including bias neuron) created." << endl;
myLayers[i].back().setOutput(1);
}
else if (i < topology.size() - 1) {
cout << "Hidden layer " << i << " (" << myLayers[i].size() << " neurons including bias neuron) created." << endl;
myLayers[i].back().setOutput(1);
}
else { cout << "Output layer (" << myLayers[i].size() << " neurons) created." << endl; }
cout << white;
}
}
void Net::setTarget(const vector<double>& targetVals) { assert(targetVals.size() == myLayers.back().size()); myTargetVals = targetVals; }
void Net::feedForward(const vector<double>& inputVals) {
assert(myLayers[0].size() - 1 == inputVals.size());
for (unsigned int i = 0; i < inputVals.size(); ++i) { // Setting input vals to input layer
cout << yellow << "Setting input vals...";
myLayers[0][i].setOutput(inputVals[i]); // myLayers[0] is the input layer
cout << "myLayer[0][" << i << "].getOutput()==" << myLayers[0][i].getOutput() << white << endl;
}
for (unsigned int i = 1; i < myLayers.size() - 1; ++i) { // Updating hidden layers
for (unsigned int j = 0; j < myLayers[i].size() - 1; ++j) { // - 1 because bias neurons do not have input
cout << "myLayers[" << i << "].size()==" << myLayers[i].size() << endl;
cout << green << "Updating neuron " << j << " inside layer " << i << white << endl;
myLayers[i][j].feedForward(myLayers[i - 1]); // Updating the neurons output based on the neurons of the previous layer
}
}
for (unsigned int i = 0; i < myLayers.back().size(); ++i) { // Updating output layer
cout << green << "Updating output neuron " << i << ": " << white << endl;
const Layer& prevLayer = myLayers[myLayers.size() - 2];
myLayers.back()[i].feedForward(prevLayer); // Updating the neurons output based on the neurons of the previous layer
}
}
void Net::printOutput(void) const {
for (unsigned int i = 0; i < myLayers.back().size(); ++i) {
cout << blue; myLayers.back()[i].printOutput(); cout << white;
}
}
void Net::backPropagate(void) {
}
}
int main(int argc, char* argv[]) {
vector<unsigned int> myTopology;
myTopology.push_back(3);
myTopology.push_back(4);
myTopology.push_back(2);
myTopology.push_back(2);
cout << myTopology.size() << endl << endl; // myTopology == {3, 4, 2 ,1}
vector<double> myTargetVals= {0.5,1};
vector<double> myInputVals= {1, 0.5, 1};
Neural::Net myNet(myTopology);
myNet.feedForward(myInputVals);
myNet.printOutput();
return 0;
}
Edit: I figured that the bias neuron in the input layer was correctly set to output 1 while the ones in the hidden layers are set to 0 and I fixed that. But the outputs are still the same every run. I did the math on a sheet of paper and it works out. Here is the output (Color coded for clarity) :
I have expected the values to be random just like the weights. Shouldn't that be the case ? I am confused.
I found my mistake. I thought that rand() initialized its seed automatically. I knew it was a dumb thing. I added srand(time(NULL)); at the beginning of the program and now it works as it should.