I recently completed (somewhat) an exercise I found on a C++ forum. The exercise is found at the end of the list of exercises and is called "Graduation" and is about bunnies in a linked list.
Write a program that creates a linked list of bunny objects. Each bunny object must have
- Sex: Male, Female (random at creation 50/50)
- color: white, brown, black, spotted
- age : 0-10 (years old) [with the exception of mutant bunnies (0-50)]
- Name : randomly chosen at creation from a list of bunny names. radioactive_mutant_vampire_bunny: true/false (decided at time of bunny creation 2% chance of true)
- At program initialization 5 bunnies must be created and given random colors.
- Each turn afterwards the bunnies age 1 year.
- So long as there is at least one male age 2 or older, for each female bunny in the list age 2 or older; a new bunny is created each turn. (i.e. if there was 1 adult male and 3 adult female bunnies, three new bunnies would be born each turn)
- New bunnies born should be the same color as their mother.
- If a bunny becomes older than 10 years old, it dies [with the exception of mutant bunnies, which can become 50 years old].
- If a radioactive mutant vampire bunny is born then each turn it will change exactly one non radioactive bunny into a radioactive vampire bunny. (if there are two radioactive mutant vampire bunnies two bunnies will be changed each turn and so on...)
- Radioactive vampire bunnies are excluded from regular breeding and do not count as adult bunnies.
- Radioactive vampire bunnies do not die until they reach age 50. The program should print a list of all the bunnies in the colony each turn along w/ all the bunnies details, sorted by age.
The program should also output each turns events such as
Bunny Thumper was born!
Bunny Fufu was born!
Radioactive Mutant Vampire Bunny Darth Maul was born!
Bunny Julius Caesar died!- The program should write all screen output to a file.
- When all the bunnies have died the program terminates.
- If the bunny population exceeds 1000 a food shortage must occur killing exactly half of the bunnies (randomly chosen)
I've gotten almost all of it done minus the file and GUI part. I'm looking for feedback specifically on performance, decisions with function return values and parameters(ex: returning a list<Bunny>
vs taking a list<Bunny>
as a parameter and modifying it in the function), design of constructors and other member functions, and use of global variables. Really, anything that would help make me a better programmer would be greatly welcome.
Note: std_lib_facilities.h
is simply a standard library access header from a book I use.
Oh, and please excuse my lack of creativity with the names.
Main.cpp
#include "Game.h"
#include <Windows.h>
int main()
{
srand(time(0));
list<Bunny>bunnies;
spawn_bunnies(bunnies);
while (Bunny::count_total_bunnies > 0)
{
Sleep(2000);
end_turn(bunnies);
Sleep(1000);
}
cout << "All bunnies have died!" << endl;
}
Bunny.h
#include "../../../std_lib_facilities.h"
enum Sex {
male, female
};
enum Color {
white, brown, black, spotted
};
class Bunny {
private:
Sex sex;
Color color;
int age;
string name;
bool radioactive_mutant_vampire_bunny;
int max_age;
public:
static int count_radioactive_bunnies;
static int count_total_bunnies;
Bunny();
Bunny(Color c);
void increment_age()
{
++age;
}
int get_age() const
{
return age;
}
Sex get_sex() const
{
return sex;
}
Color get_color()
{
return color;
}
string get_name()
{
return name;
}
bool is_radioactive() const
{
return radioactive_mutant_vampire_bunny;
}
void set_radioactive(bool b)
{
radioactive_mutant_vampire_bunny = b;
}
int get_max_age()
{
return max_age;
}
};
Bunny.cpp
#include "Bunny.h"
vector<string>bunny_names = { "Larry", "Bob", "Tom", "George", "Pal", "Joe", "Nick", "da Bunny"};
int Bunny::count_radioactive_bunnies = 0;
int Bunny::count_total_bunnies = 0;
Bunny::Bunny()
:sex{ (rand() % 100 + 1) < 50 ? Sex::male : Sex::female },
color{Color(rand() % 4)},
age{ 0 },
name{ bunny_names[rand() % bunny_names.size()] },
radioactive_mutant_vampire_bunny{ (rand() % 100 + 1) <= 2 ? true : false },
max_age{ radioactive_mutant_vampire_bunny ? 50 : 10 }
{
++count_total_bunnies;
if (radioactive_mutant_vampire_bunny)
{
cout << "Radioactive Mutant Vampire Bunny " << get_name() << " was born!" << endl;
++count_radioactive_bunnies;
}
else {
cout << "Bunny " << get_name() << " was born!\n";
}
}
Bunny::Bunny(Color c)
:sex{ (rand() % 100 + 1) < 50 ? Sex::male : Sex::female },
color{ c },
age{ 0 },
name{ bunny_names[rand() % bunny_names.size()] },
radioactive_mutant_vampire_bunny{ (rand() % 100 + 1) <= 2 ? true : false },
max_age{ radioactive_mutant_vampire_bunny ? 50 : 10 }
{
++count_total_bunnies;
if (radioactive_mutant_vampire_bunny)
{
cout << "Radioactive Mutant Vampire Bunny " << get_name() << " was born!" << endl;
++count_radioactive_bunnies;
}
else {
cout << "Bunny " << get_name() << " was born!\n";
}
}
Game.h
#include "Bunny.h"
void spawn_bunnies(list<Bunny>&b); //runs only once in the beginning to instantiate the first 5 bunny objects
void end_turn(list<Bunny>&); // runs every ~2 seconds
bool male_available(const list<Bunny>&); //checks if there is a "valid" male available to reproduce
list<Bunny> females_available(const list<Bunny>&); //returns list of "valid" females to reproduce
void create_babies(list<Bunny>&); //uses list of females_available to determine color of bunny babies, as well as the # of babies
void transform_random_bunny(list<Bunny>&b); //transforms n random normal bunnies into mutant bunnies. n = num of current mutant bunnies
void bunnies_die(list<Bunny>&); //kills half of the bunnies when their count is above 1000
list<Bunny> get_normal_bunnies(const list<Bunny>& b); //returns a list of normal bunnies
Game.cpp
#include "Game.h"
void spawn_bunnies(list<Bunny>&b)
{
Bunny b1;
Bunny b2;
Bunny b3;
Bunny b4;
Bunny b5;
b = { b1, b2, b3, b4, b5 };
}
void end_turn(list<Bunny>&b)
{
list<Bunny>::iterator bunny = b.begin();
while (bunny != b.end())
{
bunny->increment_age();
if (bunny->get_age() > bunny->get_max_age())
{
cout << "Bunny " << bunny->get_name() << " died!" << endl;
bunny = b.erase(bunny);
--Bunny::count_total_bunnies;
}
else
{
++bunny;
}
}
create_babies(b);
if (Bunny::count_radioactive_bunnies) transform_random_bunny(b);
if (Bunny::count_total_bunnies > 1000) bunnies_die(b);
}
bool male_available(const list<Bunny>&b)
{
for (const Bunny b : b)
{
if (b.get_age() >= 2 && b.get_sex() == Sex::male && !b.is_radioactive())
{
return true;
}
}
return false;
}
list<Bunny> females_available(const list<Bunny>&b)
{
list<Bunny>females;
for (const Bunny& bunny : b)
{
if (bunny.get_age() >= 2 && bunny.get_sex() == Sex::female && !bunny.is_radioactive())
{
females.push_back(bunny);
}
}
return females;
}
void create_babies(list<Bunny>& b)
{
list<Bunny>females = females_available(b);
if (male_available(b) && females.size())
{
for (Bunny p : females)
{
Bunny new_bunny{ p.get_color() };
b.push_back(new_bunny);
}
}
}
void transform_random_bunny(list<Bunny>&b)
{
list<Bunny> normal_bunnies = get_normal_bunnies(b);
for (int i = 0; i <= Bunny::count_radioactive_bunnies; i++)
{
list<Bunny>::iterator bunny = normal_bunnies.begin();
advance(bunny, rand() % normal_bunnies.size());
bunny->set_radioactive(true);
normal_bunnies.erase(bunny);
}
}
list<Bunny> get_normal_bunnies(const list<Bunny>& b)
{
list<Bunny>normal_bunnies;
for (const Bunny bunny : b)
{
if (!bunny.is_radioactive())
{
normal_bunnies.push_back(bunny);
}
}
return normal_bunnies;
}
void bunnies_die(list<Bunny>&b)
{
int bunnies_dead = b.size() / 2;
for (int i = 0; i < bunnies_dead; i++)
{
list<Bunny>::iterator dead_bunny = b.begin();
advance(dead_bunny, rand() % b.size());
b.erase(dead_bunny);
--Bunny::count_total_bunnies;
}
}