CS 302 Summer 2012, Program 3 Programming Assignment #3 for CS302ers Summer 2012

Program 3, Summer 2012

DUE by 11:59:59 PM on Tuesday, August 7th
NO LATE PROGRAMS OR PROGRAMS SENT VIA EMAILS WILL BE ACCEPTED!

P3 Announcements | Overview | Game Play | Design Specifications | Implementation Details | Development Milestones | Running with CLAs | Testing | Submission

P3 Announcements

Corrections, clarifications, and other announcements regarding this programming assignment will be found below.

  • 7/29/2012: Please let me know by Wednesday, August 1st for partner info, no requests will be accepted after Aug. 1st.
  • 7/29/2012: Program 3 assigned.

Overview

Assignment Goals

  • Gain experience with Object-Oriented Programming.
  • Understand classes by reading their javadocs.
  • Use Java's ArrayList class.
  • Implement, in whole or in part, basic instantiable classes for which we've defined the interface.

Background

Since Apple unveiled its iPhone at the Macworld 2007 convention at the Moscone Center in San Francisco, the phone has altered the smartphone landscape and ushered in the modern era of portable, intelligent, connected devices. And it’s shaken up the industry and forced changes and upheaval among many competitors like Nokia, RIM, Motorola.

Today, many people have a smartphone and a PC, and a tablet is an adder. With a smartphone or a tablet, you can manage your life by using applications for lists, to-do notes and calendars. You can communicate with other computers, such as your laptop, desktop or office computer. You can create and receive e-mail and text messages. While you're waiting in line somewhere, you might even use the device to play a game, solve a puzzle or listen to music.

Among all things a smartphone can do, playing games is probably the most popular one. And among all games available for mobile devices, one has been awarded "Best Game for Handheld Devices", called Angry Birds. No matter what mobile platform you use, iOS, Android, Windows Phone 7, or Blackberry, Angry Birds is usually the top pick.

The very important thing that you have to know on how to play Angry Birds is, you have to do your best to small all the enemies like marmosets and evil pigs. This can be done by slinging all the available Angry Birds. It will depend on how you are going to shatter the ugly pigs; whether you hit the enemies directly or you create a hiding place to fall them. In each level, you will be given a few angry birds to shoot the enemy’s territory. By restricting the number of birds you use, you will gain additional points for that. Thus, your creativity as well as your innovativeness in shooting the birds will allow you to move forward in the game.

In this programming assignment, you are gonna implement a program which can be considered a simplified version of Angry Birds. Instead of the grumpy birds and evil pigs, gorillas are used for attackers && targets. Instead of risking their lives as attackers by slinging themselves, they use bananas as the objects thrown. After the implementation of the game is done, the game windows after running the program should look like the 2 pictures shown below. The 1st picture shows the state of banana flying (NOTE: The little yellow thing in the window is not a waning moon, but a flying, airborne banana) while the 2nd one shows the game state of aiming.

Screenshot1 here
Screenshot2 here

Game Play

We already said that our game involved gorillas tossing bananas at each other. Now let's look at how the game works in more detail.

The game consists of multiple rounds. At the beginning of each round, new buildings get created and gorillas are placed atop those buildings. Gorillas take turns tossing bananas at other gorillas and get points for hitting them. Once a gorilla is hit, it is removed from the game for the rest of the round. When there is only one gorilla remaining, the next round begins. After a certain number of rounds, the game ends and the gorilla with the most points is declared winner. If more gorillas share the same high score at that point, i.e., if there is not a single winner, the game continues for a few more overtime rounds that involve only gorillas who have the high score. Additional overtimes are added as necessary until a single winner emerges.

A Closer Look At One Turn

A gorilla's turn starts with the gorilla aiming. The gorilla can change the angle and the velocity at which to toss the banana. The angle of the toss is indicated by a red line and the velocity is indicated by the length of an overlapping yellow line.

Once the gorilla finishes aiming, it tosses the banana at the angle and the velocity that was set. The laws of physics take over as the banana flies through the air.

