I'm learning C++ by myself and therefore I have no direct access to a mentor. This is the reason why I'd love if you guys could take the time to do a peer code review.
I am very much aware, as per the FAQ, that very few people would be willing to go through all my code, but every little bit helps.
The exercise is the bunny linked list exercise; the last beginner exercise from: http://www.cplusplus.com/forum/articles/12974/
I'm looking for feedback on absolutely everything that could make me a better programmer:
- Syntax
- Optimization
- Form
- Functions or variables naming
- Bugs
- Performance
- Code structure
Keep in mind that I haven't completed all aspects of the exercise related to it running in real-time or outputting events to a file.
Thanks for your time! Know that it's very much appreciated.
main.cpp
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <string>
#include "BunnyNode.h"
#include "BunnyList.h"
//BUGS
//Generate at least one female and one male on start
//TO DO
//Replace names with vectors(?) generated from separate .txt file
//Output all turn events, birth, turn mutant, dies
//Output all turns to file
using std::cin;
using std::cout;
using std::endl;
int main()
{
srand(time(0));
// Create rabbit LinkedList
RabbitList * colony = new RabbitList;
bool terminate = false;
int turns = 0;
//Add 5 rabbits to list for the first round
colony->startCheck(turns);
while(!terminate)
{
colony->printList();
cout << endl;
//Increment all rabbits age
colony->incrementRabbitAge();
//Add babies
colony->addBabies();
//Each mutant will convert one other bunny each turn
colony->mutantConversion();
//Check if colony is 0 or 1000
colony->sizeCheck(terminate);
cout << "\nThe current mutant count is: " << colony->mutantCount() << endl;
colony->printSize();
++turns;
if(colony->getColonySize() != 0)
{
cout << "\nNext turn? Press any key to continue. (Press k to purge the colony.)" << endl;
char choice;
cin >> choice;
if(choice == 'k')
colony->purge();
}
}
cout << "\nTOTAL TURNS: " << turns << endl;
delete colony;
return 0;
}
BunnyList.cpp
#include <cstdlib>
#include <iostream>
#include <string>
#include "BunnyList.h"
#include "globals.h"
using std::string;
using std::cout;
using std::endl;
RabbitList::RabbitList(): head(NULL),size(0)
{}
RabbitList::~RabbitList()
{}
int RabbitList::randomGeneration(int x)
{
return rand() % x;
}
void RabbitList::generateRandomFeatures(RabbitNode * newRabbit)
{
newRabbit->sex = randomGeneration(2); //Generate gender first to determine first name
newRabbit->colour = colourList[randomGeneration(MAX_COLOURS)]; //Generate colour
int i = randomGeneration(10);
if(newRabbit->sex) //Generate name
newRabbit->firstName = maleFirstName[i];
else
newRabbit->firstName = femaleFirstName[i];
i = randomGeneration(10);
newRabbit->lastName = lastNames[i];
i = randomGeneration(100); //Generate mutant
if(i < BORN_MUTANT_PERCENT_CHANCE)
newRabbit->radioactive_mutant_vampire_bunny = true;
}
//Turn checks
void RabbitList::startCheck(int turns)
{
if(turns == 0) // Generate first 5 rabbits
{
for(int index = 0; index < 5; ++index)
{
addRabbit();
}
}
}
void RabbitList::sizeCheck(bool& terminate)
{
if(getColonySize() == 0)
terminate = true;
else if(getColonySize() >= MAX_COLONY_SIZE)
{
purge();
}
}
bool RabbitList::fatherCheck(RabbitNode * rabbit, bool& fatherPresent)
{
if(rabbit->sex == 1 && rabbit->age >= 2 && rabbit->radioactive_mutant_vampire_bunny == false)
fatherPresent = true;
return fatherPresent;
}
bool RabbitList::motherCheck(RabbitNode * rabbit)
{
if(rabbit->sex == 0 && rabbit->age >= 2 && rabbit->radioactive_mutant_vampire_bunny == false)
return true;
else
return false;
}
//Returns true if rabbit is older than 10, false otherwise
bool RabbitList::ageCheck(RabbitNode * rabbit)
{
if(head)
{
if(rabbit->age >= MAX_AGE && rabbit->radioactive_mutant_vampire_bunny == false)
{
return 1;
}
else if(rabbit->age >= MAX_MUTANT_AGE && rabbit->radioactive_mutant_vampire_bunny == true)
{
return 1;
}
else
return 0;
}
}
//Add one year to all rabbit every turn,
//Kill the rabbit if he goes over its allowed age, 10 for regular, 50 for mutant
void RabbitList::incrementRabbitAge()
{
//If there's more than one node
if(head)
{
RabbitNode * temp = head;
RabbitNode * trail = NULL;
while(temp != NULL)
{
//The rabbit is young enough to age
if(!ageCheck(temp))
{
temp->age += 1;
trail = temp;
temp = temp->next;
}
//The rabbit is too old
else
{
//If we're on the first head node
if(head == temp)
{
head = temp->next;
cout << "Bunny " << getName(temp) << " died!" << endl;
delete temp;
temp = head;
--size;
}
//If we're beyond the head node
else
{
trail->next = temp->next;
cout << "Bunny " << getName(temp) << " died!" << endl;
delete temp;
temp = trail->next;
--size;
}
}
}
}
}
void RabbitList::addBabies()
{
if(head)
{
//Checks if there is at least one male in the colony
bool fatherPresent = false;
RabbitNode* temp = head;
while(temp != NULL)
{
fatherCheck(temp, fatherPresent);
temp = temp->next;
}
//Executes if there's at least one male
if(fatherPresent == true)
{
temp = head;
RabbitNode * trail = NULL;
while(temp != NULL)
{
if(motherCheck(temp) == true)
{
addRabbit(temp);
temp = temp->next;
}
else
{
temp = temp->next;
}
}
}
}
}
void RabbitList::addRabbit()
{
RabbitNode* newRabbit = new RabbitNode;
if(!head)
{
head = newRabbit;
generateRandomFeatures(newRabbit);
}
else
{
RabbitNode* temp = head;
while(temp->next != NULL)
temp = temp->next;
RabbitNode* newRabbit = new RabbitNode;
generateRandomFeatures(newRabbit);
temp->next = newRabbit;
}
++size;
}
void RabbitList::addRabbit(RabbitNode * mother)
{
RabbitNode* newRabbit = new RabbitNode;
if(!head)
{
head = newRabbit;
generateRandomFeatures(newRabbit);
if(newRabbit->radioactive_mutant_vampire_bunny == true)
cout << "Radioactive Mutant Vampire ";
cout << "Bunny " << getName(newRabbit) << " was born!" << endl;
}
else
{
RabbitNode* temp = head;
while(temp->next != NULL)
temp = temp->next;
RabbitNode* newRabbit = new RabbitNode;
generateRandomFeatures(newRabbit); // We'll replace the colour right after
newRabbit->colour = mother->colour; // Set baby colour to be the same as the mother
temp->next = newRabbit;
if(newRabbit->radioactive_mutant_vampire_bunny == true)
cout << "Radioactive Mutant Vampire ";
cout << "Bunny " << getName(newRabbit) << " was born!" << endl;
}
++size;
}
int RabbitList::mutantCount()
{
int mutantTotal = 0;
if(head)
{
RabbitNode * temp = head;
while(temp != NULL)
{
if(temp->radioactive_mutant_vampire_bunny)
++mutantTotal;
temp = temp->next;
}
}
return mutantTotal;
}
//For each mutant rabbit, it will convert another one each turn
void RabbitList::mutantConversion()
{
//Get the number of mutants in the colony
int amountToConvert = mutantCount();
if(amountToConvert > 0 && head != NULL)
{
//Executes if there's still bunnies left to convert, or if all the bunnies aren't mutants yet
while(amountToConvert != 0 && mutantCount() != getColonySize())
{
RabbitNode * temp = head;
//Choose a bunny randomly in the colony to convert
int bunnyToConvert = randomGeneration(getColonySize());
//Traverse list to get to the chosen bunny
for(; bunnyToConvert > 0; --bunnyToConvert)
{
temp = temp->next;
}
//Check if chosen bunny isn't already a mutant
if(temp->radioactive_mutant_vampire_bunny == false)
{
temp->radioactive_mutant_vampire_bunny = true;
cout << getName(temp) << " has turned into a mutant!" << endl;
--amountToConvert;
}
}
}
}
void RabbitList::purge()
{
if(getColonySize() == 1)
{
delete head;
head = NULL;
--size;
}
if(head)
{
//Kill half the colony
for(int amountToKill = (getColonySize()) / 2; amountToKill != 0;)
{
RabbitNode * curr = head;
RabbitNode * trail = NULL;
//Choose a bunny randomly in the colony to kill
int bunnyToKill = randomGeneration(getColonySize());
//Traverse list to get to the chosen bunny to kill
for(; bunnyToKill > 0; --bunnyToKill)
{
trail = curr;
curr = curr->next;
}
//If we're on the head node
if(curr == head)
{
head = curr->next;
delete curr;
--size;
--amountToKill;
}
//If we're beyond the head, but not on last node
else if(curr->next != NULL)
{
trail->next = curr->next;
delete curr;
--size;
--amountToKill;
}
//If we're on the last node
else
{
trail->next = NULL; // crash
delete curr;
--size;
--amountToKill;
}
}
cout << "Food shortage! Colony has been purged by half." << endl;
}
}
//DATA MEMBER ACCESSORS
string RabbitList::getGender(RabbitNode * rabbit)
{
if(rabbit->sex == 1)
return "Male";
else
return "Female";
}
string RabbitList::getName(RabbitNode * rabbit)
{
return rabbit->firstName + " " + rabbit->lastName;
}
int RabbitList::getColonySize()
{
return size;
}
void RabbitList::printList()
{
RabbitNode* temp = head;
if(head)
{
while(temp != NULL)
{
printFeatures(temp);
temp = temp->next;
}
}
}
void RabbitList::printFeatures(RabbitNode * rabbit)
{
if(head)
{
cout << "\nNAME: " << getName(rabbit) << endl
<< "AGE: " << rabbit->age << endl
<< "COLOUR: " << rabbit->colour << endl
<< "GENDER: " << getGender(rabbit) << endl;
if(rabbit->radioactive_mutant_vampire_bunny)
cout << "Radioactive mutant vampire bunny!" << endl;
cout << endl;
}
}
void RabbitList::printSize()
{
if(head)
cout << "The colony's size is currently : " << size << endl;
}
BunnyNode.cpp
#include "BunnyNode.h"
using std::cout;
using std::endl;
RabbitNode::RabbitNode():next(NULL), firstName("none"), lastName("none"), colour("none"), age(0), radioactive_mutant_vampire_bunny(0)
{}
RabbitNode::~RabbitNode()
{}
BunnyList.h
#ifndef GUARD_BUNNYLIST_H
#define GUARD_BUNNYLIST_H
#include <string>
#include "BunnyNode.h"
class RabbitList
{
public:
//Constructor
RabbitList();
~RabbitList();
//Member methods
int randomGeneration(int x);
void generateRandomFeatures(RabbitNode * newRabbit);
void startCheck(int turns);
void sizeCheck(bool& terminate);
bool motherCheck(RabbitNode * rabbit);
bool fatherCheck(RabbitNode * rabbit, bool& fatherPresent);
bool ageCheck(RabbitNode * rabbit);
void incrementRabbitAge();
void addBabies();
void addRabbit();
void addRabbit(RabbitNode * mother);
void purge();
int mutantCount();
void mutantConversion();
std::string getGender(RabbitNode * rabbit);
std::string getName(RabbitNode * rabbit);
int getColonySize();
void printList();
void printFeatures(RabbitNode * rabbit);
void printSize();
private:
RabbitNode* head;
int size;
};
#endif // GUARD_BUNNYLIST_H
BunnyNode.h
#ifndef GUARD_BUNNYNODE_H
#define GUARD_BUNNYNODE_H
#include <iostream>
#include <string>
class RabbitNode
{
friend class RabbitList;
public:
RabbitNode();
~RabbitNode();
private:
std::string firstName, lastName, colour;
int age;
bool sex;
bool radioactive_mutant_vampire_bunny;
RabbitNode* next;
};
#endif // GUARD_BUNNYNODE_H
globals.h
#ifndef GUARD_GLOBALS_H
#define GUARD_GLOBALS_H
static const int MAX_COLOURS = 4;
static const int MAX_AGE = 10;
static const int MAX_MUTANT_AGE = 50;
static const int MAX_COLONY_SIZE = 1000;
static const int BORN_MUTANT_PERCENT_CHANCE = 2;
//To do: replace with vectors(?) generated from separate .txt file
static std::string maleFirstName[] =
{
"Bob",
"Nick",
"Roger",
"Tim",
"Ivan",
"John",
"Jack",
"Vincent",
"Dave",
"Donald"
};
static std::string femaleFirstName[] =
{
"Kate",
"Jane",
"Lisa",
"Kim",
"Allison",
"Sophie",
"Anna",
"Lillian",
"Sarah",
"Alexandra"
};
static std::string lastNames[] =
{
"Smith",
"Williams",
"Brown",
"von Shaft",
"Mitchell",
"O'Connor",
"Edwards",
"Harris",
"Wood",
"Cooper"
};
static std::string colourList[MAX_COLOURS] =
{
"White",
"Brown",
"Black",
"Spotted"
};
#endif // GUARD_BUNNYNODE_H