15110 Fall 2011 [Cortina/von Ronne]

Programming Assignment 10 - due Tuesday, November 22 (NOTE: DATE CHANGE)

This assignment cannot be dropped. However, you may hand this assignment in late by Saturday, November 26 with a 2-point penalty.

For this assignment, you will create a Ruby source file containing a Ruby function(s) implementing each of the problems described below. If you find it useful for any of the problems in this assignment, you may define additional helper functions that your primary function for that problem calls to solve a sub-problem. Definitions for these helper functions should be included in the same Ruby source file as the primary function they help. You should store a source file for each problem in a folder named pa10.

Note: You are responsible for protecting your solutions to these problems from being seen by other students either physically (e.g., by looking over your shoulder) or electronically. (More information can be found in the instructions for Programming Assignment 2.)

Overview

In this assignment, you will develop a simple graphical simulation that shows how disease spreads through a population. Each time step of the simulation represents one day. A person starts off as healthy. Each day, a healthy person comes in contact with four random people. If any of those random people is contagious, then the healthy person becomes infected. It takes one day for the infected person to become contagious. After a person has been contagious for 4 days, then the person is non-contagious and cannot spread the virus nor can the person get the virus again due to immunity.

This simulation will show how the virus spreads in a population of 400 people, represented by a square 20 × 20 matrix. On the first day of the simulation, one person becomes infected with the virus.

Here is a sample of what the first eight "days" of the simulation might look like:


Each snapshot above is a time step of the simulation shown in a 200 × 200 window (in pixels). Notice that the window is "divided" up into 10 × 10 squares (in pixels). Each of these 400 squares represents one person in the simulated population.

To perform the simulation, you will need to use the Canvas object in RubyLabs. When you initialize a Canvas, you need to supply the size of the window and a title for the window. For example:

Canvas.init(200, 200, "Virus_Simulation")

creates a window of size 200 × 200 (in pixels) with a title of "Virus_Simulation". The origin of this window is at the top left corner (0,0), with the x coordinate increasing as you go from left to right, and the y coordinate increasing as you go from top to bottom.

To draw a square of size 10 × 10 with a fill color of green and an outline (border) of blue and its top left corner at coordinate (100,100) in the Canvas, we can execute:

Canvas::Rectangle.new(100, 100, 110, 110, :fill=>"green", :outline=>"blue")

The first two parameters are the top left coordinates (x,y) of the rectangle and the next two parameters are the bottom right coordinates (x,y) of the rectangle. The final two parameters specify the fill color and the outline (border) color as strings.

Complete the problems below to build this simulation in Ruby.