If the banana flies out of bounds (see below), the next gorilla gets its turn and no points are awarded. If a banana hits a gorilla, the gorilla that tossed the banana gets 2 points, unless the gorilla hit itself, in which case it loses 3 points.

When the banana hits a gorilla or a building, there is a loud splat that produces a powerful shock wave that is harmful to nearby gorillas. The shock wave doesn't damage the buildings (it could, but we've chosen not to make the game anymore complicated). If the shock wave hits a gorilla, the gorilla who tossed the banana gets a point, unless the shock wave hits itself, in which case it loses 3 points. Once the banana's shock wave stops spreading, it is the next gorilla's turn.

When a gorilla is hit by a banana or the shock wave, it is removed from the game and is not displayed for the remainder of the round. Removed gorillas cannot toss bananas or get hit by them, and don't get any more turns this round. The gorilla joins back in for some more fun at the beginning of the next round.

Design Specifications

Program Components

We use multiple classes to complete our program. Note we've kept the descriptions in this section short and put the details in the javadocs for each class that are accessed by clicking on the class names listed below. Make sure you carefully read these javadocs. Recall that the method summaries do not contain all the information about the methods. For complete specifications, you'll need to see the method details by clicking on the method names in the summary section or by scrolling down the javadoc page.

Classes you'll need to complete:

  • The Building class represents buildings. These buildings serve as obstacles and also as places for gorillas to stand on.
  • The Gorilla class represents a player in the game.
  • The Banana class represents a projectile that gorillas toss at each other.
  • The Game class ties together the three components of the game (buildings, gorillas and a banana) and handles all the game mechanics except for gorillas aiming which is handled in the GameController.

Classes we've already completed:

  • The GameGUI class displays the game in a window.
  • The GameController class allows one to play the game. It processes key presses made by players and changes what happens in the game accordingly, keeps the game running, and tells the GameGUI to periodically update its display.
  • The MainClass class contains the main method. You should run it in order to play the game.

Ticks, a.k.a. Real World versus Computer World

Time passes continuously in the real world. A thrown banana moves through the air at any given point in time, and it can hit an obstacle or a gorilla at any point in time as well. However, computer simulations of the real world require that time be partitioned into small discrete steps which we will call ticks. A tick is a certain amount of time, for example ten seconds. Each event may happen only once during each tick. For example, if each tick takes 10 seconds, a banana is immediately placed at the location where it would be 10 seconds from now, given its current position and the direction and velocity of its movement. We also check whether a banana hits a building or another gorilla only once every 10 seconds. Of course, to get computer simulations as close as possible to the real world, we want tick lengths that are much shorter than 10 seconds as otherwise a banana could "fly through a gorilla" without hitting it. In your program, tick lengths will be roughly 0.025 seconds.

Here is an analogy that gives some more intuition behind ticks. Think about what happens when you watch a movie. What happens on a movie screen looks like smooth continuous movement to the human eye. However, the reality is different. A movie consists of a lot of frames (still shots, or photos if you like) that get displayed in a sequence, one after another after another, with tens of frames being displayed every second. The human eye is not fast enough to notice that it's actually watching a sequence of photos that change at a very fast speed. Getting back to ticks, a movie "tick" would be analogous to hiding the current photo, moving to the next frame on the film reel, and showing that frame for, say, 0.025 seconds.

Now let's see what happens during one tick of our game. Nothing really happens when a player is aiming because aiming is not handled by the Game class, so let's only consider what needs to happen after aiming is done and the banana is thrown.

When a banana is flying, its position and velocity change according to the usual laws of Newton mechanics, taking wind into account. After updating the banana's position, we check whether the banana at its new position collides with some gorilla and then whether it collides with some building. If it does, the banana splats and the shock wave starts spreading with its current explosion radius being 0 (see the Banana class for more detail). Scores get updated accordingly (if a gorilla is hit) at this time.

