HackerRank Python (Basic) certification

Vending Machine

Last checked 3 min read

A classes question about state and exceptions. The object has to remember how many items are left, and the error messages have to match HackerRank’s text exactly.

What the question asks

Write a class VendingMachine(num_items, item_price). num_items is how many items the machine holds at the start, and item_price is the number of coins one item costs.

It has one method, buy(req_items, money): a customer wants req_items items and puts in money coins. Three things can happen:

  • The machine has fewer items than requested: raise a ValueError with the message Not enough items in the machine.
  • There are enough items, but the money is less than req_items * item_price: raise a ValueError with the message Not enough coins.
  • Otherwise the sale goes through: reduce the stock by req_items and return the change as an integer.

The stub creates one machine, then runs up to 100 buy requests on it. For each one it writes either the change or the text of the ValueError it caught.

Function to write

vending_machine.pyPython 3
class VendingMachine:
    def __init__(self, num_items, item_price): ...
    def buy(self, req_items, money): ...

Solution

vending_machine.pyPython 3
class VendingMachine:
    def __init__(self, num_items, item_price):
        self.num_items = num_items
        self.item_price = item_price

    def buy(self, req_items, money):
        if req_items > self.num_items:
            raise ValueError("Not enough items in the machine")

        cost = req_items * self.item_price
        if money < cost:
            raise ValueError("Not enough coins")

        self.num_items -= req_items
        return money - cost

How it works

  • The constructor stores the stock and the price on the object, so every later buy call sees the current stock.
  • The stock check comes first. If both problems are true at once (too few items and too little money), the expected message is the items one.
  • raise ValueError("...") stops the method. The stub catches it with except ValueError as e and writes str(e), which is exactly the message you passed.
  • Only a successful sale changes self.num_items. A failed request leaves the machine as it was.
  • Paying the exact amount is a valid sale and returns 0.

Mistakes that fail the hidden tests

  • Changing the message text. No full stop at the end, capital N, and “machine” not “stock”. “Not enough money” or “Insufficient coins” fail.
  • Checking money before stock. A request like 10 items for 3 coins on a machine with 9 items must say “Not enough items in the machine”.
  • Reducing the stock before the checks. If you subtract first and then raise, the next request sees the wrong stock.
  • Using >= for the stock check. Buying all the remaining items is allowed. Only req_items > num_items is an error.
  • Returning money - item_price. The change is money minus the price of all the items bought, not one item.

A note on sources: the question text says to raise ValueError, and the stub catches ValueError. Some public solutions return the message as a string instead, and that also prints the right line because the stub writes whatever buy returns. One repo uses all-lowercase messages (“not enough coins”). Go with the question: raise ValueError with the capitalised messages above. If the text in your test shows a different wording, copy their wording exactly.

Try it on sample data

A machine with 10 items at 2 coins each, then four requests, in the stub’s format:

10 2
4
1 5
10 100
7 100
2 3

Output from my run with a copy of the stub’s loop:

3
Not enough items in the machine
86
Not enough coins

Buying 1 item with 5 coins returns 3 and leaves 9 items. Asking for 10 fails. Buying 7 with 100 coins returns 86 and leaves 2. The last request has enough items but 3 coins is less than the 4 needed.