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
- VirusSim/ (index of individual files)
- VirusSim.tbz (bz2 compressed tarball of all 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