If the banana splatted already, the radius of the shock wave increases by one. After that, we check whether the shock wave hit a gorilla and update scores accordingly if it did. Once the shock wave reaches its maximum radius, the tick ends and the next turn starts.

User Interface

Players see the game displayed in a graphical user interface (GUI). The GUI displays the game area as well as some information about the current state of the game: which round it is, the scores of all players, whose turn it is, and the wind velocity.

Players use the keyboard to play the game. Here is a summary of all the key presses that can be made together with what they do.

  • UP: Increase the velocity at which to toss the banana by 1.
  • Ctrl + UP: Increase the velocity at which to toss the banana by 10.
  • DOWN: Decrease the velocity at which to toss the banana by 1.
  • Ctrl + DOWN: Decrease the velocity at which to toss the banana by 10.
  • RIGHT: Change the angle at which to toss the banana by 1 degree clockwise (increase angle by 1).
  • Ctrl + RIGHT: Change the angle at which to toss the banana by 10 degrees clockwise (increase angle by 10).
  • LEFT: Change the angle at which to toss the banana by 1 degree counterclockwise (decrease angle by 1).
  • Ctrl + LEFT: Change the angle at which to toss the banana by 10 degrees counterclockwise (decrease angle by 10).
  • N: Start a new game.
  • Q: Quit the game, which ends the program.
  • Enter: Throw the banana.

Implementation Details

Start by creating a new project and downloading the partially completed code as well as images that we've provided.

  • p3_code.zip This file contains the partially completed code for this project. Unzip these files in your project directory.
  • p3_images.zip This file contains all the image files necessary for the program to display correctly on the screen. Unzip these files in a sub-directory in your project directory that you name "images".

When you write your code, you must implement the methods EXACTLY as they appear in the skeletons we've provided. By this we mean:

  • Do not change method names.
  • Do not add or remove parameters.
  • Do not change method return types.

Of course, you will need to change each method's body so that it works as described. Note that the code we've provided will use your implementations of the unfinished classes. Our code will only work when you implement the classes as specified in their javadocs.

Before you start writing code for a class, you should look into the provided code skeleton. Sometimes there is code provided to you already and suggests what some of the instance variables should be called. You'll need more instance variables than those that are already mentioned. The comments in the skeletons provide additional information as well as suggestions for how to write your code.

Dimensions, Distances, Directions, Bounds

We need to keep track of where individual parts of the game are. We use Cartesian coordinates to keep track of individual points. Since buildings, gorillas and bananas are not points, we also need some additional information about how large our objects are.

We assume that buildings and gorillas are rectangles, and for each building and each gorilla, we keep track of the position of the bottom left corner and also their height and width. For a banana, we assume it's a circle and keep track of the position of its center and the radius. The game area itself also has a width and a height. The x and y coordinates of the lower left corner of the game are both 0.

We summarize all the position and size properties in the list below.

  • left indicates the x coordinate of the bottom left corner of a building or a gorilla
  • middle indicates the x coordinate of the middle of a gorilla
  • bottom indicates the y coordinate of the bottom left corner of a building or a gorilla
  • width indicates the width of a house, a gorilla, or the game area.
  • height indicates the height of a house, a gorilla, or the game area.
  • x indicates the x coordinate of the center of a banana.
  • y indicates the y coordinate of the center of a banana.
  • radius indicates the radius of a banana.

The figure below shows all the properties we just listed. The thick rectangle represents the game area. The origin of the coordinate system (the point where both the x and the y coordinate are zero) is in the lower left corner of the game area. Note that there are two rectangles around the gorilla. The outer rectangle (dotted lines) is the rectangle for the purposes of positioning the gorilla. When we consider collisions between a banana and a gorilla, however, we only consider a smaller rectangle (green dashed lines) whose borders are 5 units inside of the larger rectangle. This is done so that in the actual game we don't see a banana hitting the gorilla even though the picture of the gorilla doesn't reach out that wide everywhere. The banana itself (which is not drawn to scale in this picture) is approximated by a circle (also shown in green dashed lines). Finally, note that some buildings can be partially off the game area. Also note that left plus width of a building should be the same as the left of the building to the right of it.

