Virus Simulation

For my software development class (csci3081w) during the fall of 2005, we probably spent the better part of the semester working on a single project, the requirements of which were given to us in three phases (one, two, three), and several of which changed throughout the course of the project. While we were given a few strict requirements, such as the use of inheritance to implement some portion of the virus spread and vaccination models, the general program design, class structure, etc, was left to our choosing. I believe everyone worked in groups of two (or maybe three?), but I, along with my partner Adam LaPitz, implemented a simulation of a ‘city’ (rectangular grid) that was to be both infected and vaccinated according to a variety of selectable parameters and pre-defined models.

Yes, I realize this is a lot of writing to introduce some code, but really nothing compared to the dozen plus pages of documentation we wrote about the software design, class structure, functionality/interoperability, etc, and comparatively little next to the > 2000 lines of code comprising the full project. Although the situation can’t quite compare to a regular business world software development project, our development style was probably closest to Extreme Programming, with nearly all of our work being done together (all of about 5′ apart) and communicating extensively. As such, it is unfortunately difficult to identify specifically which lines of the program were actually coded by each individual, but it’s certainly fair to say that we were both intimately involved with the project.

Files

Below is the file city.cpp, which was the class instantiated to track the status of each location within the simulation throughout the run of the experiment. It contains many accessors and modifiers utilized extensively in the infection and vaccination models.

/*
City.cpp
 
This file implements the City class definition in City.h. Its general purpose is
to store and access the data associated with a virtual representaiton of a City
in a simulation. A one-dimensional array of struct GridLocation (see City.h) is
dynamically alocated by the constructor to simulate a two-dimensional array in
representation of a rectangular city.
*/
 
 
#include "City.h"
 
using namespace std;
 
 
// Constructor
City::City(char code, int numRow, int numCol){
	int i;
 
	//set the class variables based on the values passed in
	cityCode = code;
	numRows = numRow;
	numColumns = numCol;
	currentDay = 0;
	daysContagious = 0;
 
	//dynamically allocate an array of GridLocations large enough to represent a (numRows x numColums)-sized city
	grid = new GridLocation[numRows*numColumns];
 
	srand( (unsigned)time(0) );	//seed random number generator based on time of instantiation
 
	//initialize all values in the city grid as appropriate
	for(i=0; i<numRows*numColumns; i++){
		grid[i].infected = false;
		grid[i].vaccinated = false;
		grid[i].day = -1;
		grid[i].infectionDay = 0;
		grid[i].vaccinationDay = 0;
		grid[i].highRisk = false;
	}
}
 
// Default/empty constructor
City::City(){
	cityCode = '0';
	numRows = 0;
	numColumns = 0;
}
 
// Destructor
City::~City(){
	delete [] grid;
}
 
//////////////////
// Accessor methods (const)
 
char City::GetCityCode() const{
	return cityCode;
}
 
int City::GetRows() const{
	return numRows;
}
int City::GetColumns() const{
	return numColumns;
}
int City::GetDayNumber() const{
	return currentDay;
}
 
 
// IsUnaffected basically tells us whether or not a location is infectable
bool City::IsUnaffected(int r, int c) const{
	//if neither infected nor vaccinated, the true
	if(!grid[r*numColumns + c].infected && !grid[r*numColumns + c].vaccinated){
		return true;
	}
	//if vaccinated, but vaccine not effective yet, then true
	else if(currentDay < grid[r*numColumns + c].day + daysTillVaccineEffective && grid[r*numColumns + c].vaccinated){
		return true;
	}else{
		return false;
	}
}
 
// A location must be vaccinated, and the vaccine must be effective for this to be true. More accurately, this function would be "IsEffectivelyVaccinated"
bool City::IsVaccinated(int r, int c) const{
	if(currentDay > (grid[r*numColumns + c].day + daysTillVaccineEffective) && grid[r*numColumns + c].vaccinated){
		return true;
	}else{
		return false;
	}
}
 
