15-112 Spring 2014 Homework 9 (OOP)
Due Sunday, 30-Mar, at 10pm

Read these instructions first!


These problems are SOLO.  You must work alone on these problems (except, of course, for help from the course staff).

  1. Polynomial and Quadratic classes [100 pts]
    This is SOLO.  Starting from the Polynomial class in the class notes, edit that class and also implement the Quadratic class so that all the tests below succeed.  Be sure not to hardcode against these tests, as the autograder will use similar tests, but with different constants.  You may need to study some of the tests to understand what is going on, and you may well need to do some of the required reading, and perhaps also some googling, to figure out how to solve some of these.  You are also responsible for understanding how the tests themselves work. For example, understand how the list of first-class functions is being used in testPolynomialAndQuadraticClasses, and also understand why the try/except calls are used in testQuadraticClass.  Good luck!

    def testPolynomialAndQuadraticClasses():
        print "Testing Polynomial and Quadratic classes..."
        for testFn in [testPolynomialBasicsFromClassNotes,
                       testPolynomialEq,
                       testPolynomialStr,
                       testPolynomialRepr,
                       testPolynomialConstructor,
                       testPolynomialInSets,
                       testPolynomialTimesOperator,
                       testPolynomialExponentiationOperator,
                       testQuadraticClass
                      ]:
            print "  Running %s..." % testFn.__name__,
            testFn()
            print "Passed!"
        print "Passed all Polynomial and Quadratic Class tests!"

    def almostEqual(d1, d2):
        epsilon = 0.000001
        return abs(d1 - d2) < epsilon

    def testPolynomialBasicsFromClassNotes():
        # Commented out the string assertions since we actually
        # changed/improved those as part of this hw
        p1 = Polynomial([2, -3, 5])  # 2x**2 -3x + 5
        #assert(str(p1) == "2*x**2+-3*x**1+5*x**0") # ugly, but functional
        assert(type(p1) == Polynomial)
        assert(p1.degree() == 2)
        assert(p1.coeff(0) == 5)
        assert(p1.coeff(1) == -3)
        assert(p1.coeff(2) == 2)
        assert(p1.evalAt(0) == 5)
        assert(p1.evalAt(2) == 7)
        # Now test the derivative method
        p2 = p1.derivative() # 4x - 3
        #assert(str(p2) == "4*x**1+-3*x**0")
        assert(type(p2) == Polynomial)
        assert(p2.evalAt(2) == 5)
        assert(p2.evalAt(5) == 17)
        # Now test the + operator
        p3 = p1 + p2 # (2x**2 -3x + 5) + (4x - 3) == (2x**2 + x + 2)
        #assert(str(p3) == "2*x**2+1*x**1+2*x**0")
        assert(type(p3) == Polynomial)
        assert(p3.evalAt(2) == 12)
        assert(p3.evalAt(5) == 57)

    def testPolynomialEq():
        assert(Polynomial([1,2,3]) == Polynomial([1,2,3]))
        assert(Polynomial([1,2,3]) != Polynomial([1,2,3,0]))
        assert(Polynomial([1,2,3]) != Polynomial([1,2,0,3]))
        assert(Polynomial([1,2,3]) != Polynomial([1,-2,3]))
        assert(Polynomial([1,2,3]) != 42)
        assert(Polynomial([1,2,3]) != "Wahoo!")
        # A polynomial of degree 0 has to equal the same non-Polynomial numeric!
        assert(Polynomial([42]) == 42)

    def testPolynomialStr():
        assert(str(Polynomial([1,2,3])) == "x^2 + 2x + 3")
        assert(str(Polynomial([-1,-2,-3])) == "-x^2 - 2x - 3")
        assert(str(Polynomial([42])) == "42")
        assert(str(Polynomial([-42])) == "-42")
        assert(str(Polynomial([0])) == "0")
        assert(str(Polynomial([1,0,-3, 0, 1])) == "x^4 - 3x^2 + 1")
        assert(str(Polynomial([1,0,-3, 0, 1])) == "x^4 - 3x^2 + 1")
        assert(str(Polynomial([-1,0,3, 0, -1])) == "-x^4 + 3x^2 - 1")

    def testPolynomialRepr():
        for coeffs in [ [1,2,3], [0], [-1,0,2,0,-3] ]:
            assert(eval(repr(Polynomial(coeffs))) == Polynomial(coeffs))

    def testPolynomialConstructor():
        # If the list is empty, treat it the same as [0]
        assert(Polynomial([]) == Polynomial([0]))
        assert(Polynomial([]) != Polynomial([1]))
        # Remove leading 0's
        assert(Polynomial([0,0,0,1,2]) == Polynomial([1,2]))
        assert(Polynomial([0,0,0,1,2]).degree() == 1)
        # Require that the constructor be non-destructive
        coeffs = [0,0,0,1,2]
        assert(Polynomial(coeffs) == Polynomial([1,2]))
        assert(coeffs == [0,0,0,1,2])
        # Require that the constructor also accept tuples of coefficients
        coeffs = (0, 0, 0, 1, 2)
        assert(Polynomial(coeffs) == Polynomial([1,2]))
        # Allow for variable-length arguments.  That is, if the arguments
        # are not a list, then put them in a list
        assert(Polynomial(1,2,3) == Polynomial([1,2,3]))
        # And thus if no values are supplied, this is also the same as [0]:
        assert(Polynomial() == Polynomial([0]))

    def testPolynomialInSets():
        s = set()
        assert(Polynomial(1,2,3) not in s)
        s.add(Polynomial(1,2,3))
        assert(Polynomial(1,2,3) in s)
        assert(Polynomial([1,2,3]) in s)
        assert(Polynomial(1,2) not in s)

    def testPolynomialTimesOperator():
        # (x**2 + 2)(x**4 + 3x**2) == (x**6 + 5x**4 + 6x**2)
        assert(Polynomial([1,0,2]) * Polynomial([1,0,3,0,0]) ==
               Polynomial([1,0,5,0,6,0,0]))
        # (x**3 - 3x + 5) * 10 == (10x**3 - 30x + 50)
        assert(Polynomial(1,0,-3,5) * 10 == Polynomial(10,0,-30,50))
        # Hint: to do multiplication this way, you have to use __rmul__,
        # which should just call __mul__ (yes, really)
        assert(10 * Polynomial(1,0,-3,5) == Polynomial(10,0,-30,50))

    def testPolynomialExponentiationOperator():
        assert(Polynomial(1,2,3)**0 == 1)
        assert(Polynomial(1,2,3)**1 == Polynomial(1,2,3))
        assert(Polynomial(1,2,3)**2 == Polynomial(1,2,3) * Polynomial(1,2,3))
        assert(Polynomial(1,2,3)**3 == Polynomial(1,2,3) * Polynomial(1,2,3) * Polynomial(1,2,3))

    def testQuadraticClass():
        q1 = Quadratic(3,2,1)  # 3x^2 + 2x + 1
        assert(type(q1) == Quadratic)
        assert(q1.evalAt(10) == 321)
        assert(isinstance(q1, Quadratic) == isinstance(q1, Polynomial) == True)
        # the determinant is b**2 - 4ac
        assert(q1.determinant() == -8)
        # use the determinant to determine how many real roots (zeroes) exist
        assert(q1.numberOfRealRoots() == 0)
        assert(q1.getRealRoots() == [ ])
        # Once again, with a double root
        q2 = Quadratic(1,-6,9)
        assert(q2.determinant() == 0)
        assert(q2.numberOfRealRoots() == 1)
        [root] = q2.getRealRoots()
        assert(almostEqual(root, 3))
        # And again with two roots
        q3 = Quadratic(1,1,-6)
        assert(q3.determinant() == 25)
        assert(q3.numberOfRealRoots() == 2)
        [root1, root2] = q3.getRealRoots() # smaller one first
        assert(almostEqual(root1, -3) and almostEqual(root2, 2))
        # Now, creating a non-quadratic "Quadratic" is an error
        ok = False # the exception turns this to True!
        try: q = Quadratic(1,2,3,4) # this is cubic, should fail!
        except: ok = True
        assert(ok)
        # one more time, with a line, which is sub-quadratic, so also fails
        ok = False
        try: q = Quadratic([2,3])
        except: ok = True
        assert(ok)
        # And make sure that these methods were defined in the Quadratic class
        # and not in the Polynomial class (we'll just check a couple of them...)
        assert('evalAt' in Polynomial.__dict__)
        assert('evalAt' not in Quadratic.__dict__)
        assert('determinant' in Quadratic.__dict__)
        assert('determinant' not in Polynomial.__dict__)

    testPolynomialAndQuadraticClasses()

     

  2. "OOPy" Animation    [Note: moved to be part of hw10]
    This is SOLO Place your solution to this problem below the #ignore_rest line (and even below the Polynomial test code from above), since this will not be autograded.  Also note that we discussed this extensively in lecture this week, including actually writing some of the code you need to reproduce here.  Your task is to combine OOP and Animation in a meaningful way.  To do this, first convert our basic events-example0.py into a class named Animation, such that the animation can be run with this code:
        Animation().run()
    Some things to consider when making your Animation class:
    Next, create a fun little game that expressly showcases your understanding of Object-Oriented Programming, just like we did in lecture, only yours needs to be a bit more complete.  It should include at least 4 well-chosen and well-designed classes (in addition to your main class, that should subclass your Animation class you just wrote).  It should also include inheritance in a meaningful and well-designed way.  Also, you should not have a one-to-one correspondence of instances to classes -- that is, at least one of the classes should have more than one instance (so, if you have N classes, you must have at least (N+1) instances).  Also, though not really "OOPy", be sure when you first launch the app, it displays a help screen which explains (briefly) how to play and win the game, and then at any other time, pressing 'h' should toggle the visibility of the help screen.

    Note that you do not need to (nor should you!) get fancy with this game.  You do not need images, or sounds, or snazzy graphics.  You do need to showcase your understanding of OOP and then you also need fun gameplay of some kind.  Your game should also be clearly distinct from the mini-games we made in lecture and in your recitation.  In any case, as a guideline, we expect this to require 3-5 hours for most of you.  Have fun!

  3. Bonus/Optional: Unfinished Bonus!  [up to 7.5 pts]
    This is SOLO For this bonus, you may do any previously-assigned bonus from this semester that you have not yet completed.  To get credit, you need to submit your bonus along with hw9.py, but then you also need to bring your bonus to CA office hours on Monday or Tuesday night, and show your working bonus to the CA on duty.  You may do more than 7.5 pts worth of bonus, but the bonus points will be capped at 7.5 points.  In any case, have fun!


carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem