Assignment 5: The Aliens Attack Again
Goals: Design and implement a medium sized game;
Instructions
This assignment is long. Start early.
the names of classes,
the names and types of the fields within classes,
the names, types and order of the arguments to the constructor,
the names, types and order of arguments to methods, or
filenames,
...be sure that your submission uses exactly those names.
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.
There will be a separate submission for each problem - it makes it easier to grade each problem, and to provide you with feedback for each problem you work on.
The submissions will be organized as follows:
Homework 5 Problem 1: Your game implementation
Homework 5 Problem 2: The Course.java file
Problem 1: Friday Oct 25th, 10:00pm
Problem 2: Monday Oct 28th, 10:00pm
Practice Problems
Work out these problems on your own. Save them in an electronic portfolio, so you can show them to your instructor, review them before the exam, use them as a reference when working on the homework assignments.
Problem 20.2 on page 296
Problem 20.8 on page 306
Problem 20.9 on page 306
Problem 21.1 on page 312
Problem 21.3 on page 312
Problem 21.4 on page 315
Problem 21.5 on page 320
Problem 21.6 on page 320
Problem 21.7 on page 320
1 Problem 1 — Aliens Attack Again
The aliens are back! You are going to design Aliens Attack, based on the Space Invaders clone from 1114.
Your game will use the javalib.funworld library, which provides an implementation of Worlds and bigBang similar to what was used in 1114. Read the documentation carefully for more information.
1.1 Visual Clarification
Recall the demonstration from class as you work through your implementation.
1.2 Submission format
You may find it overwhelming to keep all of your code within one file. To make your code more manageable, you will submit a zip archive for this assignment instead of a single file.
1.3 World requirements
Your world must have a constructor that just takes an integer, which represents the number of bullets a player has to shoot. That is how your graders will launch your world.
Your game must end when there are no more aliens.
Your game must end when some alien reaches the back row.
The player should be able to press the space bar to fire a bullet from the spaceship.
The player should be able to press the left and right arrow keys to move the spaceship.
As well as the bullets, aliens, and the rocket, the player must also be able to see the score of how many aliens have been destroyed so far.
1.4 Ships
The aliens should spawn as a single army, arrayed in some number of rows and columns.
Aliens should move across the screen in a right-down-left-down-right pattern.
All ships should have the same speed (the magnitude of their velocity).
When an alien is hit by a bullet, it disappears.
Ships should not fly over (or under) the bullets.
1.5 Bullets
Bullets that have flown past the edge of the screen should be removed from the game.
1.6 Tick-tock On The Clock
This style of programming is called a fluent interface.
As you may have noticed, a lot has to happen in one tick (bullets and ships have to move, collisions need to be handled, ships may be spawned, etc.). To make this manageable, we recommend you design methods that use the current (this) world and produce a new world, where one of those steps have been taken, and a new world is returned. You can then chain those together to in your onTick method. Using this methodology, it should look something like this (with much better naming, of course):
// advance the world by one tick: do step one, then step two, then step three, // and then step four public MyMarioBrothersWorld onTick() { return this.doStepOne() .doStepTwo() .doStepThree() .doStepFour(); }
An added benefit of designing this collection of methods is, of course, the ability to test each step of your code independently.
As a note of caution, beware that you’re calling dependent operations in the right order. For example, the order in which you choose to remove off-screen components and handle ship/bullet collisions could affect the results of the game.
1.7 To IList or ILo?
It is up to you whether or not you want to use the IList<T> interface we have recently covered or the ILo* pattern from earlier in the course. The former has the advantage of being able to write and re-use abstractions like foldr, map, filter, etc. easily, while the latter has the advantage of not having to write many function objects.
1.8 Local variables
You will almost certainly want to use local variables for this assignment.
Be cautious, because some programms 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.
1.9 Suggested constants
While you are welcome to tweak the game to create gameplay you enjoy (so long as you adhere to all of the above specifications), here are the constants we used that we know will generate decent gameplay (as seen in the previously linked video):
Cell width: 32 pixels
Cell height: 32 pixels
Board cols: 20
Board rows: 30
Tick rate: 0.1 seconds per frame.
Number of aliens to spawn: 3 rows of 10 aliens
1.10 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.
Flesh out your wishlist and store it somewhere you can easily access. Then determine to which classes each task should belong. Then, you can add the method stubs to each of the classes, and write examples and tests before you begin to program. Give each method its own test function, and comment them out. As you implement each method, you can uncomment that method’s test function, and that way you can test the whole program as you go.
2 Problem 2 — Courses and Prereqs
A Course has a name and prereqs, which is an IList<Course> (see Lecture 15 for the data definition of parametrized lists).
Notation: When you see notation of the form ClassName#methodName(...), it means methodName is a method defined in the class ClassName, and the arguments are left for you to determine. For example, String#substring(...).
Design the Course class and any other necessary classes and interfaces.
Create the IFunc<A, R> interface, which has one method: R apply(A arg); as shown in class.
Implement the visitor pattern for IList<T>s. Your IListVisitor<T, R> interface should be a function, meaning it should extend the IFunc interface —
think carefully what types should be given to IFunc<?, ?> to make this work correctly... Design a DeepestPathLength class, which is a function object that takes a Course. This should be used in a Course#getDeepestPathLength(...) method, which computes the length of the longest path from this course to a course with no prerequisites. You must use the visitor pattern where appropriate. Calling the Course#getDeepestPathLength(...) method on a Course should produce the same value as applying a DeepestPathLength object to it.
Design the IPred<X> interface, which is an IFunc that always returns a Boolean.
Design a HasPrereq class, which is an IPred<Course> This should be used in a Course#hasPrereq(...) method, which determines if a course has a prereq (either an immediate one or a prereq of a prereq) whose name is the same as a given string. Think carefully about the constructor for this class. You must use the visitor pattern when appropriate. Calling the Course#hasPrereq(...) method on a Course should produce the same value as applying a HasPrereq object to it (given that they are both looking for the same name).
2.1 Extra Credit: Ormap
For extra credit, design the Ormap<T> class, which is a list visitor that operates as ormap did in ISL. Think carefully about the constructor for this class.
Re-implement the HasPrereq class, using Ormap whenever appropriate
If you plan on doing the extra credit, your submission to the handin server should either totally complete the above two items or not. Your graders will only grade one version of HasPrereq.