bool City::IsInfected(int r, int c) const{
	return grid[r*numColumns + c].infected;
}
 
bool City::IsContagious(int r , int c) const{
	if(grid[r*numColumns + c].infected && currentDay < (grid[r*numColumns + c].day + daysContagious)){
		return true;
	}else{
		return false;
	}
}
 
double City::GetVaccineIneffectiveness() const{
	return vaccineIneffectiveness;
}
 
bool City::IsHighRisk(int r, int c) const{
	return grid[r*numColumns + c].highRisk;
}
 
/* Checks if any of the 8 potential locations directly adjacent to grid[r,c] are
   infected. It provides special cases for each of the grid edges and corners.
*/
bool City::adjacentInfectionCheck(int r, int c) const{
		//top edge
		if(r == 0){
			//top left corner
			if(c == 0){
				if(	IsInfected(1,1) ||
					IsInfected(0,1)	||
					IsInfected(1,0)){
						return true;
				}
			}
			//top right corner
			else if(c == numColumns){
				if(	IsInfected(1,numColumns) ||
					IsInfected(1,numColumns-1) ||
					IsInfected(0,numColumns-1)){
						return true;
					}
			}
			//rest of the top edge
			else{
				if(	IsInfected(r,c-1) ||
					IsInfected(r+1,c-1) ||
					IsInfected(r+1, c) ||
					IsInfected(r+1 ,c+1) ||
					IsInfected(r, c+1)){
						return true;
				}
			}
		}
		//bottom edge
		else if(r == numRows){
			//bottom left corner
			if(c == 0){
				if(	IsInfected(numRows-1 ,0) ||
					IsInfected(numRows-1 ,1) ||
					IsInfected(numRows, 1)){
						return true;
					}
			}
			//bottom right corner
			else if(c == numColumns){
				if(	IsInfected(numRows-1,numColumns) ||
					IsInfected(numRows-1,numColumns-1) ||
					IsInfected(numRows,numColumns-1)){
						return true;
					}
			}
			//rest of bottom edge
			else{
				if(	IsInfected(r,c-1) ||
					IsInfected(r-1,c-1) ||
					IsInfected(r-1, c) ||
					IsInfected(r-1 ,c+1) ||
					IsInfected(r, c+1)){
						return true;
					}
			}
		}
		//rest of left edge (excluding corners)
		else if(c == 0){
			if(	IsInfected(r,c+1) ||
				IsInfected(r-1,c+1) ||
				IsInfected(r+1, c+1) ||
				IsInfected(r-1 ,c) ||
				IsInfected(r+1, c)){
					return true;
				}
		}
		//rest of right edge (excluding corners)
		else if(c == numColumns){
			if(	IsInfected(r,c-1) ||
				IsInfected(r-1,c-1) ||
				IsInfected(r+1, c-1) ||
				IsInfected(r-1 ,c) ||
				IsInfected(r+1, c)){
					return true;
				}
		}
		//non-edge/corner
		else{
			if(	IsInfected(r,c-1) ||
				IsInfected(r-1,c-1) ||
				IsInfected(r-1, c) ||
				IsInfected(r-1 ,c+1) ||
				IsInfected(r, c+1) ||
				IsInfected(r+1,c-1) ||
				IsInfected(r+1, c) ||
				IsInfected(r+1 ,c+1)){
					return true;
				}
		}
	return false;
}
 
//////////////////////
// Statistics collection and summary methods (const):
// Generally, these march through eveyr location in the city grid, and increment
// a counter if the condition in question is met
 
 
long City::NumberInfected() const{
	int total = 0;
	int i;
 
	for(i=0; i<numRows*numColumns; i++){
		if(grid[i].infected){
			total++;
		}
	}
	return total;
}
 
long City::NumberVaccinated() const{
	int total = 0;
	int i;
 
	for(i=0; i<numRows*numColumns; i++){
		if(grid[i].vaccinated){
			total++;
		}
	}
	return total;
}
 