Picture here

Recall that a new turn starts if a banana goes out of bounds. The area that is considered in bounds is infinitely high and its borders are shown using the dashed blue lines. A banana is considered out of bounds if the circle we use to approximate it does not intersect the area that is considered in bounds. We show a few examples in the figure below. The green examples are in bounds and the red examples are out of bounds. The thick rectangle is the game area.

Picture here

Finally, we need to describe a banana's flight. When a gorilla tosses a banana, the banana is placed 30 units above the gorilla's head, with the same x coordinate as the middle of a gorilla. Its velocity has two components: the x velocity and the y velocity. The initial x and y velocities are determined from the angle and the velocity parameters of the Game's tossBanana() method. While the user interface shows the angle as an integer between -90 and 90, the tossBanana() method's angle parameter is a value in radians. The value is between -pi/2 and pi/2. The initial x velocity is given by velocity times sin(angle) plus the wind velocity (you can ignore the wind initially and add its effects later), and the initial y velocity is given by velocity times cos(angle). The point where the banana starts is in the figure below, and the three arrows indicate some angles at which bananas can be tossed. Also note that wind blows along the x axis. A positive wind speed means the wind blows to the right and a negative wind speed means that the wind blows to the left. This is consistent with the fact that we are adding the wind velocity to the x velocity of the banana.

Picture here

Once a banana is airborne, its position and velocity change as follows during a given tick. The equations should be familiar to you if you took any introductory physics course.

  • newX = X + xVelocity * tickLength
  • newY = Y + yVelocity * tickLength - 0.5 * gravity * tickLength^2
  • newYVelocity = yVelocity - gravity * tickLength

Game States

It is good to have variables that store which phase the game is in. For example, you will probably have an instance variable that stores the number of the current round, and also one for whose turn it is. Each turn is also broken down roughly into three parts as indicated in the design section of this document. We represent the state as an integer, but since the variable corresponds to a concept that is not an integer, we use class constants for the individual values to make them more understandable. In particular, we will have constants for the following three phases of the turn:

  1. aiming - The gorilla is still figuring out the angle and the velocity at which to toss a banana.
  2. banana thrown - The gorilla finished aiming and a banana is now flying through the air.
  3. banana splatting - The banana hit a building or a gorilla and the shock wave from the explosion is now spreading through the game area.

A good reason for keeping track of the state is that often some actions are only permitted under certain conditions. For example, a gorilla can only toss a banana when it's aiming, and we do not allow bananas to be tossed while a banana is already flying. Moreover, it is often difficult to state (even in English) when a particular action is allowed. One way to make the statement easy is to simply say that an action is permitted only if the game is in a particular state.

Development Milestones

