Assignment 1: Outbreak!

Jan 22, 2024  │  m. Feb 7, 2024 by Charlotte Curtis

Due date: February 9, 2024 at 11:59pm

Introduction

Some tricksters in the biology department decided to increase school spirit by genetically engineering a virus to turn everyone’s hair blue. However, they need your help! They can’t figure out exactly what parameters to use to affect the largest number of students possible. They’ve described the virus behaviour, but it’s up to you to simulate it and find the maximum number of students that can have their hair turned blue.

Objectives

Note: This assignment is not to be completed using arrays, vectors, or any other data structure. The goal is to practice with the basic data types and control structures in C++.

The Virus

The virus is a simple one, but it has a flaw: infected persons will either have their hair turned blue (“afflicted”) or they will be able to infect others (“carriers”). The chance of any individual being afflicted is represented as $p$, while the number of other people that a carrier can infect is represented as $R_0$ . Your task is to find the $p$ and $R_0$ values that will afflict the largest number of people.

The range of $p$ values must be between 0 (no one afflicted) and 1 (everyone afflicted), while the range of $R_0$ values is restricted to between 0 (not at all contagious) and 20 (approximately as contagious as measles).

The Program

Your program should allow the user to input the start and end values for a range of $p$ and $R_0$, then simulate the virus for each combination, with a fixed number of 10 steps between the start and end values (inclusive). It should then print out the maximum number of people afflicted and the $p$ and $R_0$ values that produced that maximum.

Sample run, with user input in bold:

$ ./a1
$ Enter the range of R0 values (0 - 20): 0.5 12
$ Enter the range of p values (0 - 1): 0.1 0.95
$ Running scenarios, this may take some time
$ Maximum of 5088 afflicted at an R0 of 10.7 and p value of 0.5

Note: due to the randomness of the simulation, your results are likely to differ from the sample output.

In addition, your program should perform some basic error checking on the user input. If the user enters a range that is out of bounds (e.g. $R_0 > 20$), set the value to the maximum/minimum allowed value and inform the user, e.g.:

$ ./a1
$ Enter the range of R0 values (0 - 20): 0.5 25
$ 25 is too high, setting to 20
... etc

Finally, if the user enters the range backwards (e.g. start value is greater than end value), swap them. No need to inform the user in this case. For example:

$ ./a1
$ Enter the range of R0 values (0 - 20): 12 0.5
$ Enter the range of p values (0 - 1): 0.95 0.1
$ Running scenarios, this may take some time
$ Maximum of 4893 afflicted at an R0 of 8.2 and p value of 0.5

(Note that the numbers are somewhat different from the first sample run due to the randomness of the simulation.)

The Starter Code

cd to your 1633 directory (or wherever you keep your files for this course), then clone the starter code repository from /library/students/comp1633/a1.git using the following command:

$ git clone /library/students/comp1633/a1.git

This will create a directory called a1 in your current directory. cd into a1 and configure your assignment dropbox using the git-asg-config command as described in lab 2 . This time, the menu items have been updated to be shown in alphabetical order, so select option 1 to configure for assignment 1.

Inside this directory you will find the following structure:

$ tree
.
├── main.cpp
├── makefile
├── testing
│   ├── makefile
│   └── test_a1.cpp
├── virus.cpp
└── virus.h

It seems like a lot of files, but there’s a method to the madness! The bulk of your code should go in virus.cpp, while you should not need to modify virus.h, test_a1.cpp, or the two makefiles.

main.cpp

This file contains the main() function (your program entry point) and a get_range function declaration. You will need to:

virus.h

This file contains the declarations for the functions you will need to implement in virus.cpp. You should not modify this file.

In addition, virus.h defines the following constants:

You may modify these constants during development if you wish, but make sure to set them back to their original values before submitting. These numbers were chosen for a reasonable balance between runtime and accuracy.

virus.cpp

Here’s where most of the magic happens! You will need to implement the three functions declared in virus.h.

Testing and development tips

Test early, test often! Do not try to write the entire program at once - start with each piece and test as you go. For example, you can hard-code some $R_0$ and $p$ values in main and call single_trial to make sure it works. Then you can test mean_afflicted by calling it with a few different values. Finally, you can test run_scenarios by calling it with a few different ranges and checking the output. I don’t recommend adding the user input until the very end, as it’s much faster to just re-run a program when there are no prompts for user input.

As with labs, the starter code includes a test program that you can use to test the single_trial function. To run the tests, cd into the testing directory and run make. This will compile the test suite, at which point it can be run with ./test_a1. If all goes well, you should see the following output:

Running main() from gtest_main.cc
[==========] Running 8 tests from 1 test case.
[----------] Global test environment set-up.
[----------] 8 tests from SingleTrialTest
[ RUN      ] SingleTrialTest.ZeroP
[       OK ] SingleTrialTest.ZeroP (0 ms)
[ RUN      ] SingleTrialTest.ZeroR0
[       OK ] SingleTrialTest.ZeroR0 (0 ms)
[ RUN      ] SingleTrialTest.HighR0MediumP
[       OK ] SingleTrialTest.HighR0MediumP (0 ms)
[ RUN      ] SingleTrialTest.HighR0LowP
[       OK ] SingleTrialTest.HighR0LowP (1 ms)
[ RUN      ] SingleTrialTest.MediumR0MediumP
[       OK ] SingleTrialTest.MediumR0MediumP (0 ms)
[ RUN      ] SingleTrialTest.MediumR0LowP
[       OK ] SingleTrialTest.MediumR0LowP (0 ms)
[ RUN      ] SingleTrialTest.LowR0MediumP
[       OK ] SingleTrialTest.LowR0MediumP (0 ms)
[ RUN      ] SingleTrialTest.LowR0LowP
[       OK ] SingleTrialTest.LowR0LowP (0 ms)
[----------] 8 tests from SingleTrialTest (1 ms total)

[----------] Global test environment tear-down
[==========] 8 tests from 1 test case ran. (1 ms total)
[  PASSED  ] 8 tests.

If you see any FAILED tests, you should fix your code before submitting. Note that this test only checks the single_trial function - you will need to test the other functions yourself, but as they all depend on single_trial I thought it appropriate to provide some expected values for that function.

Repeatable randomness

Feb 7 update: I’m getting some questions about how to know whether the solution is correct. It’s tough with random numbers! The tests define a random number “seed” that makes it produce a repeatable sequence of values, but your main program sets the seed based on the current time, whatever that happens to be.

If you’d like to verify your results in a repeatable way, open main.cpp and comment out the following line:

// Random number stuff - do not modify
srand(static_cast<unsigned int>(time(NULL)));

Instead, set the seed to a fixed value, e.g.:

// Random number stuff - do not modify
// srand(static_cast<unsigned int>(time(NULL)));
srand(42);

With this change my program always produces the following result:

$ ./a1
Enter the range of R0 values (0 - 20): 0.5 12
Enter the range of p values (0 - 1): 0.1 0.95
Running scenarios, this may take some time
Maximum of 4822 afflicted at an R0 of 12.0 and p value of 0.5

(or 4821 if you are truncating instead of rounding the final number - I’m fine with either approach).

This is a good way to verify that your program is working as expected. Just remember to change the seed back to the original value before submitting!

Incremental development

A portion of the marks for this assignment is for “evidence of incremental development”. This means I am looking for several meaningful commits to your git repository rather than just adding your solution all in one go. This is ultimately a good habit to get into! I recommend committing your changes whenever you:

You may add, commit, and push your code as many times as you like up until the assignment deadline. I will only mark the final version, but I will look at your commit history to see how you developed your solution.

Marking scheme

This assignment is worth 8% of your final grade and roughly divided as:

Refer to the style guide for a reference on style and documentation. If you use external resources such as Stack Overflow, ChatGPT, or a friend in the class, make sure to cite them in your comments. Failure to cite external resources will be considered plagiarism. An example of a citation is as follows:

// Jordan Pratt helped me with the logic for this loop
for (int i = 0; i < 10; i++) {
    // ...
}

If your solution uses arrays, vectors, or other data structures or techniques not covered in class, you will receive a reduced grade, possibly as low as 0. If you have previous experience, try to challenge yourself to solve this problem using only the basics.

In addition, there will be an automatic 20% deduction if your code fails to compile or run. If the problem is extreme and I cannot fix it with a small change, a grade of 0 may be assigned. Make sure your code compiles and runs on INS with the following compiler flags:

$ g++ -ansi -pedantic-errors -Wall -Wconversion


Next: Assignment 2: Revisiting the Applicant Scoring Program