long City::NumberVaccinatedAndInfected() const{
	int total = 0;
	int i;
 
	for(i=0; i<numRows*numColumns; i++){
		if(grid[i].vaccinated && grid[i].infected){
			total++;
		}
	}
	return total;
}
 
////////////////
// Print and display methods
 
void City::PrintDay(int dayToPrint) const{
	int i;
	cout << "City Code: \\t" << cityCode << endl;
	cout << "Number of Rows: \\t" << numRows << endl;
	cout << "Number of Columns: \\t" << numColumns << endl;
	cout << "High Risk is denoted by capital letters" << endl;
	cout << "Infected = I|i \\t Vaccinated = V|v "<<endl <<"Vaccinated and Infected = O|o \\t Unaffected = U|u"<< endl;
	cout << "Grid layout: " << endl; 
	for(i=0; i< numRows*numColumns; i++){
		if(i % numColumns == 0){
			cout << endl << i/numColumns << ": \\t" ;
		}
		if(grid[i].infected && grid[i].vaccinated && grid[i].highRisk && dayToPrint >= grid[i].infectionDay && dayToPrint >= grid[i].vaccinationDay){
			cout << "O ";
		}else if(!grid[i].infected && !grid[i].vaccinated && grid[i].highRisk){
			cout << "U ";
		}else if(grid[i].vaccinated && grid[i].highRisk && dayToPrint >= grid[i].vaccinationDay ){
			cout << "V ";
		}else if(grid[i].infected && grid[i].highRisk && dayToPrint >= grid[i].infectionDay){
			cout << "I ";
		}else if(grid[i].infected && grid[i].vaccinated && !grid[i].highRisk && dayToPrint >= grid[i].infectionDay && dayToPrint >= grid[i].vaccinationDay){
			cout << "o ";
		}else if(!grid[i].infected && !grid[i].vaccinated && !grid[i].highRisk){
			cout << "u ";
		}else if(grid[i].vaccinated && !grid[i].highRisk && dayToPrint >= grid[i].vaccinationDay){
			cout << "v ";
		}else if(grid[i].infected && !grid[i].highRisk && dayToPrint >= grid[i].infectionDay){
			cout << "i ";
		}else if(grid[i].infected && dayToPrint <= grid[i].infectionDay  && !grid[i].highRisk){
			cout << "u ";
		}else if(grid[i].vaccinated && dayToPrint <= grid[i].vaccinationDay  && !grid[i].highRisk){
			cout << "u ";
		}else if(grid[i].highRisk){
			cout << "U ";
		}else{
			cout << endl << "Error: " << i << endl;
			cout << grid[i].infected << " " << grid[i].infectionDay<< " " << grid[i].vaccinated << " "<< grid[i].vaccinationDay << " " << grid[i].highRisk << endl;
		}
	}
	cout << endl << endl; 
}
void City::Print() const{
	int i;
	cout << "City Code: \\t" << cityCode << endl;
	cout << "Number of Rows: \\t" << numRows << endl;
	cout << "Number of Columns: \\t" << numColumns << endl;
	cout << "High Risk is denoted by capital letters" << endl;
	cout << "Infected = I|i \\t Vaccinated = V|v "<<endl <<"Vaccinated and Infected = O|o \\t Unaffected = U|u"<< endl;
	cout << "Grid layout: " << endl; 
	for(i=0; i< numRows*numColumns; i++){
		if(i % numColumns == 0){
			cout << endl << i/numColumns << ": \\t" ;
		}
		if(grid[i].infected && grid[i].vaccinated && grid[i].highRisk){
			cout << "O ";
		}else if(!grid[i].infected && !grid[i].vaccinated && grid[i].highRisk){
			cout << "U ";
		}else if(grid[i].vaccinated && grid[i].highRisk){
			cout << "V ";
		}else if(grid[i].infected && grid[i].highRisk){
			cout << "I ";
		}else if(grid[i].infected && grid[i].vaccinated && !grid[i].highRisk){
			cout << "o ";
		}else if(!grid[i].infected && !grid[i].vaccinated && !grid[i].highRisk){
			cout << "u ";
		}else if(grid[i].vaccinated && !grid[i].highRisk){
			cout << "v ";
		}else if(grid[i].infected && !grid[i].highRisk){
			cout << "i ";
		}else{
			cout << endl << "Error: " << i << endl;
		}
	}
	cout << endl << endl; 
}
 
