p2-tefball

EECS 285 Project 2: Tefball

Project Due Friday, 8 Oct 2021, 8pm

For this project, you will implement a simple game called TefBall (tef = two eighty-five). The game has some remote resemblance to American football, but is very simple in comparison. No knowledge of football is necessary to be successful on this project.

TefBall is played on a rectangular field. The game consists of multiple objects on the field, including a ball, and some number of players. All players progress through the game via a function named performMove() – however, different types of players act differently, so this project will utilize polymorphism to provide multiple implementations (ways to move) for a common interface (the performMove() method). The primary focus of this project is the use of polymorphism, so be sure not to try to circumvent the use of it during your design. If you do not make good use of inheritance and polymorphism as expected, you will not receive credit for your project. The goal of TefBall is to successfully throw the ball and have a receiver catch it without being caught by a defender.

Authors

The original project was written by Andrew M. Morgan for EECS 285.

Table of Contents

Project Roadmap

This is a big-picture view of what you’ll need to do to complete this project. Most of the pieces listed here also have a corresponding section later on in the spec that goes into more detail.

This project will be autograded for correctness. We will also hand grade it for both programming practices and the comprehensiveness of your test cases. We will also run automated style and programming-practice checks on your code.

The following is a tentative grade breakdown for this project:

You may work alone or with a partner. Please see the syllabus for partnership rules.

Download the starter code

The starter code is available at https://eecs285.github.io/p2-tefball/starter-files.zip. The following files are included:

File(s) Description
PlayingField.java Game driver – implements the game simulation
PlayTefball1.java
PlayTefball2.java
Command-line interface, intended as test code
PlayTefball1.correct
PlayTefball2.correct
Correct output from running the test code

Extract the files to a temporary directory.

Set up your project

Follow the Project 1 setup tutorial to set up your project. Use the package name described below.

Your Java files should all be in a package with the structure eecs285.proj2.<uniqname>, where <uniqname> is your uniqname. If you are working in a partnership, then use both of your uniqnames, in alphabetical order, separated by an underscore. For example, if I am working alone, I would put the following package directive at the top of each .java file:

package eecs285.proj2.akamil;

If I am working with schatzju, then I would use the following:

package eecs285.proj2.akamil_schatzju;

Familiarize yourself with the game rules below

Read through and make sure you understand the rules for Tefball before writing any code.

Familiarize yourself with the required interface

Your implementation must adhere to the required interface described below.

Design, implement, and test your classes

Implement your game simulation in the PlayingField class that is part of the starter code. Define the other parts of the required interface and any other classes you need and place them appropriately as standalone classes in their own .java files, nested classes, or anonymous classes.

Write your test code in files that follow the PlayTefball*.java naming pattern. We will exclude them from some of the automated style checks (e.g. code duplication, since it is likely there will some duplication in setting up individual tests).

Submit

Submit the following files to the autograder.

As per course policy, we will grade your last submission to the autograder. It is your responsibility to ensure that your last submission is complete.

Early Submission Bonus

If your final submission occurs at least two days before the due date (at or before 11:59pm two days before the due date), you will receive bonus points calculated at 5% of your overall score for the project (including both autograder tests and hand grading). In other words, if your overall score is N, it will be 1.05 * N after the bonus is applied.

If your final submission occurs on the day before the due date (by 11:59pm), you will receive bonus points calculated at 2.5% of your overall score for the project. In other words, if your overall score is N, it will be 1.025 * N after the bonus is applied.

Game Rules

Player Types and Descriptions

There are only three types of players in this version of TefBall, and they are described below. Each specific type of player must inherit from a more generic class that represents any player, regardless of type. You should put attributes and functionality that apply to all players, regardless of type, in the base class, and attributes and functionality that apply only to specific types in the appropriate subclass. You should avoid placing functionality that doesn’t apply to all subclasses in the base class.

Quarterback

