HW 9 (Lunar Lander Part 1)

Assignment overview

You'll implement a version of the classic video game Lunar Lander. In this assignment, you'll implement the game logic and use a text based interface; in HW 10 (Lunar Lander Part 2), you'll add graphics.

Goals

Practice using object-oriented programming.

Logistics

This is a partner assignment, which means you should complete all pieces of this assignment with your assigned partner. In particular, any coding must be completed side-by-side in a pair programming style. You are welcome to discuss the assignment with other classmates, course staff or Anna. Make sure to cite any help you receive in the "acknowlegdements" portion of the assignment.

There are new partnerships for this assignment. Check Moodle to see who you're working with.

This assignment is due at 10PM on Wednesday, May 20.

Setup

Mount the COURSES drive and create a folder called hw9 in your STUWORK folder. Open the new folder in VSCode. If you need a refresher on how to complete these steps, refer back to the in-class lab from the first day of class.

Download the starter code and move the files (interfaces.py and graphics.py) into your hw9 folder.

About the code

  • interfaces.py contains two classes. For this assignment, you'll use (and minimally edit) the TextLanderInterface class. Take some time to read through this class and its methods.
  • graphics.py won't be used until HW10 (Lunar Lander Part 2).

You should create a third file, lunarLander.py in your hw9 directory. Almost all of your code will be written to this file.

Lunar Lander

In this game, you are controlling a moon lander that is descending towards the moon. Your goal is to land safely on the moon without crashing (i.e., you can't land on the moon at too high of a speed). Gravity will steadily accelerate your lander towards the moon, and you have one control: a "thrust" button that will apply thrust to slow down your spacecraft. You have a limited amount of fuel though, and once the fuel runs out gravity will take its course for the rest of the journey.

Basic functionality

LunarLander Class

First, you'll write a class caled LunarLander (in Python, the names of classes traditionally start with a capital letter). This class will represent the spacecraft.

You'll need to create several instance variables and methods for this class.

Instance variables you need include altitude (how far the spacecraft is above the surface of the moon), velocity (how fast the ship is moving. A negative velocity means that the spacecraft is moving towards the moon, while a positive velocity means its moving away from the moon's surface and further into space), and fuel (how much fuel is left in the tank).

Methods you need include:

  • A constructor, that takes in the parameters necessary to create a LunarLander object.
  • getAltitude, a method that takes no parameters other than self and returns the numeric (integer) altitude of the lander. Positive numbers indicate higher in the sky, while 0 indicates you are on the moon's surface.
  • getVelocity, a method that takes no parameters other than self and returns the current velocity of the moon lander.
  • getFuel, a method that takes no parameters other than self and returns the amount of fuel currently in the tank.
  • update, a method that takes the parameters self and thurstAmount, an integer indicating how much thrust to apply to the ship. update will update the lander's altitude and velocity in accordance with how much thrust was applied. Specifically, you should
    • Use thrustAmount units of fuel if possible (if not enough fuel is available, just use what is left)
    • For each unit of fuel that is burnt, increase the ship's velocity by 2.
    • Subtract 1 from the velocity to account for gravity (remember, positive velocity means the spacecraft is traveling away from the moon and negative velocity means it's going towards the moon).
    • Compute and set the new altitude, calculated as the old altitude plus the new velocity. If this value is less than or equal to 0, the spacecraft has reached the moon (either a successful landing or a crash) in which case you should set the altitude to 0.

LanderGame class

To actually play our game, we need a class that will handle game play: it'll create the lunar lander object and then repeatedly ask the user for how much thrust to use, while updating the lander's instance variables to keep track of altitude, fuel, velocity, and whether it has landed.

Create a class LanderGame in lunarLander.py. For now, this class will have a constructor and one other method, play. The constructor should create two instance variables, lander and interface. lander is an instance of the LunarLander class and should be initialized to have velocity 0, altitude 100, and fuel 30. interface will (for this assignment) be a TextLanderInterface object. You'll need to import the interfaces module at the top of your file – be cognizant of how you import the module and how you'll have to initialize objects from this module.

The play method will handling playing the game. It should keep asking the user for information (how much thrust to use) until the lunar lander lands safely or crashes. play should do the following tasks:

  • Normal game play: do the following in a loop as long as the lander instance variable has altitude above 0.

    • Ask the interface object to show the current status of the lander (altitude, velocity, fuel). Do not write your own code to do this. Use the methods in TextLanderInterface.
    • Ask the interface to get a thrust amount from the user. Again, don't write your own code to do this: use the methods in TextLanderInterface.
    • Update the lunar lander's information with the returned amount.
  • Handling ground contact: when the lander's altitude reaches 0, check its velocity. If its velocity is less than -5, it has crashed. Otherwise, it has landed safely.
  • Display results to the user: Based on whether it was a successful landing or a crash, display the correct error message to the user. Don't write your own code to do this; use the methods in TextLanderInterface.
  • Close the interface: call the interface's close() method to exit.

Main function

Add a main function to lunarLander.py that will create a LanderGame instance and call its play method to play the game. Here's a sample run of what playing the game should look like, if the player always chooses 0 thrust:

Lander Status: Altitude 100, Velocity 0, Fuel 15
Thrust amount? 0
Lander Status: Altitude 99, Velocity -1, Fuel 15
Thrust amount? 0
Lander Status: Altitude 97, Velocity -2, Fuel 15
Thrust amount? 0
Lander Status: Altitude 94, Velocity -3, Fuel 15
Thrust amount? 0
Lander Status: Altitude 90, Velocity -4, Fuel 15
Thrust amount? 0
Lander Status: Altitude 85, Velocity -5, Fuel 15
Thrust amount? 0
Lander Status: Altitude 79, Velocity -6, Fuel 15
Thrust amount? 0
Lander Status: Altitude 72, Velocity -7, Fuel 15
Thrust amount? 0
Lander Status: Altitude 64, Velocity -8, Fuel 15
Thrust amount? 0
Lander Status: Altitude 55, Velocity -9, Fuel 15
Thrust amount? 0
Lander Status: Altitude 45, Velocity -10, Fuel 15
Thrust amount? 0
Lander Status: Altitude 34, Velocity -11, Fuel 15
Thrust amount? 0
Lander Status: Altitude 22, Velocity -12, Fuel 15
Thrust amount? 0
Lander Status: Altitude 9, Velocity -13, Fuel 15
Thrust amount? 0
Crash! Oh no!
Goodbye

Make sure to also add the if name == "__main__" block so that you can play your game by running python3 lunarLander.py from the terminal.

Improvements to the game

Congratulations, you've implemented a working game! Now let's see if we can add some improvements to the player experience. Note that these enhancements are not worth many points relative to the overall functionality, so you should make sure the basic game works before trying these pieces.

  • Change the constructor of the LanderGame class so that it can take an integer parameter indicating the difficulty. Based on the given difficulty, create the lander with different initial values as follows:
    • 1 (easy): Altitude 100, Velocity 0, Fuel 15
    • 2 (medium): Altitude 200, Velocity 0, Fuel 15
    • 3 (hard): Altitude 300, Velocity 0, Fuel 10
  • Change the main function in lunarLander.py to explain to the user what the different difficulty levels mean and allow them to choose their preferred level.
  • Edit the getThrust method in the TextLanderInterface class in interfaces.py to allow the user to press Enter (i.e., to not enter any number) when they want to use 0 thrust.

Wrap up

When you're finished, make sure to complete the usual documentation steps. This includes adding comments, writing function docstrings, and adding a top-level comment, acknowledgements, and a reflection to the header.

You should also think about coding style. Have you written everything in a consistent way that is easy to read? Does your code have any unnecessary print statements? (Remove them.) Is there any repetitive code that could be rewritten to use loops or helper functions? Review the style document on Moodle for the expectations for this assignment.

Assignment submission and misc. notes

You can test your code by running python3 lunarLander.py.

Handing in the assignment

You need to hand in lunarLander.py and interface.py on Gradescope. Only one partner should submit the assignment to Gradescope (but make sure to add your partner to your group after submitting).

Grading

This assignment is worth 30 points, broken up as follows:

  • 10 points LunarLander class
  • 10 points LanderGame class
  • 5 points Improvements
  • 5 points style (reflections, acknowledgments, comments, coding style)

Start early, ask lots of questions, and have fun!

Anna's acknowledgements

This assignment was adapted from Layla Oesper's teaching materials. Thanks for sharing!