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) 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. 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)
★ Modify the program to run in real time, with each turn lasting 2 seconds, and a one second pause between each announement.
★★ Allow the user to hit the 'k' key to initiate a mass rabit cull! which causes half of all the rabits to be killed (randomly chosen).
★★★★ Modify the program to place the rabits in an 80x80 grid. Have the rabits move one space each turn randomly. Mark juvenile males with m, adult males w/ M, juvenile females w/ f, adult femails w/ F radioactive mutant vampire bunnies with X
Modify the program so that radioactive mutant vampire bunnies only convert bunnies that end a turn on an adjacent square. Modify the program so that new babies are born in an empty random adjacent square next to the mother bunny. (if no empty square exits then the baby bunny isn't born)
I recently started learning C++ myself online and I have attempted to do the bunny beginner exercise here. I did not do the "run in real time" and "save each turn to a file" modification yet, but I did do everything else, including the grid.
Basically I am looking for any feedback, if at all. Some feedback I have seen so far on similar threads are:
- Do not use "using namespace std"
- Do not use rand(), instead use C++ 11 random ( it was too late for me to change)
Honestly any comment that would help, such as errors in way of doing things and ways to make things simpler would be much appreciated. It is a lot of lines in total, therefore I am thankful if you read through it.
Here are the codes:
main.cpp
#include <iostream>
#include <string>
#include <time.h>
#include "Bunny.h"
#include "listfunc.h"
#include <windows.h>
using namespace std;
int main() {
srand(time(NULL));
listfunc bunnylist;
for(int x=0; x<5; x++) { // initialize 5 bunnies.
bunnylist.create();
}
bunnylist.echo(); // shows first list of bunnies
bunnylist.display();
char c;
cout<< "Enter 'P' to start the bunny simulation\n";
cin>> c; // allows user to "start"
while(c=='P') {
system("cls");
bunnylist.age(); //first increase all bunnies age
bunnylist.old(); //second kill those that are too old
bunnylist.radioinfect(); //third cause the radioactivity to spread
bunnylist.baby(); //fourth create bunny reproduction
bunnylist.display(); //show the grid.
bunnylist.event(); //display what happened that turn
if (bunnylist.whatnum()>1000) { // if number of bunnies is more than 1000, kill half of the bunnies.
bunnylist.cull();
}
cout<< "Enter 'P' again to continue, 'K' to cull half of all bunnies, 'E' to show list of bunnies, enter anything else to exit\n";
cin>> c;
while(c=='K' || c=='E') {
if (c=='K')
bunnylist.cull();
if (c=='E')
bunnylist.echo();
cout<< "Enter 'P' again to continue, 'K' to cull half of all bunnies, 'E' to show list of bunnies, enter anything else to exit\n";
cin>> c;
}
}
}
Bunny.h
#ifndef BUNNY_H
#define BUNNY_H
#include <iostream>
#include <string>
#include <iomanip>
using namespace std;
class Bunny{
public:
Bunny(int x, int y, int a);
friend class listfunc; // lets listfunc access Bunny
string whatname(); // creating a list of bunny names
bool ifradio(); // for deciding if bunny is radioactive or not
string whatcolor(); // for outputting color in text
void echo(); // reading out the bunny's parameters
private:
bool male; // is it male
int color; // color represented by int
int age;
string name;
bool radio; // is it radioactive
Bunny *next;
int X, Y; // bunny's coordinates on grid.
};
#endif // BUNNY_H
Bunny.cpp
#include "Bunny.h"
Bunny::Bunny(int x, int y, int a)
:male(rand()% 2), color(a), age(0), name(whatname()), radio(ifradio()), next(NULL), X(x), Y(y) {}
string Bunny::whatname() { // creating a list of bunny names
string names[14] = {"Thumper", "Oreo", "Bunn", "Coco", "Cinnabun", "Snowball", "Bella",
"Midnight", "Angel", "Shadow", "Hops", "Bugs", "Floppy", "Whiskers"};
return names[rand()%14];
}
bool Bunny::ifradio() {
int a = rand() % 50; // 1/50 chance
return ((a==0) ? true : false);
}
string Bunny::whatcolor() { // for outputting 1 of 4 colors.
int a = color;
switch(a) {
case 1:
return "white";
break;
case 2:
return "brown";
break;
case 3:
return "black";
break;
case 4:
return "spotted";
break;
}
}
void Bunny::echo() {
cout <<setw(7)<<left<< ( (male) ? "Male" : "Female" ) <<setw(8)<< whatcolor() << age << " " <<setw(9)<< name << ((radio) ? "Radioactive" : "Normal") << "\n";
}
listfunc.h
#ifndef LISTFUNC_H
#define LISTFUNC_H
#include <iostream>
#include <string>
#include "Bunny.h"
class listfunc {
public:
listfunc()
: head(NULL), tail(NULL), num(0), radionum(0) {
for (int x=0; x<Xmax; x++) {
for (int y=0; y<Ymax; y++) {
grid[x][y] = blank; //initialise the grid
}
}
}
void create( int x= rand()%Xmax, int y= rand()%Ymax,int a = rand()%4+1 ); //create new bunny, parameter is for color.
void echo (); //output bunny stats
void baby(); //bunny reproduction function
void age(); //increase all age by 1, aka one turn
void old(); //kill all bunnies that are too old
void kill(Bunny *a); //the function used to kill any particular bunny
void cull(); //function to kill half of all bunnies
void radioinfect(); //function to infect one normal bunny for every radioactive bunny there is
void display() const; //outputs the grid
void movebunny(Bunny *a); //move the specific bunny a random position.
bool checkif(Bunny *a, int b, char c) const; //a function to check if the adjacent position is a particular char.
void event(); //a function to echo out the events of previous turn.
int whatnum() {
return num; // function to read num
}
private:
Bunny *head, *tail;
int num, radionum; // bunny and radioactive counter
const char blank = '.', jmale = 'm', jfemale = 'f', amale='M', afemale= 'F', radiob = 'X'; // for managing the grid legend.
static const int Xmax = 40, Ymax = 100; // set the size of the grid.
char grid[Xmax][Ymax];
int eventradio; // keeps track of how many radioactive bunnies infected per turn
string eventborn, eventdead; // keeps track of the bunnies dead or born per turn.
};
#endif // LISTFUNC_H
listfunc.cpp
#include "listfunc.h"
void listfunc::create(int x, int y, int a) { // create new Bunny, if no input is given, randomly assign 1-4 for color.
while(grid[x][y] != '.') { // check if spot is taken, check really only required for first 5 bunnies.
x = rand()%Xmax; y = rand()%Ymax; // randomly choose new spot
}
Bunny *temp = new Bunny(x, y, a);
temp->next= NULL;
if (head==NULL) {
head=temp;
tail=temp;
}
else {
tail->next=temp;
tail = temp;
}
if(temp->radio) {
eventborn.append("Radioactive bunny "); eventborn.append(temp->name); eventborn.append(" was born!\n"); // write to the events log.
radionum++;
grid[x][y] = radiob;
}
else {
(temp->male) ? grid[x][y] = jmale : grid[x][y] = jfemale;
eventborn.append("Bunny "); eventborn.append(temp->name); eventborn.append(" was born!\n");
}
num++;
}
void listfunc::echo () { // for outputting the bunny stats.
cout<< "Current bunny count is " << num << " with " << radionum << " radioactive bunnies!\n\n" <<"SEX COLOR AGE NAME RADIOACTIVE?\n";
Bunny *temp = head;
while(temp!=NULL) {
temp->echo();
temp = temp->next;
}
cout<< "\n";
}
void listfunc::baby() { // for bunny reproduction.
Bunny *temp = head; // sets temp back to head
int x=0; // counter for male/female
while(temp!=NULL && x==0) { // search for at least 1 male aged >1.
if(temp->male == true && temp->age>1 && temp->radio==false) {
x++;
}
else {
temp=temp->next;
}
}
if(x>0) { //if at least 1 male
temp=head; //sets temp back to head
while (temp!= NULL) {
if(temp->male == false && temp->age>1 && temp->radio==false) { // when temp points to a female Bunny aged >1
if (checkif(temp, 1, blank) || checkif(temp, 2, blank) || checkif(temp, 3, blank) || checkif(temp, 4, blank)) { //if at least one adjacent square is blank.
int w = 0;
while (w<1) { // if no bunnies is created.
int z = (rand()%4+1); // random direction.
if(checkif(temp, z, blank)) { // if random direction is empty, create bunny in that direction. else repeat loop with different random number.
switch(z) {
case 1:
create(temp->X-1, temp->Y, temp->color);
break;
case 2:
create(temp->X+1, temp->Y, temp->color);
break;
case 3:
create(temp->X, temp->Y-1, temp->color);
break;
case 4:
create(temp->X, temp->Y+1, temp->color);
break;
}
w++;
}
}
}
}
temp=temp->next;
}
}
}
void listfunc::age() { //increases all age by 1.
Bunny *temp=head;
while (temp != NULL) {
temp->age++;
if (temp->radio==false) { // if a non-radioactive turns 2, they will be represented by capital letter on the grid.
if (temp->male && temp->age == 2) {
grid[temp->X][temp->Y] = amale;
}
else if (temp->male == false && temp->age == 2) {
grid[temp->X][temp->Y] = afemale;
}
}
listfunc::movebunny(temp); // bunny moves randomly every turn.
temp=temp->next;
}
}
void listfunc::old() { //kill those that are too old
Bunny *temp=head;
Bunny *tokill;
while (temp != NULL) {
if(temp->radio) { // radioactive bunny dies at age 50
if(temp->age == 50) {
tokill=temp; //point tokill to the object to be killed.
temp=temp->next; //move temp to next object.
kill(tokill);
}
else {
temp=temp->next; // if no one is to be killed, move temp to next object either way.
}
}
else {
if (temp->age == 10) { // normal bunny dies at age 10
tokill=temp; // same procedure as above
temp=temp->next;
kill(tokill);
}
else {
temp=temp->next;
}
}
}
}
void listfunc::kill(Bunny *a) { // for deleting a certain bunny
Bunny *temp=head;
if(a==head) { // deal with the head first.
head=head->next; //change head to next in line
}
else if (a==tail) { //if its the last bunny
while(temp->next !=tail) {
temp=temp->next;
}
tail=temp;
temp->next= NULL;
}
else{ // if somewhere in between
while(temp->next != a) {
temp=temp->next;
}
temp->next= a->next; // link object before a to object after a
}
if(a->radio) {
eventdead.append("Radioactive bunny "); eventdead.append(a->name); eventdead.append(" has died.\n"); // write to the events log.
radionum--;
}
else {
eventdead.append("Bunny "); eventdead.append(a->name); eventdead.append(" has died.\n");
}
grid[a->X][a->Y] = blank;
delete a; // delete a (aka kill)
num--; // reduce bunny count
}
void listfunc::cull() { // function to kill half of all bunnies randomly.
Bunny *temp=head, *tokill;
int killnum = num/2, x=0;
while(x < killnum) { // trying to make a loop that doesn't break until 1/2 rabbits are killed.
while(temp != NULL) {
if (rand()%2) { // 50/50 chance to kill that bunny
tokill=temp;
temp=temp->next;
kill(tokill);
x++; //increase kill counter if a bunny is culled
if (x==killnum) {
break;
}
}
else
temp=temp->next;
}
temp=head; // go to start of list again if not enough bunnies are killed
}
}
void listfunc::radioinfect() { //function to for one radio bunny to infect other bunnies
Bunny *temp=head;
int x=0; // infected counter
while(temp != NULL) { // search through all the bunnies to find radioactive bunnies.
if (temp->radio) {
if ((checkif(temp, 1, blank) == false && checkif(temp, 1, radiob) == false && temp->X != 0) || (checkif(temp, 2, blank) == false && checkif(temp, 2, radiob) == false && temp->X != Xmax-1) || // check if adjacent has a
(checkif(temp, 3, blank) == false && checkif(temp, 3, radiob) == false && temp->Y != 0) || (checkif(temp, 4, blank) == false && checkif(temp, 4, radiob) == false && temp->Y != Ymax-1) ) { // non-radioactive bunny.
bool looping = true;
while(looping) { // if no bunny has been infected yet, looping won't continue.
int z = (rand()%4+1); // chose one of 4 directions.
if ( (checkif(temp, z, blank) == false) && (checkif(temp,z,radiob) == false)) { // checking if that direction is NOT a radioactive bunny or a blank space
switch(z) {
case 1:
if (temp->X != 0) { // required because checkif function will also return false if checking a position out of the grid.
grid[temp->X-1][temp->Y] = radiob; // all conditions satisfied, set that grid to a radioactive bunny first.
looping = false;
}
break;
case 2:
if (temp->X != Xmax-1) {
grid[temp->X+1][temp->Y] = radiob;
looping = false;
}
break;
case 3:
if (temp->Y != 0) {
grid[temp->X][temp->Y-1] = radiob;
looping = false;
}
break;
case 4:
if (temp->Y != Ymax-1) {
grid[temp->X][temp->Y+1] = radiob;
looping = false;
}
break;
}
if (looping==false) {
x++; radionum++;
}
}
}
}
}
temp = temp->next; // go next bunny is current bunny is not radioactive.
}
temp = head;
while(temp!=NULL) {
if (grid[temp->X][temp->Y] == radiob) // set all bunnies that is labeled as radioactive on the grid to radioactive.
temp->radio=true;
temp=temp->next;
}
eventradio = x;
}
void listfunc::display() const { // to display the rabbits in a grid
for (int x=0; x<Xmax; x++) {
for (int y=0; y<Ymax; y++) {
cout<< grid[x][y];
}
cout << "\n";
}
}
void listfunc::movebunny(Bunny *a) {
int z = rand()%5+1;
char old = grid[a->X][a->Y];
if (checkif(a,z,blank)) { //first check if direction given by z is clear or not.
switch(z) {
case 1: // move up
grid[a->X][a->Y]= blank; // swapping places with one position above on the grid.
grid[a->X-1][a->Y]= old;
a->X--; // change bunny internal X coordinate value.
break;
case 2: // move down
grid[a->X][a->Y]= blank; // swapping places with one position below on the grid.
grid[a->X+1][a->Y]= old;
a->X++; // change bunny internal X coordinate value.
break;
case 3: // move left
grid[a->X][a->Y]= blank;
grid[a->X][a->Y-1]= old;
a->Y--;
break;
case 4: // move right
grid[a->X][a->Y]= blank;
grid[a->X][a->Y+1]= old;
a->Y++;
break;
case 5: // stay still
break;
}
}
else this->movebunny(a); // if conditions don't meet, retry movement.
}
bool listfunc::checkif(Bunny *a, int b, char c) const {
switch (b) {
case 1: // check up
if( grid[a->X-1][a->Y] == c && a->X != 0 ) { // checking if one position upwards is char specified, and current X is not at highest point.
return true;
}
else return false;
break;
case 2: // check down
if( grid[a->X+1][a->Y] == c && a->X != Xmax-1 ) {
return true;
}
else return false;
break;
case 3: // check left
if( grid[a->X][a->Y-1] == c && a->Y != 0 ) {
return true;
}
else return false;
break;
case 4: // check right
if( grid[a->X][a->Y+1] == c && a->Y != Ymax-1 ) {
return true;
}
else return false;
break;
case 5: // movebunny() has 5 as a possible choice.
return true;
break;
}
}
void listfunc::event() {
cout<< "Events List\n\n";
cout<< eventdead << "\n";
cout<< eventborn << "\n";
cout<< eventradio << " bunnies were infected by radioactive bunnies!\n\n";
eventdead.clear();
eventborn.clear();
}
Also, the exercise requires a linked list. However is std::vector a much better option?
std::list
orstd::forward_list
. \$\endgroup\$std::vector
(andstd::string_view
) among others. \$\endgroup\$