The quarterback is the player that has possession of the ball at the start of the game. In TefBall, there is exactly one quarterback, who acts in a very specific way. First, the quarterback starts at a specified location, and upon starting the game, moves toward a specified final position while holding the ball (that is, the ball moves with the quarterback at the beginning of the game, until thrown). Upon arriving at the final (actual floating point, non-rounded) position, the quarterback throws the ball to a pre-determined specified location. After throwing the ball, the quarterback stops moving and is no longer a factor in the game.

Receiver

TefBall allows multiple receivers to play at the same time. Each individual receiver starts at a specified location, and upon starting the game, moves toward a specified intermediate position. Upon arriving at the intermediate (actual floating point, non-rounded) position, the receiver changes direction and moves toward a specified final location. When a receiver arrives at the final location, they wait there, without further movement.

Defender

TefBall allows multiple defenders to play at the same time. A defender starts at a specified location, and upon starting the game, moves toward the quarterback’s current location. Once the quarterback throws the ball, the defenders change their strategy and begin running to the location that the ball is being thrown to (i.e. the ball’s destination).

Progression of the Game

As the game progresses, the user is shown a view of the field. Players and the ball move in a continuous way (i.e. their positions, and destinations are maintained and calculated as double-precision floating point values). However, when the field is drawn to the screen, an object in the game (ball or player) is drawn at the nearest (integer rounded) row and nearest (integer rounded) column. If two objects occupy the same row and column, the object that moved last is the one that is displayed. Since we are using polymorphism, players will move in the order they were specified by the user. The order that players move is an important strategy in TefBall, since if a defender moves before the quarterback moves and ends up occupying the same space, a sack will have been made. However, if the quarterback moves first, the quarterback may have moved to a different location before the defender arrives, and when the defender moves, they are occupying different spaces. Due to this, you must ensure that players move in the order specified, which is, in order of index – it is not acceptable for all receivers to move first, followed by all defenders, etc. However, players need not to be added in order of index.

For further context on in order of index, see @param playerIndex in add() methods in class PlayingField. You can assume this index is always 0-based and ranges to the number of players in the game.

The game proceeds in rounds. During each round, the players take turns moving. Before the ball is thrown, its position is the same as the quarterback’s, as described above. Starting the round after the ball is thrown, the ball moves to its destination in each round before any other players move.

Before a round of the game, the current state of the field is drawn to the screen as shown here for a field with width 16 and height 12:

 01234567890123456
0-----------------
1--------Q--------
2-----------------
3-----------------
4-----------------
5--------D--------
6-----------------
7----------B------
8----R-----R------
9-----------------
0-----------------
1-----------------
2-----------------

Note: Column and row indicators are provided to allow easy determination of game-object positions. The indicators are just the last digit of the 0-based column or row index. The largest column and row are equal to the width and height, respectively; since positions are rounded to the nearest integer when drawn, a column position of 15.8 would be rounded up to 16.

Field elements are printed as follows:

After the field is printed, each object in the game will perform a move. If, at the end of the round, the game is still ongoing, play continues with another round.

Ending the Game

All objects in the game continue to move as described above. The game ends when one of the following conditions occurs in your program:

Note that games don’t only end when a full round is complete. The game can end whenever any of the above criteria is met, even if some objects have not yet moved.

User Interaction

There is no user interaction for this project. Instead, we have provided two test files, PlayTefball1.java and PlayTefball2.java, that contain main() functions used to test the other classes. You should develop your own test files as well – the provided ones do not fully test your program. When grading, we will utilize PlayTefball classes that cover a wider range of test cases, so be sure to develop your own tests to ensure your implementation performs as expected in all cases.

We have also included the expected output from running the two test files. Your implementation must match the output exactly. Use the command-line diff tool or an online diff checker to make sure this is the case.

Error Handling

In an attempt to make this programming project less work, most error handling will not be required. You can safely assume we will not test your program with the following cases:

