Lunar Lander

Download Report

Transcript Lunar Lander

Lunar Lander
An Example of Interacting Classes
26-Jul-16
LunarLanderGame


This class contains the public static void main(String[] args)
method. In this method, you should (1) create a LunarLander
object, (2) create an IOFrame object, and (3) send messages to
these two objects in order to get the burn rate from the user, tell
the lander what to do, find out the lander status, and report the
status back to the user. When the lander's altitude is less than or
equal to zero, the game ends (one way or the other).
When the game finishes, you should ask the user whether he/she
would like to play again. Allow as many games as the user wants
before quitting the program.
2
Additional constraints

At the time this program was assigned, we had:







Classes and methods
Declarations, with and without initialization
Assignment statements
while loops
if and if-else statements
System.out.print and System.out.println
The first version of this program reflects these
constraints
3
public static void main(String[] args)

Here’s the outline of the main method:
public static void main(String[] args) {
IOFrame frame = new IOFrame("Lunar Lander");
LunarLander lander;
double safeLandingSpeed = 10.0;
String again = "yes";
// Main loop—One game is once through the loop
// Inner loop: Play until the Lander crashes or lands
}
frame.exit();
4
Main loop


Recall that we had a String again = "yes"; declaration
Here’s the main loop:
while ("yes".equals(again)) {
// setup
lander = new LunarLander(100, 0, 15);
frame.clear();
frame.displayLine("Altitude: " + lander.getAltitude());
frame.displayLine("Velocity: " + lander.getVelocity());
frame.displayLine("Fuel left: " + lander.getFuelRemaining());
// inner loop to play the game
// tell user how game ended
}
again = frame.getString("Would you like to play again?");
5
Inner loop to play game




// inner loop to play one game
while (lander.getAltitude() > 0) {
double amount = frame.getDouble("How much fuel should I burn?");
lander.burn(amount);
frame.displayLine("");
frame.displayLine("Altitude: " + lander.getAltitude());
frame.displayLine("Velocity: " + lander.getVelocity());
frame.displayLine("Fuel left: " + lander.getFuelRemaining());
}
// tell user how game ended
double finalVelocity = lander.getVelocity();
if (finalVelocity > safeLandingSpeed) {
frame.display("CRASH!");
}
else {
frame.display("Congratulations! A safe landing!");
}
6
LunarLander instance variables


public class LunarLander {
double altitude;
double velocity;
double fuel;
final double acceleration = 1.64;
final double fuelFactor = 1.0;
final double fuelLimit = 2.0;
Where:



acceleration is the Moon’s gravity (meters/second/second)
fuelFactor is how much to reduce the velocity per unit of fuel
burned
fuelLimit is the maximum amount of fuel that can be burned
at once
7
The LunarLander constructor

LunarLander(double altitude, double velocity,
double fuel) {
this.altitude = altitude;
this.velocity = velocity;
this.fuel = fuel;
}
8
The burn(double) method


This is the method that does all the work of the
LunarLander—it recomputes velocity, altitude, and fuel
remaining
void burn(double amount) {
if (amount > fuel) amount = fuel;
if (amount > fuelLimit) amount = fuelLimit;
velocity = velocity + acceleration - amount * fuelFactor;
fuel = fuel - amount;
altitude = altitude - velocity;
}
9
The “getter” methods





double getFuelRemaining() {
return fuel;
}
double getAltitude() {
return altitude;
}
double getVelocity() {
return velocity;
}
That’s all...
...but the program could be improved
10
Part II
Improving the Lunar Lander program
Refactoring


Refactoring is the process of rearranging and modifying
code to improve it (without changing what it does)
The following lines occur twice in the code:



frame.displayLine("Altitude: " + lander.getAltitude());
frame.displayLine("Velocity: " + lander.getVelocity());
frame.displayLine("Fuel left: " + lander.getFuelRemaining())
Duplicated code is bad because, if you have to change
it, you have to change it everyplace it is used
One way to avoid code repetition is to put the offending
code into a method



This particular refactoring is called “Extract Method”
It isn’t necessarily a single-step method
I’ll do it informally
12
Extract Method I—displayStatus()

I create the following method:


I call our new method this way (in two places):


void displayStatus() {
frame.displayLine("Altitude: " + lander.getAltitude());
frame.displayLine("Velocity: " + lander.getVelocity());
frame.displayLine("Fuel left: " + lander.getFuelRemaining());
}
displayStatus();
This gives me syntax errors, because both frame and lander are
local variables in main
 I have three choices:



Pass in frame and lander as parameters
Make frame and lander instance variables of LunarLanderGame
Make frame and lander class variables of LunarLanderGame
13
Extract Method II—frame




I only ever want one IOFrame, so it makes sense to make frame
a static variable
I move this line
IOFrame frame = new IOFrame("Lunar Lander");
out of the main method, put the word static in front of it, and
make it a field of the LunarLanderGame class
public class LunarLanderGame {
static IOFrame frame = new IOFrame("Lunar Lander");
This solves the syntax error for frame
14
Extract Method III--lander

It seemed to me that each LunarLanderGame should have its
own LunarLander
Besides, I have no easy way to “reset” LunarLander for a new game
 I therefore decided to make LunarLander an instance variable
Hence, this code:
 public class LunarLanderGame {
public static void main(String[] args) {
LunarLander lander;
...
Becomes this code:
 public class LunarLanderGame {
LunarLander lander;
public static void main(String[] args) {
...




This does not fix the syntax error—I can’t access LunarLander
(an instance variable) from within main (a static method)
15
Extract Method III--run

We have a syntax error because:



Every LunarLanderGame has its own LunarLander
We have no LunarLanderGame objects
Solution: Make a LunarLanderGame object in the
main method, and let it do the work


public static void main(String[] args) {
LunarLanderGame game = new LunarLanderGame();
game.run();
}
void run() { ...use lander here... }
16
Minor improvements I

It seems unnatural to set the variable again just so we
can go through the loop the first time:


String again = "yes";
while ("yes".equalsIgnoreCase(again)) {
....
again = frame.getString("Would you like to play again?");
}
With the do...while loop, we can revise this to:


String again;
do {
...
again = frame.getString("Would you like to play again?");
} while ("yes".equals(again));
Note: It doesn’t work to define again within the loop
17
Minor improvements II

As written, any response other than “yes” will be taken to mean
“no”


This is a real nuisance, especially if you make a typing error
do {
...main body of loop...
do {
again = frame.getString("Would you like to play again?");
} while (!"yes".equals(again) && !"no".equals(again));
} while ("yes".equals(again));

Instead of equals, we can use equalsIgnoreCase

This allows responses such as YES as well as yes
18
How is this improved?

We’ve moved our status display code into a method, so
any corrections or improvements in it need only be done
in one place



We’ve used the “right” kind of loop to avoid a rather
silly-looking initialization
We’ve made the yes/no question much more robust


This also made the main loop smaller, so it’s easier to read and
understand
Come to think of it, this would be a good candidate for a
method—getYesOrNo("Would you like to play again?")
The code ends up being eight lines longer (you can’t
win them all!)
19
Summary: Refactoring

Refactoring is the process of rearranging and modifying
code to improve it (without changing what it does)

You improve code by making it:






Easier to read and understand
Easier to debug
Easier to modify
“Real” programs are constantly being read and modified
Hence, refactoring is important and useful
Conventional wisdom says: “If it ain’t broke, don’t fix
it!”



This is because it’s easy to introduce errors
Refactoring is best done if you have a suite of tests you can
run to make sure you didn’t break anything
JUnit is one of the best ways to develop a suite of tests
20
The End
21