$30
CS300: PROGRAMMING II
Pair Programming is NOT allowed for this assignment.
This assignment involves reorganizing the code from your P2 Swim Sim (Graphic) program, and
then adding interactions among different kinds of objects. The fish will be able to eat food when
they get close enough, and the hook will be able to catch fish. As usual, the code for this assignment
will be used as the basis for future programming assignments, so be sure to keep your code clean,
clear, and well commented.
Objectives and Grading Criteria
The goals of this assignment include using instantiable classes to organize your program in a more
object oriented style. In addition to reorganizing the functionality that you have previously
implemented, you’ll also be adding features related to the interactions between objects in this
simulation.
Grade Breakdown:
25
points
5 zyBooks Tests: automated grading test results are visible upon submission, and allow
multiple opportunities to correct the organization and functionality of your code. Your
highest scoring submission prior to the deadline will be recorded.
25
points
5 Hidden Tests: automated grading tests are run after the assignment’s deadline. They
check for similar functionality and organizational correctness as the zyBooks Tests. But
P3: SWIM SIM (CLASSES)
LECTURE NOTES
you will NOT be able to resubmit corrections for extra points, and should therefore
consider and test your own code even more thoroughly.
GETTING STARTED
0. If it’s not already there, open the project containing your P2 code in Eclipse. Make a copy of this
project and name this copy whatever you’d like, although P3SwimSim would be a descriptive
choice.
1. Update the SwimSim.jar file in your project. The functionality within this .jar file remains
unchanged, but this updated file does a much better job of relaying information about problems
within your code back to you.
REFACTORING THE TANK
2. Up to this point, our simulation has been organized into static methods. We’ve tried to generalize
these methods to work well with different kinds of objects, but these kinds of generalizations
often lead to overly complex methods with many dependencies. A more object-oriented
organization might aim to separate out these concerns and dependencies into separate
instantiable classes. Another benefit of these instantiable classes is that they make it much easier
for us to scale the number of objects of any kind in our simulation, or even to run multiple
simulations at once.
3. Let’s start by creating a new class called SwimSimulation: right-click your project folder in the
“Package Explorer”, then choose “New” then “Class” (and make sure that the package field in the
resulting dialog is left empty). Instead of using the Data class from P2, we’ll group all of our
simulation data here in our SwimSimulation objects. Add the following private instantiable fields
to your SwimSimulation class:
PApplet processing;
int[][] fishPositions;
int[][] foodPositions;
int[][] hookPositions;
4. Next we’ll add a constructor to initialize these fields whenever a new SwimSimulation object is
instantiated. This constructor will need to take a PApplet reference as its only input parameter,
but should be able to instantiate arrays of appropriate sizes to track the positions of: 4 fish, 6
food, and 1 hook. Note that you can call Main.generateRanomdPositions(…) to help out with this.
5. Add an instance method named “update” to our SwimSimulation class. This method should have
no parameters or return value. Since the job of this method will be to update a simulation, let’s
start by copying the contents of our our old Main.update() method into the body of our new
SwimSimulation.update() method’s body. Remove the “data.” portion of all field reference from
the SwimSimulation.update() copy, so that these references are to a SwimSimulation object’s
fields rather than a Data object’s fields. Then add Main-dot (Main.) in front of each
moveAllObjects() and placeObjectInTank() call, so that our old static methods can continue to be
called from this other class.
6. The last method that we’ll add to our SwimSimulation class will be:
The body of this method can be copied from Main.onClick(), as long as the “data.” references
have been removed (as in the previous step).
7. Now that all of this code has been re-organized into the SwimSimulation class, let’s run our
simulation through this class by:
Adding a private static field of type SwimSimulation to our Main class.
Instantiate a new SwimSimulation object from the Main.setup() method, and store its
reference in that previously mentioned field. The rest of the code in this method should be
removed (or at least commented out).
Call that SwimSimulation object’s update() method from Main.update(). The rest of the
code in this method should be removed (or at least commented out).
Call the SwimSimulation object’s handleClick() method from Main.onClick(). The rest of
the code in this method should be removed (or at least commented out).
8. Running your simulation should now work as well as it had before we started this reorganization
process! This may not look like much of an improvement, but sometimes things need to get
worse before they get better. On to getting better…
REFACTORING FISH, FOOD, AND THE HOOK
9. Create a new class called Fish, to encapsulate the code responsible for simulating each fish: each
Fish object will represent a single fish in our simulation. Each fish should keep track of its own
int x and int y position, along with a reference to the PApplet that it should be drawing itself to,
and a reference to the PImage that describes its appearance.
10. The Fish constructor should take a reference to a PApplet as its only input, and should initialize
all of the fields within that fish. The Fish class should also contain an update() method with no
parameters or return value, that moves itself one pixel to the right (with wraparound), and then
draws itself to the screen. Note that you’ll be using Utility.randomInt() instead of
generateRandomPositions() to initialize each Fish object’s initial position.
11. Go to the SwimSimulation class and replace the private field fishPositions with an array of Fish
references. Then visit each of the places in this class that used to reference fishPositions, and
update them to instead use this array of fish objects. Note that you’ll be replacing call to our old
static method with calls to instantiable methods in our Fish class, as you proceed.
1 public void handleClick(int mouseX, int mouseY) {}
12. Test that your fish are now moving correctly before proceeding to create similar classes called
Food and Hook for those objects. The main difference will be the direction that their update()
method moves them in, and the images that they use to draw themselves to the screen. The Hook
class will additionally need a method: void handleClick(int mouseX, int mouseY) so that it can
update its own position whenever it is clicked. When the mouse is clicked, the Utility class will
call your Main.onClick() method, which should call your SwimSimulation.handleClick() method,
which should call your Hook.handleClick() method, which should correctly update that Hook’s
position. After implementing these new classes, return to your SwimSimulation class and make
sure it uses objects of these new class types instead of the old position arrays. Since there is no
longer any benefit to keeping your hook data in an array, keep only a single Hook reference field
inside your SwimSimulation object.
HANDLING COLLISIONS
13. A difficulty when organizing code into classes is figuring out where to put code that deals with
interactions between different kinds of objects. For example, we could add code allowing Fish
objects to eat Food objects into the Fish class, into the Food class, or into a different class all
together. Since the fish is doing the eating in this case, we’ll add this code to the Fish class, in the
form of a method with the following signature:
In order to eat, a fish object needs to be close enough (within 40 pixels) of a food object. But
since the Food’s position fields are private, we’ll need to add some way for the Fish to determine
how close it is to the Food object it is trying to eat. If the food is close enough for eating, we’ll also
need some way of letting Food objects know when they have been eaten. Let’s add the following
methods to the Food class for these purposes:
After implementing these methods in the Food class, you should be able to use them to
implement the Fish.tryToEat() method. Then make sure that every fish attempts to eat every
food by calling this method repeatedly from the beginning of your SwimSimulation.update()
method. distanceTo() Note: if a refresher on calculating the distance between two points would
be helpful, check here. getEaten() Note: Instead of actually removing any food objects from our
simulation, we are instead moving eaten objects to give the appearance of old food
disappearing and new food simultaneously being added to the simulation.
14. In addition to Fish eating Food, we’d also like to have Hooks catching Fish, so add the following
method to your Hook class for this purpose:
1 public void tryToEat(Food food) {}
1
2
public float distanceTo(int x, int y) {} // returns the distance from this food to position
public void getEaten() {} // moves this food to a random position at the top of the screen
1 public void tryToCatch(Fish fish) {}
In order to detect whether the hook is close enough (within 40 pixels) to a fish to catch it, and to
let the fish know when they are caught, add the following methods to the Fish class:
After implementing these features go ahead and run you simulation to ensure that they are
working and to see how many fish you can catch in one cast (one sweep of the hook from bottom
to top). Note: When fish are caught, we are NOT animating them to follow the hook they are
caught by. Instead we are instantaneously moving them to produce an effect similar to when
Food are eaten by Fish.
15. Congratulations on finishing this third CS300 assignment! After verifying that your work is
correct, and written clearly in a style that is consistent with the course style guide, you should
submit your work through zybooks. If the automated tests detect defects in your code, you may
fix those and re-submit for full credit, although you will need to wait an hour between
submissions. The most recent of your highest scoring submissions prior to the deadline of 17:00
on Thursday, September 28th will be used as part of your score for this assignment.
Additional grading tests will then be run against your highest scoring submission, to determine
the rest of your assignment grade.
1
2
public float distanceTo(int x, int y) {} // returns the distance from this fish to position
public void getCaught() {} // moves this fish to a random position along the left edge of th