CSE 8B: Introduction to Computer Science: Java (Winter, 2006)

Home | Syllabus | Schedule | Exams | Programming Assignments

Programming Assignment 4

Due Monday, February 6 at 11:59 PM.

Last modified: February 6 at 5:30 PM Updated explanation of Doodlebug dying (it looks a lot more like dying after 4 time periods)

Previous modifications:

Overview

You'll be creating a predator/prey simulation. The program will read in a representation of ants and doodlebugs in a rectangular board. Here's an example with 5 Doodlebugs, and 7 Ants.
.........D
...A......
A.DD....A.
.....D...A
.A...A..AD
Both Doodlebugs and Ants reproduce (although at different rates), and move. Doodlebugs eat Ants. You'll be running a simulation where you iterate through all the Critters, having each critter do its actions for a given timestep. Here are the actions:

Input/Output

The input will be a file containing three numbers followed by a representation of the board
the number of rows in the board
the number of columns in the board
the number of timesteps to simulate
board
The board has one line per row, with each location on the board represented with one of: Here's sample input:
10
5
10
.........D
...A......
A.DD....A.
.....D...A
.A...A..AD

The output is a sequence of board descriptions, one for every requested timestep. Here's the desired output for the sample input:
At time 0:
.........D
...A......
A.DD....A.
.....D...A
.A...A..AD

At time 1:
...A....D.
..........
.DD.......
....DA..AA
A.......AD

At time 2:
.......D..
.DDA......
....D...A.
......A..A
A.......AD

At time 3:
..........
.DD.AA.D.A
...D.....A
.....AA.AD
A.......A.

At time 4:
.D...A...A
.D....D...
..D.AA..A.
.......AA.
A......A.D

At time 5:
.D...A...A
..........
...A..AA.A
..........
A.....A.D.

At time 6:
......AA..
.........A
.....AAA.A
A..AA.AA.A
A......D..

At time 7:
......AA..
........A.
...A.AAA.A
.A...AADA.
A.........

At time 8:
......AA..
......AA.A
.A..AA.AA.
.....AA...
.A....DD..

At time 9:
......AA.A
A...AAA.A.
A..A.AA...
AA.A.ADAAA
......D...

At time 10:
A.....A..A
....AAAA.A
A..AAAA...
A...ADA...
.A....D.AA

Your output must match exactly the output we are expecting. I recommend you test your application with:
java Simulation < testInput.txt > myOutput.txt
diff myOutput.txt testOutput.txt
If diff reports any difference, your output doesn't match. If diff doesn't print anything, that means your output (myOutput.txt) is identical to the provided testOutput.txt.

Provided files

All files are in ~/../public/P4.

There are four Java files provided: a Random number generator in Rand.java, and a class representing pairs of integers in Pair.java.

I've also provided you Simulation.java and TestSimulation.java. You can choose to use these files as-is, use them as a starting point for your code, or write your own code from scratch. You'll still need to turn in the Simulation.java file (even if it is unchanged).

The random number generator has one method: nextDirection which returns an integer from 1 to 4. The direction should be interpreted as:

It is vital that you follow this ordering, otherwise, your critters will move (and give birth) in different directions than we're expecting, and your code will not pass our automated testing.

The Rand class has two constructors, a default one, and one taking an array of integers. You need to use the default constructor to solve this exercise, however, the other one can be useful for your unit testing (see Unit Testing section).

The Rand object will always generate the same sequence of random directions. That way, the directions the critters move is reproducible; it'll be the same in each student's application, as well as in my solution.

In addition to the Java files, there are test input and output in ~/../public/P4/tests. For each test, there are three files:

For example, there are files Test1in.txt, Test1out.txt, and Test1outdebug.txt.

Unit Testing

Although I highly suggest you use Test-Driven Development to develop your application, and have a complete set of unit tests, it is not required.

This assignment is large enough that unit tests will help in writing your application tremendously. In order to use unit tests, you'll be testing the functionality of your classes, and not doing much testing of the actual input/output of this program (that's a form of acceptance testing, rather than unit testing).

