CSE 142 Homework #5

A Rather Fishy Simulation


Summary


 

The Problem

This assignment introduces the concept of a simulation. A simulation represents some natural process using an established set of rules. In a way, it is much like animation. There is a series of rounds (like frames), and each round we apply the rules of the simulation. In fact, computer animation is simply a simulation of real motion.  In this assignment, we will be simulating a lake, populated by fish. These fish move, breed, and die according to a set of rules. The program you'll write will display a lake with fish, and run the rounds of the simulation, applying the rules.  We'll again be using GP 142 for this assignment. If you need to look something up, refer to the GP 142 User's Guide.

Preliminaries

First, download the sample executable. Take some time to familiarize yourself with the program flow, the animation, etc. The green squares are fish. Then, download the starter code, from hw5_vc6.zip or hw5_vc6.exe. The distribution includes the folllowing files:

Rules of the simulation

Rules:

The screen is divided into a grid, with the number of rows and columns determined by program constants given in the starter code. For a particular square, another square is adjacent if it is immediately above, below, to the left, or to the right. The edges of the screen wrap to the other side. That is, the leftmost squares are adjacent to the rightmost squares, and the topmost squares are adjacent to the bottomost squares.

Each square in the grid may be empty, or occupied by a single fish.

The simulation starts with some number of fish occupying squares in the grid.

Fish movement. Each round of the simulation, each fish moves in a random direction to an adjacent unoccupied square, if one exists. If not, the fish stays put.

Fish breeding. The breeding time for a fish is set as a program constant. When a number of rounds has passed equal to the breeding time, the fish produces another fish in a random adjacent unoccupied square, if one exists. If not, no fish is produced. The counter for breeding begins when the fish is created, and is reset when breeding time is reached (even if no fish is produced). Note that this means a single fish can produce multiple offspring during the course of the simulation.

Fish crowding. Fish don't like to be crowded. If on any turn all four adjacent squares to a fish are occupied by other fish, the fish in the center square dies.

Turn order. During each round, each fish is processed independently, and the fish are processed from top to bottom and from left to right. A particular fish first moves, then breeds (if necessary), and then determines whether it is crowded and dies (if necessary). Then the next fish is processed. Each fish is processed once per round (even if the fish moves to a location in the grid that is yet to be processed).

Specification

Your program should run as follows:

Your program should display the the grid and identify which squares are occupied by fish.

Your program should implement the simulation according to the rules above, and should run indefinitely, subject to the specifications below.

The initial number of fish will be set as a program constant. At the start of the simulation, this number of fish should be placed in random locations throughout the grid. See the section on random number generation.

In addition to the initial number of fish, the numbers of rows and columns and the fish breeding time will be set as program constants. Your program should work correctly despite the values of these constants.

Included code

We've provided you with the following code:

Program constants to define various aspects of the simulation, and to set the boundaries of the screen. You should only be concerned with the constants defined under the comments "Size of the grid" and "Fish breeding parameters".

A grid data structure. Represented as a 2-dimensional array of structs, this data structure stores some basic information about the grid, namely whether each square is occupied. You will need to extend the GridSquare struct and define additional data structures as necessary.

main(). Defines the GP 142 event loop, and a few variable declarations and function calls for the simulation. You will need to fill in code here.

convertGridToGP142(). This function translates grid coordinates (row and column with (0,0) at the upper left) to GP 142 coordinates (x and y, with (0,0) at the center). The GP 142 coordinate represents the upper left hand corner of the grid square at those grid coordinates.

drawScreen(). Just an empty body here, to give you something to start from.

findFreeAdjacentSquare(). Given the grid and a square within that grid, finds a random unoccupied adjacent square if one exists. This function is useful for fish movement and breeding.

Hints

The following hints may be helpful in completing this assignment:

You might want to write the program in the following order:
First, try to get the drawScreen() function working, at least to draw the basic grid. Make liberal use of the convertGridToGP142() function.

Next, figure out how to extend the provided data structure or add additional data structures to store the information you need (where the fish are, when they need to breed, etc.). Think about what information is necessary to process the fish.  Remember: each fish is processed once and only once per round.  This information could be associated with each fish as a boolean "isProcessed".  When a new round begins, be sure to set isProcessed to FALSE.

Next, write code to initialize the grid, i.e. place the fish.

Next, try to define some function to update the simulation every round. You'll probably want to iterate over the grid and process each fish independently. Try to get the fish to move first.

Finally, extend the above function to handle fish crowding and breeding.

To see what's happening in your grid, turn off the animation and step through it one frame at a time.  This will help you debug your code if something is not working properly.

You will need to define several additional functions to write the program effectively.

Submission Details

Only turn in the file hw5.c. This is the only file that you should modify. We will provide copies on the turnin server for all the other files when we compile your program.

Random number generation

In computer programs, we often need to produce random numbers. More specifically, we often want to produce a random integer in some range (like a random integer between 1 and 10). C provides a library function to handle this, called rand(). rand() produces a random integer in the range 0 to RAND_MAX, where RAND_MAX is a #define constant (and is really big).

So, how can we use rand() to produce a number between 1 and 10? Well, the number of integers in this range is 10 (1,2,...,10). If we take rand() % 10, we'll get a random integer between 0 and 9, which also has 10 numbers. Add 1 to this total, and voila, you've got a random number between 1 and 10. In general, to obtain a random number in the range [min_num, max_num], use the expression:

(rand() % (max_num - min_num + 1)) + min_num

You might, for example, want to generate a random number in the range [0,NUMCOLS].

rand() needs to be initialized sometime before it's first called, and this is done by calling a function called srand(). srand() takes a single integer argument, and this argument determines which "random" numbers rand() will produce. If srand() is called with the same arguments (or is not called), your program will produces the same random numbers every time it runs. This can be very useful for debugging. To get truly random numbers, we need to call srand() with a random number, and programmers usually use the time of day using the call srand(time(NULL)). time() is defined in <time.h>, and time(NULL) returns the current time.