On this page:
Instructions
5.1 Space Invaders
5.1.1 Local Variables
5.1.2 Getters, instanceof, Casting, Exceptions, etc.
5.1.3 To IList or ILo?
5.1.4 A Note About Randomness
5.1.5 Submission format
5.1.6 Yikes, This Sure Is A Lot!
6.10

Assignment 5: Space Invaders Game

Goals: Design a game.

Instructions

This assignment is long. Start early.

Make sure you follow the style guidelines that we enforce. For now the most important ones are: using spaces instead of tabs, indenting by 4 characters, following the naming conventions (data type names start with a capital letter, names of fields and methods start with a lower case letter), and having spaces before curly braces.

You will submit this assignment by the deadline using the online submission system. You may submit as many times as you wish. Be aware of the fact that close to the deadline the system may slow down to handle many submissions - so try to finish early.

The submissions will be organized as follows:

Due Date:
  • The game: Wednesday May 27th, 10:00pm

5.1 Space Invaders

You are going to design the classic game Space Invaders.

Your game will use the javalib.funworld library, which provides an implementation of Worlds and bigBang similar to what was used in Fundies 1. Read the documentation carefully for more information.

For your version of the game you will have to represent the following elements:
  • the spaceship

  • four rows of invaders

  • spaceship bullets

  • invader bullets

The spaceship must be located at the bottom of the scene and should be allowed to move left and right only. The spaceship will also have the ability to fire bullets in order to hit invaders. Bullets are fired when the space bar is pressed by the user. Spaceship bullets move vertically (bottom to top) at a steady speed. The spaceship cannot go beyond the left or right border. If the spaceship reaches the edge of the scene and the user presses the arrow key in the direction of the edge, the spaceship will not move. If the user presses the arrow key in the opposite direction the spaceship can move in that direction.

Invaders are represented in rows, each row must have at least 9 invaders side by side. Invaders in the same row must have some space between them. Invaders do not move at all. At every clock tick a number of invaders get to fire bullets. The choice of which invader gets to fire on each tick is random and an invader cannot fire more than one bullet per tick. Invader bullets are the same as spaceship bullets in that they are the same size and move vertically. Invader bullets however move in the opposite direction as spaceship bullets, i.e., top to bottom.

When a spaceship bullet hits an invader, then the invader is destroyed. Destroyed invaders should no longer be visible on the canvas and should not be able to fire bullets. The spaceship bullet that hits an invader is also removed from the scene.

The game ends when either all invaders have been eliminated, or, when the spaceship has been hit by an invader bullet.

The version of the game you are going to design will be close to the original Space Invaders with some modifications/restrictions.

Here is an example image of the initial world:

Here is another screen shot while playing the game:

5.1.1 Local Variables

You will almost certainly want to use local variables for this assignment.

Be cautious, because some programmers use local variables as an excuse for not using helper methods. Remember, one task per method.

Let’s say we were computing the average of a list, and returning 0 if the list was empty:

public int average() {
if (this.length() == 0) {
return 0;
} else {
return this.sum() / this.length();
}
}

The problem, of course, is the length is being computed twice. One way to get around this would be to use a helper method:

public int average() {
return this.averageHelper(this.length());
}
 
public int averageHelper(int length) {
if (length == 0) {
return 0;
} else {
return this.sum() / length;
}
}

Why did the programmer choose to compute the sum in the helper and not in the outer method and pass it to the helper?

This is fairly verbose for such a simple operation, however. Instead, we can use a local variable:

public int average() {
int length = this.length();
if (length == 0) {
return 0;
} else {
return this.sum() / length;
}
}

This local variable could have been called something besides length.

As in ISL, local variables should be used to avoid duplicate computation, clarify what expressions mean, and contain simple computations another method wouldn’t need to use. Proper usage of local variables, also like in ISL, is a judgement call.

5.1.2 Getters, instanceof, Casting, Exceptions, etc.

As we have explained many times in lecture and lab, reliance on getters, instanceof, casting, fields of fields, and fields of parameters of another class are all poor design. We have none of these in our implementation, so we know that it is possible to design the game without these. Please do not ask us whether or not you are allowed to use these in some cases or how many points you will get off if you do: the answers are no and some, respectively.

Sometimes, to get around the use of instanceof, poorly disciplined programmers will check whether or not the length of a list is 0, which is effectively asking whether or not a list is an instance of an empty list. The only time you should do this is in your constructor when checking for data constraints.

Sometimes, to add methods to one case of union data but leave others unsupported (for example, getFirst on a list), poorly discinplined programmers will throw an exception in one case of the data. This practice will invariably lead to exceptions at runtime, and is always an indication of bad design.

5.1.3 To IList or ILo?

You should use the IList<T> interface we have recently covered in the course. This has the advantage of being able to write and re-use abstractions like foldr, map, filter, etc.

5.1.4 A Note About Randomness

There are two ways to generate random numbers in Java. The easiest is to use Math.random(), which generates a double between 0 (inclusive) and 1 (exclusive). You can multiply this number by some integer to make it bigger, then coerce to an int to produce a random integer in the range you wish. However, this is not easily testable: you’ll get different random values every time.

The better way to generate random numbers is: First, import java.util.Random at the top of your file. Next, create a new Random() object, and use its nextInt(int maxVal) method, which will give you a random integer between zero (inclusive) and maxVal (exclusive).

This is known as a "pseudorandom number generator", since the numbers aren’t really random if they can be reliably repeated...

The reason this is better is because there is a second constructor, new Random(int initialSeed), which has the property that every time you create a Random with the same initial seed, it will generate the same "random" numbers in the same order every time your program runs. You should therefore design your world classes with two constructors:

5.1.5 Submission format

You may find it overwhelming to keep all of your code within one file. To make your code more managable, you may submit a zip archive for this assignment instead of a single file. If you are more comfortable submitting as a single file, though, that is also fine.

5.1.6 Yikes, This Sure Is A Lot!

Yes, it is, but don’t worry, you can do it. A big part of being able to succeed with this assignment is to keep track of what data represents what, how to organize it, and what you will need to do with it. As always, sticking to the design recipe and fleshing out your wishlist in advance is the best way to go about handling daunting programs.