CPSC 124 Introduction to Programming Spring 2024

Lab 11
Inheritance

Due: Tue 4/30 at the start of lab


Introduction

rabbit hunt screenshot

A fox (the red circle) is chasing a rabbit (the brown circle) in a field. The field contains a number of bushes (green squares), and is completely surrounded by a large hedge made up of a bunch of bushes. The hedge is impenetrable, so no animals can leave the field. The bushes obstruct the view of both rabbit and fox, so they may or may not be able to see each other at any given moment. There are also some sloths (gray circles), which are mostly sleeping ("Zz") but occasionally wake up (no "Zz") and make a move before going back to sleep. The sloths don't do much (they aren't interested in the fox and rabbit, and the fox and rabbit aren't interested in them), but they do obstruct the view of the rabbit and fox (which may work to the rabbit's advantage).

If the fox catches the rabbit, the fox gets lunch (and wins). If the rabbit can evade the fox for 100 turns, the fox gives up in disgust and leaves (and the rabbit wins). Will the rabbit escape?

Your task is to find out, by creating a program to simulate the chase just described. Inheritance and polymorphism will be essential for reducing the amount of code you need to write! (The goal of this lab is to gain some experience with inheritance and polymorphism, and with figuring out how to use a new class.)

This is based on an assignment by David Matuszek.


Collaboration

Labs and projects are for practice and learning. While it can be very productive to work on problems with your peers, it is also easy to underestimate how much you yourself understand and can do in such situations — so often something looks easy when someone else does it! With this in mind, you should always make the first attempt on a problem or programming task yourself using only the resources provided for the course (textbook, slides and examples posted on the schedule page, other resources linked on the course webpages). After that point, you are encouraged to come to office hours and/or go to Teaching Fellows. You may not collaboratively write solutions or code, and you may not copy solutions or code written by others, even if you contributed ideas.

You can discuss specific exercises with other students in general terms — such as how you might get started on that problem, or how or when to use various programming constructs, or strategies for debugging — but how to use a particular programming construct to solve a specific problem or debugging a particular program should only be discussed in office hours or with the Teaching Fellows.


Setup

A demo has been provided so you can see what the finished program should look like. Run it with:

jfx -jar /classes/cs124/demos/rabbithunt.jar

Use 'g' (for "go") to start the simulation, 'p' to pause, 's' to step a single step at a time, and 'r' to reset the current chase.


Provided Code and API Documentation

You have been provided with several classes:

You've also been provided with two .class files:

API documentation for the provided classes can be found below:


Exercises

For all exercises:

Also, you are once again working with multiple files and thus need to make sure that every file needed for the program is compiled. It is strongly recommended that you use the following to compile all of the .java files at once:

  jfxc *.java

Refer back to lab 9 for more on working with multiple files.

Note: it is very important that you name your classes and methods as directed below, and that you have the right header for each method. (i.e. the right types of parameters in the right order, and the right return type) If you don't, the provided code will not work! (You'll get compiler errors and possibly program crashes with NoSuchMethodError exceptions.) Start on the lab early enough so that you have time to ask if you run into problems you can't fix.

Second note: it is very important that you organize the functionality of your code correctly. Pay attention to what the language is telling you! Remember to be a lazy programmer — put functionality as high as possible in the inheritance hierarchy so that you aren't repeating things. If you get the organization incorrect, you may notice problems with the fox (e.g. the fox doesn't appear). Start on the lab early enough so that you have time to ask if you run into problems you can't fix.

A third note: You should include the lines

  import javafx.scene.paint.*;
  import javafx.scene.canvas.*;

at the very beginning of every file where drawing is involved.


Exercise #1

The field contains a number of different kinds of things, including foxes, rabbits, sloths, and bushes. These things have some elements in common (such as all being things that can be in a field), and the following inheritance hierarchy has been proposed:

  Thing --+-- Animal --+-- Fox
          |            | 
          |            +-- Rabbit
          |            |
          |            +-- Sloth
          |
          +-- Bush