To make your work easier, here is an outline that tells you what steps you should take in order to get a working program. Before you move on to the next step, test that what you wrote so far works correctly. Your grade for correctness of execution will be proportional to how many of these steps you finish. The numbers in parentheses for each of the steps indicate how much of the 90 percent you get for correctness of execution if you correctly complete a step and all the steps before it.

  1. A window opens and buildings appear (15%):

    The following should be done in this step:

    • A complete implementation of the Building class
    • The Game constructor sets up the height and the width of the game area and there is code which places buildings in the game by adding them to the buildings instance variable provided in the code skeleton.
    • The getWidth() and getHeight() accessors of the Game class.

  2. Gorillas appear (8%): Now that you know you can code up a component of the game, add another one—the gorillas. The following should be done in this step:
    • A partial implementation of the Gorilla class, namely the constructor, the accessors getName(), getBottom(), getLeft() and getMiddle(), and also the place() method.
    • Code in the Game constructor that places gorillas on top of buildings. Make sure that each player is on top of a different building and that there is at least one building between every pair of players.
    • The getNumGorillas() accessor in the Game class.
  3. Gorillas take turns tossing a banana (15%): Now that we have buildings and gorillas, let's add the banana and make it move. This is where you first encounter ticking. The following should be done in this step:
    • The constructor and the getX() and getY() accessors in the Banana class.
    • The getTurn(), getTurnState(), getCurrentGorilla(), getBanana() and tossBanana() methods in the Game class. You'll also need some additional code in the Game constructor.
    • Enough of the tick() methods in the Game and Banana classes to make the banana move and to start the next turn when the banana goes out of bounds. There is no need to check whether the banana hit anything. We'll leave that for the next step.
  4. Playable game (15%): Now that all the parts have been coded up and you've had some experience with ticks, it is time to make a version of the program that actually feels like a game. The following should be done:
    • The methods getScore(), addToScore() wasHit() and setWasHit() in the Gorilla class.
    • The methods isSplatting(), splat() and getCurrentShockWaveRadius() in the Banana class.
    • The constructor and methods getRound(), getNumRounds(), isGameOver() and getWinningGorillas() in the Game class.
    • Update the tick() methods in the Game and Banana classes so that collisions between the banana and, gorillas and buildings are handled. The splat shock wave of a banana should also be spreading at this time and you should handle the shock wave hitting gorillas. A new turn or round should only start after the banana's shock wave finishes spreading (or if the banana went out of bounds).
  5. Polish game (10%): Now that we have a game which one can play, let's add more features to make the game interesting.
    • A complete implementation of the Gorilla class.
    • A complete implementation of the Game class. That is, overtimes should now happen when there is a tie at the end of the last round, wind should be present and changing randomly each turn, and the players can start a new game.
  6. Turn Time Limits (14%):

    To make the game more challenging, gorillas should only be allowed a certain amount of time to aim. If a gorilla runs out of time, it loses its turn and the next gorilla gets its turn.

    To implement this, you will add another constructor to the Game class. This constructor gets an additional parameter in comparison to the constructor you wrote earlier, namely the number of ticks a gorilla can spend aiming. You will also write two accessors, getTicksPerTurn() and getTicksLeftThisTurn(), and update the tick() method to keep track of the fact that a gorilla spent another tick aiming without actually tossing a banana, ending its turn if it runs out of time. See the Game javadoc for more detail on these methods.

    To set the turn limit when running the program, add two more program arguments in the run configurations dialog. The first two arguments should be -t followed by an integer number of seconds. These two arguments are then followed by all the other arguments you entered earlier. For example if you want to give each gorilla 30 seconds to aim, the program arguments from an earlier example become
    -t 30 1024 700 4 2 9.8 Jim Deb Beck
    If you don't want the time limits, either omit the first two command line arguments or set the number of seconds to zero. The MainClass will take care of translating the number of seconds to the right number of ticks for you. The GUI will display a warning message and a countdown for the last five seconds of aiming time automatically.

  7. Splatting Mid-Air (8%):

    It can be quite challenging to hit a gorilla, so Exploding Bananas Inc. designed a new kind of banana that can splat while flying. The gorilla who tosses the banana controls when the banana splats. As before, once the banana splats, it stops moving and the shockwave starts spreading from the place where the banana splatted. If the banana hits a building or a gorilla before the player chooses to splat the banana, the banana splats the way it used to before. Since Exploding Bananas Inc. did not want to oversimplify the game too much, a player is not allowed to splat the banana if it still has the potential of hurting the gorilla who tossed it.

    Start by adding a splatMidAir() method to the Game class. If there is a banana flying right now, the method should splat the banana, the banana should stop moving and the shock wave should start expanding the same way as when the banana hits a building or a gorilla. However, this should only happen if the banana's x coordinate and the x coordinate of the middle of the current gorilla differ by at least half the gorillas width plus the banana's shock wave radius. The latter is done to prevent a player from fixing their mistake after a gorilla tosses a banana in a way that could hit it (either directly or by the splat shock wave).

    Second, add code to the keyReleased() method of the GameController class. Right now this method makes a gorilla toss a banana when the player hits enter and the game is in the aiming state. Expand this code so that when the banana is flying, the banana splats if it is far enough from the gorilla who tossed it (as described in the previous paragraph).

  8. Displaying More Information (5%):

    Some information that should be easily accessible is currently hidden in the status bar below the game area. This may be frustrating. Your goal is to modify the GUI code so that it displays the following when a player is aiming.

    • Whose turn it is - This is displayed at the top of the game area. The text should be centered and should contain the player's name.
    • Current angle and velocity - This information should appear in a box next to the aiming pointer. If angle is negative, it should appear to the left of the arrow, and otherwise it should appear to the right.
    See the screenshot(same as the 2nd picture in the "Overview" section) below which shows these new features. For displaying the angle and velocity, the distance between the tip of the red aiming arrow and the top corner (either top left or top right depending on which side of the aiming arrow the text is displayed) of the text box should be 10.

    SCREENSHOT HERE

    Modify the update() method of the GameGUI class for this. Before you start, you may find it helpful to look around that method and also the private displayString() methods it uses. The comments should explain everything you need in order to display text and draw a box around it. You may find it helpful to write a private helper method that displays multiple lines of text in a box. One thing you should be careful about is that while a y coordinate of zero corresponds to the bottom of the game area in Game, the Graphics2D object you'll be using has a y coordinate of zero at the top.

  9. Enjoy the game: This is not a step in the project. Successfully completing the first eight steps correctly will give you full credit for correctness of execution.
    Please remember that you probably have other things to do besides playing your game :).