In order to make your critters move in exactly the direction you want (for testing purposes), you can use the Rand class with one constructor that takes an array of directions. So, if you want to test placing an ant on the board, and having it move right, you could create a Rand object with:

    int [] arr = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, };
    Rand r = new Rand(arr);
The rand object will throw an exception if you call nextDirection more times than the length of the array.

Organization

Your program should be organized into four classes:

Plan of Atack

Here's the procedure I'd follow to get solve this project. It's an incremental approach that takes advantage of the given unit tests.
  1. Get the thing to compile and pass its first test
    1. I'd comment out the code in readBoard; there's a lot to do before trying to deal with reading in boards and running time steps. I'd comment out the code because it tries to instantiate Ants and Doodlebugs, and I don't want to worry about Doodlebugs until sometime down the road.
    2. Create a Critter class with the 2 needed abstract methods (trying to compile Simulation.java will tell you what those methods are).
    3. At this point, Simulation.java should compile.
    4. Comment out tests but the first in TestSimulation.java. We do this because the tests won't work, and we want to build up working tests.
    5. Comment out the code that instantiates a new ant in the setUp method of TestSimulation.
    6. Comment out the declaration of the Ant in TestSimulation.
    7. At this point, you should be able to pass the first test: testEmptySimulation.
  2. Get the second test, testWithOneCritter, to pass by uncommenting out the code dealing with Ants from TestSimulation you commented out in the last step. You'll also need to actually write an Ant class that overrides the two methods from Critter. Your oneTimeStep doesn't need to do anything yet!
  3. At this point, lots of other tests probably work (all the ones that test the Simulation up through testMoveCritterUpLeftAtEdge).
  4. Get the testSimpleStep to work. You'll need to actually make oneTimeStep do something in the Ant, but it need not worry (yet) about anything in the way).
  5. Get testSimpleStepWithImpediment working. You'll need to check before moving to see if there's anything in the way.
  6. Get testManySteps to work. That'll require writing a Doodlebug class that implements the two abstract methods from Critter. The Doodlebug will also need to move. Once you get this test working, refactor by moving common code shared between Ant and Doodlebug to a common method in Ant. For example, if you have a move method in Ant and a move method in Doodlebug that both do the same thing, create a single move method in Critter.
  7. Get testDoodlebugEat to work. That'll require separate code for Doodlebugs and Ants because Ants can't move onto other critters, but Doodlebugs can move onto Ants. Most of the code for moving will be the same between the two classes, but the code for checking whether it's valid to move onto a space will be different. So, make a method in Critter for doing that checking. Override it one way in Ant and another way in Doodlebug.
  8. Get testDoodlebugBreed to work. You'll need to keep track of timesteps in order to do this.
  9. Note that there are no unit tests for ants breeding or for doodlebugs dying. You'll probably want to write these and get them working.
  10. Now you're in a position to start testing with input. Uncomment out the code in readBoard and start testing the overall program rather than the unit tests. If you find that the sample output doesn't match your output, trace through the detailed dump and find how it works differently than yours. Write a unit test based on that analysis and get the unit test working. Then, try testing with the input again. One very important note: you've got to be calling the randomDirection method the same number of times and in the same order as the test did, otherwise you'll get out of sync.
  11. When your code is handling all the inputs correctly, refactor. Find any common code you can that's shared between Ant and Doodlebug and move it into methods in Critter. Find any common code in Critter and put that in new methods. Remember, don't repeat yourself.

Hints

Turnin

Create a directory on ieng6 with the following files: To turn in your assignment, cd to that directory and run the command
  bundleP4
and carefully follow the dialog. If it is before the due deadline and all necessary assignment files are present, this will turn in your solution files. You can run bundleP4 more than once if you want; only the last one will count.

Grading

We'll be running your simulation from the command-line with input of our own, and checking the output, which will need to match identically (we'll be doing a diff from the command-line). Some of the tests will also involve replacing the random number generator with a different one, so be aware that the sequence of random directions may be different when your program is tested.

The assignment is worth 20 points:

As always, an additional 5% (1 point) is available if you turn the assignment in at least 24 hours early.