Some error checking is required, though. Specifically, in order to play a game of TefBall, the user (the person who wrote the main() function) must add exactly one quarterback and exactly the number of players they indicated would play in the playing field’s constructor. These criteria must be checked inside your implementation of the checkIsValidGame() method of PlayingField.

There certainly is a lot of error checking that could (and really should) be done for a project like this. However, you may assume that, except for the specific error checking described for checkIsValidGame(), the user will do what they are supposed to.

Required Interface

You must implement a specified interface so that users can write main() functions to test your version of TefBall. Unlike Project 1, however, you are free to make as many additional members (data and/or functions) as you need to complete the project. Some of your grade will be based on your design, so think carefully about where attributes or functionality belong, and make sure you don’t duplicate code more than absolutely necessary. When grading, we will be looking for inappropriate members, methods at the wrong level of the inheritance tree, etc. An example of an inappropriate member is having a variable called j in a class simply because many methods need a loop counter. Since that value doesn’t describe an attribute of the object at all, it should not be a instance field, even if that means declaring a loop variable in 12 different methods.

The following is the required interface you must develop. You must follow this exactly, including class and method names. However, you may add other data attributes, methods, classes, enumerated types, etc., as needed by your implementation. The additional items you add will not be utilized directly by the author of main().

Class: PlayingField

An object of this class will be used to represent the TefBall playing field that objects in the game move around on as described. Read through the documentation in the starter code for what you should implement. You must adhere to the interface provided in the starter code.

Enum: GameResultEnum

A list of possible outcomes from a round of Tefball. The possible values are: ONGOING, SACK, INTERCEPTION, RECEPTION, and INCOMPLETION. While the enum definition will likely be short, you must put the definition in its own Java source file named GameResultEnum.java.

Player-Type Classes: Quarterback, Receiver, Defender

You should define a class for each type of player. These classes allow you to instantiate player objects that participate in the game. Each player class has a unique implementation of a method named performMove(). You may use whatever parameters you need for this method.

A Base Player Class

While the individual player type classes have different implementations of performMove() (and potentially other methods too), the interface is common across different player types. Therefore, you’ll need to set up an inheritance tree and ensure the common interface is defined at a base class level (in Player or above). Polymorphism must be used to a full extent in this way, as this is the primary learning goal for this project.

You may find it appropriate to introduce additional base classes for interfaces and functionality that is common among other classes (e.g players and non-players).

Movement in the Tefball Simulation

All objects that move in TefBall move the same way. To move, first determine the object’s 2D vector of travel by subtracting its current location from its destination location. Next, make this 2D vector be a unit vector (with a magnitude of 1.0) by dividing the vector elements by the total length of the 2D vector. At this point, you have a unit vector describing the direction that the object will travel. Multiplying the unit vector by the object’s speed will indicate how far, and in which direction, the object could travel on its next turn. To ensure your objects move in the same way as the objects in our solution do, be sure to follow this algorithm when developing your solution.

Movement Example: Let’s say a Receiver starts its turn at a position of (4.0, 4.0) having a destination of (2.0, 6.0), and runs at a speed of 1.4143. The receiver’s movement would be performed as follows:

A game object never overshoots its destination. If the movement calculated above would take it past its destination, the object stops at its destination instead.

An object only performs one action in each turn. Specifically, a quarterback does not throw the ball until the turn after they reach their destination. (If a quarterback’s destination is the same as their initial location, they don’t throw the ball until their second turn.) Similarly, a receiver does not start moving to their final destination until the turn after they reach their intermediate destination. (If the receiver’s starting and intermediate locations are the same, they don’t move toward their final destination until their second turn.)

Requirements, Restrictions, and Clarifications

Final Thoughts

This project is fairly involved, but it does not necessarily require a lot of code. There is no requirement for number of lines of code, but just to give you a feel, my implementation has about 300 lines of code, excluding comments and blank lines. It’s fine if you have more or less than that, but if you find you’re writing 1200 lines of code, you may be making it more complicated than it needs to be.