CMU 15-112: Fundamentals of Programming and Computer Science
Homework 9 (Due Sunday 27-Oct at 8pm)
Note: +5 early-bird bonus if submitted by Saturday 26-Oct at 8pm
Helpful forms (note: you must be authenticated to andrew.cmu.edu for these to work):
- 15-112-f19 Extension Request Form
- Note: this week's booster sessions do not have any requirements for attending, though we strongly encourage you to invest 4+ hours into the hw before attending, in order to get anything useful out of them.
- Also: even with this change, you may not take any notes or in any way record any solutions provided during the booster sessions.
Notes:
- This week there is no starter code!
- This week there is no linter! We had previously announced that you should use a week9 linter. We have changed that, so just omit the linter entirely. You should still abide by our style rules, of course!
- Be sure to follow these steps to install the required modules used by the animation framework!
- As always, be sure to properly cite any code you use from the course
website (or anywhere else). You are free to use/copy code from the course
website, but it must include a comment indicating you did so, including
the URL of the code's source. For example, this is not ok:
# Got this from the course website def make2dList(rows, cols): return [ ([0] * cols) for row in range(rows) ]
But this is ok:# Copied from: http://www.cs.cmu.edu/~112/notes/notes-2d-lists.html def make2dList(rows, cols): return [ ([0] * cols) for row in range(rows) ]
To be clear: improperly cited or uncited code is a violation of course policies.
A few more notes:
- Do not use recursion this week.
- Do not hardcode the test cases in your solutions.
- Even though we have split writing-sessions into their own separate category, homeworks will continue to be out of 75 points instead of 100 points so that each hw is equally weighted.
Some more important hints:
- Use ignore_rest
Place just your Bird class and subclasses at the top of your file. Then include this line:# ignore_rest (The autograder ignores all code below here)
Then place your test functions and your animations, including their imports, below theignore_rest
line. That way, the autograder will not get confused by any of that code. It only needs your Bird class and subclasses, so only that code should be above theignore_rest
line. - Use the right version of cmu_112_graphics!
As of now, that is version 0.8.5! The version prints out in the console when you run your code. Be sure it is the most-recent version! - Use the right imports!
As this example shows, when you use PIL/Pillow images, you have to add this extraimport
after you import from tk, like so:from cmu_112_graphics import * from tkinter import * from PIL import Image # <-- need to do this after tkinter import
If you don't do this, then theImage
class is actually from tk and not from PIL/Pillow, which leads to this kind of error:Exception: type object 'Image' has no attribute 'FLIP_LEFT_RIGHT'
- Use mode.app and app.mode
You can access the app from any mode usingmode.app
. So for example you can get the width of the canvas usingmode.app.width
. Also, so long as you save each mode in the app inside appStarted, as we did in our examples, then you can access any mode from the app usingapp.mode
, as inapp.helpMode
. - Have your Player take the app as an argument
When you construct your player (assuming you even do things that way), it's a good idea to take the app as an argument. So in your app, you might do this:app.player = Player(app) # <-- see how we provide app as an argument?
Then, inside your Player class, your constructor might look like this:class Player(object): def __init__(self, app): self.app = app ...
This way, you can access the player from the app, and you can access the app from the player. Woohoo! - Make your cursor disappear (optionally)
You do not have to make the real cursor disappear, so you can ignore this, but... If you want to make the real cursor disappear, try doing something like this:mode.app._root.configure(cursor='none')
Good luck!!!!
- Bird Class and Subclasses [15 pts, autograded]
Write the Bird, Penguin, and MessengerBird classes so that they pass testBirdClasses and use the OOP constructs we learned this week as appropriate.
Note/Hint: getLocalMethods does not include static methods, and startMigrating should be a static method.
def getLocalMethods(clss): import types # This is a helper function for the test function below. # It returns a sorted list of the names of the methods # defined in a class. It's okay if you don't fully understand it! result = [ ] for var in clss.__dict__: val = clss.__dict__[var] if (isinstance(val, types.FunctionType)): result.append(var) return sorted(result) def testBirdClasses(): print("Testing Bird classes...", end="") # A basic Bird has a species name, can fly, and can lay eggs bird1 = Bird("Parrot") assert(type(bird1) == Bird) assert(isinstance(bird1, Bird)) assert(bird1.fly() == "I can fly!") assert(bird1.countEggs() == 0) assert(str(bird1) == "Parrot has 0 eggs") bird1.layEgg() assert(bird1.countEggs() == 1) assert(str(bird1) == "Parrot has 1 egg") bird1.layEgg() assert(bird1.countEggs() == 2) assert(str(bird1) == "Parrot has 2 eggs") tempBird = Bird("Parrot") assert(bird1 == tempBird) tempBird = Bird("Wren") assert(bird1 != tempBird) nest = set() assert(bird1 not in nest) assert(tempBird not in nest) nest.add(bird1) assert(bird1 in nest) assert(tempBird not in nest) nest.remove(bird1) assert(bird1 not in nest) assert(getLocalMethods(Bird) == ['__eq__','__hash__','__init__', '__repr__', 'countEggs', 'fly', 'layEgg']) # A Penguin is a Bird that cannot fly, but can swim bird2 = Penguin("Emperor Penguin") assert(type(bird2) == Penguin) assert(isinstance(bird2, Penguin)) assert(isinstance(bird2, Bird)) assert(not isinstance(bird1, Penguin)) assert(bird2.fly() == "No flying for me.") assert(bird2.swim() == "I can swim!") bird2.layEgg() assert(bird2.countEggs() == 1) assert(str(bird2) == "Emperor Penguin has 1 egg") assert(getLocalMethods(Penguin) == ['fly', 'swim']) # A MessengerBird is a Bird that carries a message bird3 = MessengerBird("War Pigeon", "Top-Secret Message!") assert(type(bird3) == MessengerBird) assert(isinstance(bird3, MessengerBird)) assert(isinstance(bird3, Bird)) assert(not isinstance(bird3, Penguin)) assert(not isinstance(bird2, MessengerBird)) assert(not isinstance(bird1, MessengerBird)) assert(bird3.deliverMessage() == "Top-Secret Message!") assert(str(bird3) == "War Pigeon has 0 eggs") assert(bird3.fly() == "I can fly!") bird4 = MessengerBird("Homing Pigeon", "") assert(bird4.deliverMessage() == "") bird4.layEgg() assert(bird4.countEggs() == 1) assert(getLocalMethods(MessengerBird) == ['__init__', 'deliverMessage']) # Note: all birds are migrating or not (together, as one) assert(bird1.isMigrating == bird2.isMigrating == bird3.isMigrating == False) assert(Bird.isMigrating == False) bird1.startMigrating() assert(bird1.isMigrating == bird2.isMigrating == bird3.isMigrating == True) assert(Bird.isMigrating == True) Bird.stopMigrating() assert(bird1.isMigrating == bird2.isMigrating == bird3.isMigrating == False) assert(Bird.isMigrating == False) print("Done!") testBirdClasses() - runCreativeSidescroller [60 pts, manually graded]
Note: the point of this exercise is for you to practice some of the key ideas of this week while also continuing to expand your creativity and flair for original design. This will of course be of significant value as we enter term project season soon!
With that in mind, write the functionrunCreativeSidescroller()
that takes no arguments and that demonstrates each of the following (with the 60 points distributed in reasonable fashion across each of these requirements):- Animation Stuff
- spritesheets (so the player is running, for example). Also, do this
without using the spritesheet from the notes (you can easily find others
all over the web).
- Note that you cannot submit an image file with hw9. So you have to load your spritesheet using a URL. If you want to use a local image, then use one of the many free image sharing services (such as Imgur, Flickr, among many others) to place the image online so you and the graders can all load that image using a URL.
- modes (at least a splash screen, main screen, and actually helpful help screen)
- sidescrolling (of course) -- in that the game actually requires sidescrolling in order to win.
- a custom cursor (image) following the mouse using mouseMoved. You can easily find images for this all over the web, or design your own!
- the ability to drag-and-drop some objects in the game using the mouse
- spritesheets (so the player is running, for example). Also, do this
without using the spritesheet from the notes (you can easily find others
all over the web).
- OOPy Stuff
- At least 3 well-chosen and well-designed top-level classes of objects (something like Player, Object, Enemy) and at least 2 well-chosen and well-designed subclasses (something like FastEnemy and SillyEnemy). So that's 5 total classes at minimum. Also, the inheritance needs to be done appropriately -- inheriting rather than copying code, etc.
- At least one use of a set or dictionary whose keys are instances of one of your classes (thus showing that you understand how to use __eq__ and __hash__). For example, you might have a set of monsters that are currently frozen and cannot move.
- General Gameplay
- The game should be simple, but fun and engaging.
- It should be playable, in that someone can walk up and use it without knowing anything special about it. They may have to read a help screen, though.
- If it makes sense at all, it should also be winnable and losable. If it is point-based, for example, then you can win if you get a certain number of points, and you can lose if you get below some number of points.
- superhelp: if the user (or grader) presses 'S' (shift-s), then they should get superhelp. This should print to the console (unlike literally everything else, that must be in the canvas). It should print everything the player/grader needs to play the game, and also to grade it and be sure it complies fully with this spec.
- Creativity
- It should be creative. We are looking for creativity and cleverness here.
- Size
- It's hard to say exactly how many lines of code you need for this (just like for your term project). We do not grade for volume but for quality. That said, we would expect submission to be in the 150-250 lines range (not counting the superhelp text, which may pad it by quite a few more lines).
- That said, remember that this is just part of one hw. Don't overdo it. You have plenty of time to do amazing things in your term projects!
Be creative, and have fun! - Animation Stuff
- Bonus/Optional: Sorting animation [3 pts] [manually graded in hw9]
Note: this was included in the hw8 writeup, but is part of hw9. It is reproduced here for your convenience.
Write an animation function for your favorite sorting algorithm! You'll want to use the animation starter code, but you can be creative regarding how you visualize the sorting process. The only requirements are the following features:
- Clearly indicate the name of the sorting algorithm being demonstrated
- Demonstrate using a randomly-generated shuffled list of the numbers from 0 to 20
- Clearly demonstrate each comparison, move, swap, etc.
- Use the right arrow key to move the sort a step forward, and the left key to move a step back
- Type the "r" key to reset the animation to a new shuffled list
Feel free to look at the sorting links in the efficiency course notes for inspiration! There's lots of cool visualizations and sorting algorithms out there.