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 for these to work):
A few more notes:
Some more important hints:

  1. 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 the ignore_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 the ignore_rest line.

  2. 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!

  3. Use the right imports!
    As this example shows, when you use PIL/Pillow images, you have to add this extra import 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 the Image 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'

  4. Use and app.mode
    You can access the app from any mode using So for example you can get the width of the canvas using 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 using app.mode, as in app.helpMode.

  5. 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):
   = app
    This way, you can access the player from the app, and you can access the app from the player. Woohoo!

  6. 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:'none')

  7. Good luck!!!!

  1. 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( == "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( == "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( == "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()

  2. 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 function runCreativeSidescroller() that takes no arguments and that demonstrates each of the following (with the 60 points distributed in reasonable fashion across each of these requirements):

    1. Animation Stuff
      1. 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.
      2. modes (at least a splash screen, main screen, and actually helpful help screen)
      3. sidescrolling (of course) -- in that the game actually requires sidescrolling in order to win.
      4. a custom cursor (image) following the mouse using mouseMoved. You can easily find images for this all over the web, or design your own!
      5. the ability to drag-and-drop some objects in the game using the mouse

    2. OOPy Stuff
      1. 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.
      2. 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.

    3. General Gameplay
      1. The game should be simple, but fun and engaging.
      2. 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.
      3. 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.
      4. 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.

    4. Creativity
      1. It should be creative. We are looking for creativity and cleverness here.

    5. Size
      1. 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).
      2. 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!

  3. 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.