Objectives

The goal of this assignment is to practice:

  • Writing code that creates and manipulates objects from an existing class definition.
  • Creating Python classes both with and without constructors.

Docstrings

For this homework, and all of the remaining homeworks, all of your modules and functions must exhibit correct documentation. Submissions that don’t pass docstring checks will receive no credit.

You should read the Docstring Requirements carefully to see how it should be done.

Exercise 9.1 Course Registration (Using Existing Classes)

Each semester the computer science department needs to determine the appropriate number of sections for each course based on the expected number of students who will enroll. For this exercise you will write a function to help us out with that process by creating an appropriate collection of Section objects to meet student demand. The Section class has been completed for you in the fileregistration.py. The examples below illustrate the process of creating a single Section object and calling its instance methods.

sec1 = Section("CS 159", 1, 32) # Create a section object.
    
print(sec1.course_code)     # Prints: 'CS 159'
print(sec1.section_number)  # Prints: 1
print(sec1.capacity)        # Prints: 32
print(sec1.enrolled)        # Prints: 0
    
sec1.add_students(10)
print(sec1.enrolled)        # Prints: 10
    
sec1.add_students(25)       # Prints: 'Error: not enough seats!'
print(sec1.enrolled)        # Prints: 10

For this exercise, you must complete the unfinished create_sections method that is stubbed out in registration.py. You should not modify the provided Section class. The following code snippet shows an example of calling the completed function.

sections = create_sections("CS 149", 30, 100)
for section in sections:
    print(section)

In this case the output should be:

CS 149 0001 (30/30)
CS 149 0002 (30/30)
CS 149 0003 (30/30)
CS 149 0004 (10/30)

Exercise 9.2 Shopping Cart

Typically, an online store will have the concept of a “shopping cart”. In this problem we implement such a shopping cart.

Start from the file called shopping.py. In this file create a class called ShoppingCart. The class will have the following structure:

  • An attribute contents, which is a list containing the names of what the user wants to buy.
  • Methods:
    • buy, which takes one parameter – the name of the product the user wants to buy. It returns nothing.
    • total_cost, which takes no parameters and returns the total cost of everything in the list contents.

The starter code contains a price dictionary you will use to calculate the total cost.

Here are some examples illustrating the expected behavior:

>>> %Run interval.py
>>> cart = ShoppingCart()
>>> cart.buy("Boots")
>>> cart.buy("FitBit")
>>> cart.total_cost()
220

Exercise 9.3

When a bank makes a loan to a private individual, they typically charge some percentage interest every month, and the borrower pays a monthly payment. You will create a class to represent a loan and its repayment. In a file named loan.py, create a class named Loan. It will have the following attributes:

  • loan_amount: the current amount of the loan
  • monthly_rate: the monthly percentage interest charged
  • total_paid: the total amount paid on the loan to date.

It will have a constructor and one function:

  • Create a constructor that takes the appropriate parameters and initializes anything else it needs to initialize.
  • pay_monthly_bill: calculates the new value of loan_amount after interest is charged and the monthly payment is made. The function accepts one parameter representing the amount of payment being made. This function should also update total_paid so that it includes the new amount paid. Take special care when the borrower makes a payment that is higher than the current loan amount plus interest.

Here are some examples illustrating the expected behavior:

>>> %Run loan.py
>>> l = Loan(1000, 0.5)
>>> l.total_paid
0
>>> l.pay_monthly_bill(20)
>>> l.loan_amount
985
>>> l.total_paid
20
>>> l.pay_monthly_bill(1000)
>>> l.loan_amount
0
>>> l.total_paid
1009.925

Exercise 9.4 Object-Oriented Book Cipher