This means that Thing is the top-level class, Animal and Bush are subclasses of Thing, and Fox, Rabbit, and Sloth are subclasses of Animal.

Note that while these specifications describe what functionality (instance variables and methods) each kind of thing should have, you'll need to decide where each instance variable and method should be declared, what methods and classes should be abstract and what methods should have bodies, and what should be private/protected/public. Make use of inheritance! (Pay attention to the language — if something applies to animals, for example, don't repeat that element for each particular kind of animal.) Also make use of super and this as needed to reuse code instead of repeating things.

Additional notes:

When you are done, compile all of the .java files in your directory (your new classes plus the provided ones) and then run the main program (RabbitHunt). If all is well, there should be no error messages and the program should work just like the demo version. (Remember to press the proper key to advance the simulation.)


Exercise #2

The simulation contains and coordinates all of the things in the field. You've been provided with a class Sim which has the basics for a simulation: it stores the field and the step number, and provides step and draw for one step of the animation and drawing the scene, respectively.

In addition to what is inherited, RabbitHuntSim should contain:

You may add additional private helper methods if you find that convenient. (A private helper method is a method which is used only internally within a class and so is private instead of public.)

If you haven't already looked at Field, you should do now (before you start writing RabbitHuntSim) — being familiar with what methods are available will make writing RabbitHuntSim much easier. Remember that you only need to look at public and protected things (constants, variables, methods) and the associated comments — while you can look at the method bodies if you are curious, they are not important for using the class.

Instance variable details: The array of animals is being used as a holder of a collection. It is not a 2D array corresponding to the grid of the field.

Constructor details: As usual, the constructor should initialize the instance variables and you should use the superclass constructor to initialize the inherited instance variables. For the new instance variables: the rabbit is initially free, and the array of animals should be initialized to contain one rabbit, one fox, and 15 sloths. The animals should be placed randomly in the field, along with a reasonable number of randomly-placed bushes. Things should not be placed on top of each other.

reset details: This method should reset the simulation, which means extending Sim's reset method to also randomly place a new group of bushes, reset and randomly place the existing animals, and restore the rabbit to freedom. ("Extending" means calling Sim's version of reset before doing the new tasks.) reset should not repaint the field.

step details: This method does most of the work involved in one step of the simulation: if the rabbit is free, Sim's step should be called and then, for each animal:

step should not repaint the field, and you should make sure your code works no matter where in the array the fox and rabbit are stored — don't assume that the rabbit is always in position 0, for example. This means you'll need to use instanceof to determine which animal is the fox and which is the rabbit — see the description of instanceof in section 5.5.3 in the text or its usage in AccountDemo3.java from class.

When you are done, compile all of the .java files in your directory and then run the main program (RabbitHunt). If all is well, the "*** provided RabbitHuntSim! ***" message should have gone away, there are no error messages, and your program should work just like the demo version.


Extra Credit

The rabbit is not very clever, and usually ends up caught. Write a new class SmarterRabbit which is just like Rabbit except that the getNextMove method is smarter about what direction it picks. Use any strategy you want to decide how the rabbit will move in each turn, but you may not change the provided Field class or change the methods/parameters in Rabbit or any of its superclasses. Include a comment with a description of your strategy.

Your score for the bonus will be proportional to the percentage of times the rabbit successfully evades the fox for 100 steps, for a large number of trials. You can test the success of your strategy by having RabbitHuntSim put a SmarterRabbit in the field instead of a regular Rabbit, and then using the provided RabbitHuntPounder program. RabbitHuntPounder just runs many trials quickly, without the graphical interface and without any delays, and reports the percentage of times the rabbit evaded the fox. (The regular Rabbit escapes less than 1% of the time.)


Handin and Grading

Don't forget to hand in your work when you are done!

As in lab 2 (and all labs), grading will be based on both functionality and style. Style means following the good programming style conventions outlined in lab 2, as well as producing reasonably compact and elegant solutions. "Reasonably compact and elegant" means that your program is not significantly longer or more complex than it needs to be. In particular, you must use loops when loops are appropriate — achieving repetition by copying-and-pasting a bunch of code will not earn credit.