Problems

  1. [3 points] Write a function display(matrix) (in the file display.rb). The parameter is a 20 X 20 matrix representing the population for the simuation. Each person is a cell of the matrix. Each cell has an integer in the range 0 through 6 (inclusive), which encodes the person's health as follows:

    0    healthy, not infected
    1    infected
    2    contagious (day 1)
    3    contagious (day 2)
    4    contagious (day 3)
    5    contagious (day 4)
    6    non-contagious/immune
    

    Your function should go through the entire matrix and display each "person" as a square of size 10 pixels X 10 pixels in one of the following colors:

    white    healthy, not infected
    pink     infected
    red      contagious
    purple   non-contagious/immune
    

    General algorithm:

    1. For each row and column of the matrix, do the following:
      1. Set color equal to "white" if matrix[row][column] represents a healthy individual.
      2. Set color equal to "pink" if matrix[row][column] represents an infected individual.
      3. Set color equal to "red" if matrix[row][column] represents a contagious individual.
      4. Set color equal to "purple" if matrix[row][column] represents a non-contagious/immune individual.
      5. Draw a 10 X 10 square on the canvas based on the current row and column with a fill color as specified above and an outline of "black".

    Test your function using this Ruby function that creates a matrix of size 20 × 20 and fills each cell with a random integer between 0 and 6:

    def test_display()
            # create a canvas of size 200 X 200
            Canvas.init(200, 200, "Testing_Display")
            # initialize matrix a randomly
            a = Array.new(20)
            for i in 0..19 do
                    a[i] = Array.new(20)
                    for j in 0..19 do
                            a[i][j] = rand(7)
                    end
            end
    	# display the matrix using your display function
    	display(a)
    end
    

    Sample Usage:

    >> load "display.rb"
    => true
    >> load "test_display.rb"
    => true
    >> test_display()
    

    Your image will differ since we're using a random number generator.

  2. [4 points] Write a function update(matrix) (in the file update.rb). This function takes a 20 × 20 matrix representing our population, as described above, for the current time step of the simulation, and returns a new 20 × 20 matrix representing our population during the next time step (i.e. after one day has passed).

    The basic idea here is that we start with the current population in the array matrix and create a new snapshot of the population after one time step in an array new_matrix. Each cell new_matrix[row][column] represents the new status of the person from matrix[row][column].

    General Algorithm:

    1. Create a new 20 × 20 matrix, called new_matrix, with each cell initialized to 0. (HINT: Look at the test_display function above to see how to create and initialize a matrix.)
    2. For each row and column of the matrix, do the following:
      1. If matrix[row][column] represents a non-contagious/immune individual, set new_matrix[row][column] so this cell is also a non-contagious/immune individual.
      2. If matrix[row][column] represents an infected or a contagious individual, set new_matrix[row][column] so that it contains a value that is 1 + matrix[row][column]. (That is, the individual advances one position in the scale from 1-6.)
      3. If matrix[row][column] represents a healthy individual, do the following:
        1. Set new_matrix[row][column] equal to 0.
        2. Repeat the following four times:
          1. Pick a random person from the matrix. (See NOTE below.)
          2. If that person is contagious, then set new_matrix[row][column] equal to 1 (which means the healthy individual will be infected for the next day).
    3. Return the new_matrix as the final result.

    Test your update function using the following function:

    def test_update()
            # create a canvas of size 200 X 200
            Canvas.init(200, 200, "Testing_Update")
            # initialize matrix a to all healthy individuals
            a = Array.new(20)
            for i in 0..19 do
                    a[i] = Array.new(20)
                    for j in 0..19 do
                            a[i][j] = 0
                    end
            end
    	# infect one random person
    	a[rand(20)][rand(20)] = 1
    	display(a)
    	sleep(2)
    	# run the simulation for 10 "days"
    	for day in 1..10 do
    		a = update(a)
    		display(a)
    		sleep(2)
    	end
    end
    

    Sample usage:

    >> load "display.rb"
    => true
    >> load "update.rb"
    => true
    >> load "test_update.rb"
    => true
    >> test_update()
    

    NOTE: For this problem, if an individual is healthy and you pick 4 random people to come in contact with, it is ok if you pick the same person twice or if you pick the current individual you are testing.

  3. [2 points] Write a function all_immune(matrix) (in the file all_immune.rb) that takes a 20 × 20 matrix that represents our population of 400 people, and returns true if EVERYONE is immune, and false otherwise.

    We will use this function to complete our simulation, having the simulation stop once the entire population has become non-contagious/immune.

    Use the following function to test your final simulation:

    def run_simulation()
            # create a canvas of size 200 X 200
            Canvas.init(200, 200, "Simulation")
            # initialize matrix a
            a = Array.new(20)
            for i in 0..19 do
                    a[i] = Array.new(20)
                    for j in 0..19 do
                            a[i][j] = 0
                    end
            end
    	# infect one random person
            a[rand(20)][rand(20)] = 1
            display(a)
    	sleep(2)
    	# run simulation until everyone is immune
            while !all_immune(a) do
                    a = update(a)
                    display(a)
                    sleep(2)
            end
    end
    

    Sample usage:

    >> load "display.rb"
    => true
    >> load "update.rb"
    => true
    >> load "all_immune.rb"
    => true
    >> load "run_simulation.rb"
    => true
    >> run_simulation()
    

  4. [1 point] Create a new version of the simulation function from problem 3, named run_simulation_v2, that starts with n random infected people, where n is given as a parameter to the function. You may assume n is positive and is not greater than 400.

    Sample usage (to start with 5 randomly infected people):

    >> load "display.rb"
    => true
    >> load "update.rb"
    => true
    >> load "all_immune.rb"
    => true
    >> load "run_simulation_v2.rb"   (CORRECTED)
    => true
    >> run_simulation_v2(5)
    

    CAUTION: This is trickier than you might think. If you just pick n random positions in the matrix, you might pick the same position more than once! We want n different people in this problem.

    Submission

    You should now have a pa10 directory that contains the required files, display.rb, update.rb, all_immune.rb, and run_simulation_v2.rb, each—in turn—containing the corresponding function(s). Zip up your directory and upload it using the handin system. (The handin system will accept submissions beginning on Friday until the deadline Tuesday night. Submissions after the deadline but before the late deadline will be marked on our system as "late".)