In Exercise 8.5 we implemented a book cipher using books downloaded from Project Gutenberg. For this exercise you will create a more user-friendly implementation using classes. Your class must be named BookCipher and it must have the the following methods:

  • The constructor __init__, must take a single string argument containing a url representing the web address of a text document. The constructor must complete the following steps:
    • Download the text of the book and convert it into a list of words. These words must be stored in an instance variable named book_words.
    • Create a dictionary that maps from all words in book_words to the index of their final appearance. This dictionary must be stored in an instance variable named word_locations.
  • encode This method will take a single argument containing a list of strings. It must return a list of integers corresponding to the locations of the words in book_words. Words that do not appear in book_words must be encoded as -1.
  • encode This method will take a single argument containing a list of integers representing an encoded message. It must return the decoded message as described in HW 8.5.

The following interaction illustrates the desired behavior:

>>> %Run book_cipher_oo.py
>>> cipher = BookCipher('https://w3.cs.jmu.edu/spragunr/jabberwock.txt')
>>> print(cipher.book_words)
['beware', 'the', 'jabberwock', 'my', 'son', 'the', 'jaws', 'that', 'bite', 'the', 'claws', 'that', 'catch']
>>> print(cipher.word_locations)
{'beware': 0, 'the': 9, 'jabberwock': 2, 'my': 3, 'son': 4, 'jaws': 6, 'that': 11, 'bite': 8, 'claws': 10, 'catch': 12}
>>> print(cipher.encode(['bite', 'the', 'jabberwock', 'please']))
[8, 9, 2, -1]
>>> print(cipher.decode([8, 9, 2, -1]))
['bite', 'the', 'jabberwock', '---']

Notes:

  • Don’t worry if you didn’t complete HW 8.5. We’ll post a reference solution after the HW8 deadline that you can use as a starting point.
  • The url in the example above, https://w3.cs.jmu.edu/spragunr/jabberwock.txt, refers to a very tiny except from Lewis Carroll’s Through the Looking Glass. This is provided to help you debug your implementation using a manageable input size. The full text is located here:https://www.gutenberg.org/files/12/12-0.txt

Exercise 9.5 Parking Lot

There’s a major event happening at the fairgrounds, and cars are lining up from everywhere. We need a program to help vehicles find a parking space when they arrive. Since parking is limited, and not all of the spaces are the same size, we need to be careful to put smaller vehicles in the smaller spots so that we can save the larger spots for vehicles that need them.

For the sake of simplicity both cars and parking spots will be represented as rectangles. A simple Rectangle class has been provided. Download the following two files:

Define a class named ParkingLot in parking.py with the following methods:

  • A constructor that takes a list of rectangle objects representing available parking spots. The constructor must store a copy of this list in an instance variable named spaces.

  • best_fit - This method takes a single Rectangle argument representing a car that needs to be parked. It must return the index of the smallest available parking spot that can accommodate the car. There must be at least two feet of space around all sides of the car when parked. Otherwise the passengers will have a hard time getting out without damaging adjacent vehicles. In the case of ties, the space with the larger index should be selected. If there is no spot that can accommodate the car, then the method must return -1.

  • park - This method takes a single Rectangle argument representing a car that needs to be parked. The car must be parked in the most appropriate spot as selected by the best_fitmethod.

    When a car is parked, two changes must happen:

    • The list element for the space must be set to None (i.e., “occupied”).
    • The car object must be “moved” to be centered within the space.

    For example, if the space is 10x10 at location (0, 0) and the car is 6x6, then the car’s location must be updated to (2, 2) as shown in the diagram:

    Parking Lot Example

    The park method must return the index where the car was parked. If no appropriate parking space is available, the method must return -1 (and no changes should be made to the spaces list or car object).

  • num_spaces - This method must return the remaining number of unoccupied parking spaces.

For this exercise, you may assume that widths will be less than or equal to heights (i.e., portrait orientation). There is no need to “rotate” the rectangles – there is no parallel parking at the fairgrounds.

Submit only parking.py to Gradescope.

Note: We are not providing any sample code illustrating usage for the class described above. The feedback provided by Gradescope probably won’t be very useful. In order to complete this exercises you will probably need to spend some time developing your own testing code. I suggest adding an if __name__ == "__main__" block and writing code to test various parking scenarios. Don’t bother submitting to Gradescope until you have convinced yourself that your code is working correctly.

Back to Top