# CMU 15-112 Spring 2018: Fundamentals of Programming and Computer Science Homework 9 (Due Saturday 24-Mar, at 8pm)

• This assignment is SOLO. This means you may not look at other student's code or let other students look at your code for these problems. See the syllabus for details.

• To start:
1. Go to your folder named 'week9'
2. Create a file named hw9.py in that folder
3. Edit hw9.py using Pyzo
4. When you're done, submit hw9.py to Autolab. You may submit up to 5 times.
• Do not hardcode the test cases in your solutions.

1. Lab Problems [10 pts]
Attend your scheduled lab on Friday. While there, complete the basic problem and make a real attempt at the advanced problem. One of the TAs will record your participation by hand.

2. VendingMachine class [35 pts]
Write the VendingMachine class so that it passes testVendingMachineClass, and uses the OOP constructs we learned this week as appropriate.

Hint: Make sure you're representing money properly wherever it might appear in the string representation of a Vending Machine. The test cases shown here don't cover every possible case...

def testVendingMachineClass(): print("Testing Vending Machine class...", end="") # Vending machines have three main properties: # how many bottles they contain, the price of a bottle, and # how much money has been paid. A new vending machine starts with no # money paid. vm1 = VendingMachine(100, 125) assert(str(vm1) == "Vending Machine:<100 bottles; \$1.25 each; \$0 paid>") assert(vm1.isEmpty() == False) assert(vm1.getBottleCount() == 100) assert(vm1.stillOwe() == 125) # When the user inserts money, the machine returns a message about their # status and any change they need as a tuple. assert(vm1.insertMoney(20) == ("Still owe \$1.05", 0)) assert(str(vm1) == "Vending Machine:<100 bottles; \$1.25 each; \$0.20 paid>") assert(vm1.stillOwe() == 105) assert(vm1.getBottleCount() == 100) assert(vm1.insertMoney(5) == ("Still owe \$1", 0)) # When the user has paid enough money, they get a bottle and # the money owed resets. assert(vm1.insertMoney(100) == ("Got a bottle!", 0)) assert(vm1.getBottleCount() == 99) assert(vm1.stillOwe() == 125) assert(str(vm1) == "Vending Machine:<99 bottles; \$1.25 each; \$0 paid>") # If the user pays too much money, they get their change back with the # bottle. assert(vm1.insertMoney(500) == ("Got a bottle!", 375)) assert(vm1.getBottleCount() == 98) assert(vm1.stillOwe() == 125) # Machines can become empty vm2 = VendingMachine(1, 120) assert(str(vm2) == "Vending Machine:<1 bottle; \$1.20 each; \$0 paid>") assert(vm2.isEmpty() == False) assert(vm2.insertMoney(120) == ("Got a bottle!", 0)) assert(vm2.getBottleCount() == 0) assert(vm2.isEmpty() == True) # Once a machine is empty, it should not accept money until it is restocked. assert(str(vm2) == "Vending Machine:<0 bottles; \$1.20 each; \$0 paid>") assert(vm2.insertMoney(25) == ("Machine is empty", 25)) assert(vm2.insertMoney(120) == ("Machine is empty", 120)) assert(vm2.stillOwe() == 120) vm2.stockMachine(20) # Does not return anything assert(vm2.getBottleCount() == 20) assert(vm2.isEmpty() == False) assert(str(vm2) == "Vending Machine:<20 bottles; \$1.20 each; \$0 paid>") assert(vm2.insertMoney(25) == ("Still owe \$0.95", 0)) assert(vm2.stillOwe() == 95) vm2.stockMachine(20) assert(vm2.getBottleCount() == 40) # We should be able to test machines for basic functionality vm3 = VendingMachine(50, 100) vm4 = VendingMachine(50, 100) vm5 = VendingMachine(20, 100) vm6 = VendingMachine(50, 200) vm7 = "Vending Machine" assert(vm3 == vm4) assert(vm3 != vm5) assert(vm3 != vm6) assert(vm3 != vm7) # should not crash! s = set() assert(vm3 not in s) s.add(vm4) assert(vm3 in s) s.remove(vm4) assert(vm3 not in s) assert(vm4.insertMoney(50) == ("Still owe \$0.50", 0)) assert(vm3 != vm4) print("Done!")

3. Gate class and Subclasses [30 pts]
A logic gate is a physical device that creates the functional equivalent of a logic operation in code [and, or, not]. It takes some number of input values, such as two values for and (input1 and input2), and produces a single output value. Write the Gate classes required to make the following test function work properly.

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. result = [ ] for var in clss.__dict__: val = clss.__dict__[var] if (isinstance(val, types.FunctionType)): result.append(var) return sorted(result) def testGateClasses(): print("Testing Gate Classes... ", end="") # require methods be written in appropriate classes assert(getLocalMethods(Gate) == ['__init__', '__str__', 'numberOfInputs', 'setInput']) assert(getLocalMethods(AndGate) == ['getOutput']) assert(getLocalMethods(OrGate) == ['getOutput']) assert(getLocalMethods(NotGate) == ['getOutput', 'numberOfInputs']) # make a simple And gate and1 = AndGate() assert(type(and1) == AndGate) assert(isinstance(and1, Gate) == True) assert(and1.numberOfInputs() == 2) and1.setInput(0, True) and1.setInput(1, False) # Hint: to get the name of the class given an object obj, # you can do this: type(obj).__name__ # You might do this in the Gate.__str__ method... assert(str(and1) == "And(True,False)") assert(and1.getOutput() == False) and1.setInput(1, True) # now both inputs are True assert(and1.getOutput() == True) assert(str(and1) == "And(True,True)") # make a simple Or gate or1 = OrGate() assert(type(or1) == OrGate) assert(isinstance(or1, Gate) == True) assert(or1.numberOfInputs() == 2) or1.setInput(0, False) or1.setInput(1, False) assert(or1.getOutput() == False) assert(str(or1) == "Or(False,False)") or1.setInput(1, True) assert(or1.getOutput() == True) assert(str(or1) == "Or(False,True)") # make a simple Not gate not1 = NotGate() assert(type(not1) == NotGate) assert(isinstance(not1, Gate) == True) assert(not1.numberOfInputs() == 1) not1.setInput(0, False) assert(not1.getOutput() == True) assert(str(not1) == "Not(False)") not1.setInput(0, True) assert(not1.getOutput() == False) assert(str(not1) == "Not(True)") print("Passed!") testGateClasses()

4. recursive powerSum(n, k) [10 pts]
Write the function powerSum(n, k) that takes two possibly-negative integers n and k and returns the so-called power sum: 1**k + 2**k + ... + n**k. If n is negative, return 0. Similarly, if k is negative, return 0.

You must use recursion to solve this problem; using a for or while loop will result in the code failing on Autolab. You may also not use inherently iterative functions. These include range, sum, max, min, count, replace, sort, reverse, sorted, and reversed.

5. recursive generateLetterString [15 pts]
Write the function generateLetterString(s) that takes a two-character string and returns a new string that contains the all of the letters between the first letter and the second letter. For example, generateLetterString("ko") would return "klmno". This should also work backwards, so generateLetterString("ME") would return "MLKJIHGFE". If the initial provided string is not two characters long or contains two identical characters (like "22"), return the empty string.

You must use recursion to solve this problem; using a for or while loop will result in the code failing on Autolab. You may also not use inherently iterative functions. These include range, sum, max, min, count, replace, sort, reverse, sorted, and reversed.