CSCI 136 |
In this assignment, you will be extending your previous Ultima assignment.
You will implement Python threads.
You will learn how to create code that properly protects data/operations shared between different threads.
UPDATE: If you weren't able to get the last lab to work correctly, here is code that you can use as a basis for this week's assignment: Lab 6 Solution |
Getting started.
You can use your previous Ultima assignment as starting point for this assignment.
Once everyone has turned in part 1 next week, we will provide you with working files for that in case you got stuck on the first part.
Start by downloading ultima0.1.zip.
This zip file includes all of the images you will need, the python library code necessary, and new configuration files for running the game.
The Ultima.py file has been changed for the new and improved game, and you should use this as the main game loop - in other words, you don't need to make any changes to Ultima.py.
In your improved game, there are monsters you must go around and do battle with.
We have provided a stub version of Monster.py that you can fill in with your own code.
You attack monsters by running into them.
Monsters attack you in the same way.
You can also walk on lava if you want, but it costs you one hit point.
Tile.
As before, a Tile object represents an individual position in the Ultima world.
You will be using the same tiles as in the previous version.
You already have code to handle most of this. You will only need to add one method to the Tile class.
Below is the API for the Tile class. Most of these methods were already implemented in the previous lab, but you will need to add the getDamage() method to the Tile class for this lab.
Avatar. The Avatar class has gotten somewhat more advanced. The Avatar now has a number of hit points (life, or health). Once your hit points reach 0, the game is over and you lose. The Avatar also has a damage amount, this is how many hit points of damage our hero causes when attacking a monster. You will need to update your constructor to handle these new attributes.class Tile ----------------------------------------------------------------------------------------- __init__(self, string code) # Create a new tile based on a String code boolean getLit(self) # Return whether this Tile is lit or not setLit(self, boolean value) # Change the lit status of this Tile draw(self, int x, int y) # Draw at index position (x, y) boolean isOpaque(self) # Does this type of Tile block light? boolean isPassable(self) # Can the Avatar walk on this Tile? int getDamage(self) # Returns 1 if the tile is Lava, 0 otherwise
The Avatar's line in the game text file now has 5 numbers, for example:class Avatar ---------------------------------------------------------------------------------------- Avatar(self, int x, int y, int hp, int damage, double torch) # Modify constructor to accomodate additional # input parameters and functionality int getHitPoints(self) # Get the number of hit points left incurDamage(self, int damage) # Reduce the Avatar's hit points by the amount damage int getDamage(self) # Get the amount of damage the Avatar causes monsters
The above means the Avatar starts at position (11, 2), starts with 20 hit points, causes 3 hit points of damage, and has an initial torch radius of 100.11 2 20 3 100.0
Name | Filename | Image | Code |
Skeleton | skeleton.gif | SK | |
Orc | orc.gif | OR | |
Slime | slime.gif | SL | |
Bat | bat.gif | BA |
Monsters are defined at the end of the game text file. You can assume the values in the file are valid starting locations, monsters won't start on top of each other, on top of a mountain, etc. Here is an example:class Monster ----------------------------------------------------------------------------------------- __init__(self, World world, String code, int x, int y, int hp, int damage, int sleepMs) # Constructor initializes monster characteristics, # including a copy of the World object it has been # instanitaed in, a code for the monster type, several # attributes that are the same as avatar (initial # position, hit points, and damage), and a number # representing the number of ms it should sleep # between moves incurDamage(self, int damage) # Reduce this monster's hit points by the amount damage draw(self) # Draw the monster int getHitPoints(self) # Get the number of remaining hit points of this monster int getDamage(self) # Get how much damage this monster causes int getX(self) # Get current x-position of this monster int getY(self) # Get current y-position of this monster setLocation(self, int x, int y) # Move this monster to a new location run(self) # Worker thread that periodically moves this monster
The above defines:SK 3 3 10 3 1000 OR 6 19 8 2 1000 BA 20 10 4 1 500 SL 25 16 6 2 1500
The monsterMove() should be called by a monster's run() method. If the proposed location is not valid or not passable, then nothing happens. If there is currently another monster at the proposed location, then nothing happens (monsters don't attack each other). If the Avatar is at the proposed location, then the monster gets to attack the Avatar and do the appropriate damage. In this case, the monster stays at its current location (Avatar and monsters never overlap). Otherwise, the monster makes its move to the new location, incurring any damage associated with the new location (i.e. if the new location is lava). Note: since only the World object knows the outcome of the monster's call to monsterMove(), the World object must update the calling Monster object by calling setLocation() and/or incurDamage().class World ----------------------------------------------------------------------------------------- boolean avatarAlive(self) # Is the Avatar still alive? monsterMove(self, int x, int y, Monster monster) # Attempt to move given monster to (x, y) avatarMove(self, int x, int y) # Attempt to move Avatar to (x, y) int getNumMonsters(self) # Return number of alive monsters
Grade Item | Points Possible | Points Earned |
---|---|---|
Program Compiles and Runs | 6 | |
Comments on All Classes and Methods | 3 | |
Tile Changes | 3 | |
Avatar Changes | 3 | |
Monster Class | 6 | |
World Changes | 4 | |
Used Threads for Each Monster | 4 | |
No Concurrency Issues on World Instance | 4 | |
Total | 30 |
Extra credit.
Make the game better.
One idea is to refactor the code so that it takes advantage of abstraction in the hierarchy of classes. Another idea is to create a source of health restoration for both the hero and the monsters. You might want to make additional maps, use additional tile types, or add sound to the game. Or come up with your own creative ideas. If you do submit extra credit work, be sure to submit the lab first, as defined by the assignment, and then submit your improved version to the Extra Credit dropbox. Submission. Submit your programs Avatar.py, Tile.py, World.py and Monster.py, using Moodle. Be sure each submitted source file has the required header with your name and a description of the program. For extra-credit, please submit a single zip file containing all your programs/graphics/sounds via the special extra-credit drop box. Page last updated: March 02, 2022 |