void City::StreamPrint(ostream &out) const{
	int r, c;
	for(r = 0; r < numRows; r++){
		for(c = 0; c < numColumns; c++){
			if(grid[r*numColumns + c].vaccinated && grid[r*numColumns + c].infected && grid[r*numColumns + c].highRisk){
				out<< cityCode << " " << r << " " << c << " V "<< grid[r*numColumns + c].vaccinationDay << " S "<<grid[r*numColumns + c].infectionDay << " H"<<'\\n'; 
			}else if(grid[r*numColumns + c].vaccinated && grid[r*numColumns + c].highRisk){
				out<< cityCode << " " << r << " " << c << " V "<< grid[r*numColumns + c].vaccinationDay<< " H" << '\\n';
			}else if(grid[r*numColumns + c].infected && grid[r*numColumns + c].highRisk){
				out<< cityCode << " " << r << " " << c << " S "<<grid[r*numColumns + c].infectionDay << " H" << '\\n';
			}else if(grid[r*numColumns + c].highRisk){
				out<< cityCode << " " << r << " " << c << " H"<<'\\n';
			}else if(grid[r*numColumns + c].vaccinated && grid[r*numColumns + c].infected){
				out<< cityCode << " " << r << " " << c << " V "<< grid[r*numColumns + c].vaccinationDay << " S "<<grid[r*numColumns + c].infectionDay <<'\\n'; 
			}else if(grid[r*numColumns + c].vaccinated){
				out<< cityCode << " " << r << " " << c << " V "<< grid[r*numColumns + c].vaccinationDay<< '\\n';
			}else if(grid[r*numColumns + c].infected){
				out<< cityCode << " " << r << " " << c << " S "<<grid[r*numColumns + c].infectionDay<< '\\n';
			}
		}
	}
}
 
ostream & operator<<(ostream & out, const City & cityToPrint) {
	cityToPrint.StreamPrint(out);
	return out;
}
 
////////////////////
// Modifier methods
 
 
/* The following two methods do not perform any error checking, but simply set
   a grid location to vaccinated or infected, and sets the related fields to the
   current day (of the simulation) for purposes of checking if a location is
   contagious or if the vaccine is effective yet.
*/
bool City::VaccinateLocation(int r, int c){
	grid[r*numColumns + c].vaccinated = true;
	grid[r*numColumns + c].day = currentDay;
	grid[r*numColumns + c].vaccinationDay = currentDay;
	return true;
}
 
bool City::InfectLocation(int r, int c){
	grid[r*numColumns + c].infected = true;
	grid[r*numColumns + c].day = currentDay;
	grid[r*numColumns + c].infectionDay = currentDay;
	return true;
}
 
/* Originally designed to perform some internal changes to set "just infected"
   locations to the regular definition of infected, FixGrid now just sets
   currentDay. It was left in the code for legacy compatability.
*/
void City::FixGrid(int newDay){
	currentDay = newDay;
}
 
void City::SetDaysContagious(int numberOfDays){
	daysContagious = numberOfDays;
}
 
void City::SetDaysUntilVaccineEffective(int days){
	daysTillVaccineEffective = days;
}
 
void City::SetVaccineIneffectiveness(double probability){
	vaccineIneffectiveness = probability;
}
 
void City::SetHighRisk(int r,int c){
	grid[r*numColumns + c].highRisk = true;
}
 

Download this code: VirusSim/City.cpp