Running Your Program with Command Line Arguments

You will need to do something different from what you're used to in order to run your program in Eclipse. The main class uses command line arguments which you need to specify.

To run the program, right-click MainClass.java and choose Run AsRun Configurations. See the picture below.

Screenshot here

Clicking Run Configurations brings up a dialog that includes some additional details about how to run your program. In this dialog, you should go to the second tab labeled Arguments. In the program arguments text field, you must enter the following information separated by spaces: ("-t" and number of ticks a gorilla can spend aiming), the width of the game area, the height of the game area, the number of rounds, the number of overtime rounds, the gravity constant (recommended 9.8), and then a space-separated list strings that represent player names. There must be at least two and at most four player names. After you specify the program arguments, click Run in the lower-right corner. You can see an example in the picture below.

Screenshot here

Testing

You should write driver programs as you develop your instantiable classes to ensure they work as specified. You may assume that the incoming values for parameters are valid. Of course, you'll need to make sure you provide valid information in your method calls, but you are NOT required to add code that validates the parameter values.

As you develop your program, it can be difficult to find mistakes in your code when something is continuously happening like in this game program. In order to help you debug, I have enhanced the user interface with the ability to pause the game, run it one tick at a time, and display information about the game on the console. In order to get the last bit of functionality, you must write toString() methods in all your classes for debugging purpose.

Here is the list of additional key presses you can use.

  • P: Pause/resume the game.
  • S: Works only if the game is paused. Calls the toString() method of the game.
  • T: Works only if the game is paused. Runs one tick of the game.

Submission

Before handing in your program, check that you've done the following:

  • Did you verify that your program works correctly? 90% of your grade is based on your program working correctly. Note to get full credit you must make sure your program works for normal input/circumstances and is robust (i.e., works reasonably for abnormal/unusual/erroneous input/circumstances).
  • Did you turn in all necessary source files listed below?
  • Did you use good programming style by following the CS302 Style Guide? About 5% of your grade is based on good programming style.
  • Did you properly comment your program by following the CS302 Commenting Guide? About 5% of your grade is based on proper commenting.

Submit these files to the dropbox for Programming Assignment #3 in Learn@UW:

  • "Building.java" with your implementation of the Building class
  • "Gorilla.java" with your implementation of the Gorilla class
  • "Banana.java" with your implementation of the Banana class
  • "Game.java" with your implementation of the Game class
  • "GameController.java" with your implementation of the features in the GameController class.
  • "GameGUI.java" with your implementation of the features in the GameGUI class.