A simple coffee machine simulator written in Python — a small stateful program that models a real coffee vending machine: it stores resources (water, milk, coffee beans, disposable cups, money), serves three coffee types (espresso, latte, cappuccino), notifies when resources are insufficient and accepts buy, fill, take, remaining, and exit commands.
This project was originally created while working through a Hyperskill exercise. Project: https://hyperskill.org/projects/68 Profile: https://hyperskill.org/profile/386978127
- Overview
- Features
- How it works (quick)
- Requirements
- Run the program
- Interactive commands & prompts
- Example session
- Project structure & code walkthrough
- Testing & validation ideas
- Possible improvements
- Contributing
- License
The coffee machine program models:
- an inventory of resources (water, milk, beans, cups, money),
- a set of recipes for drinks (espresso, latte, cappuccino),
- a simple state machine to drive user interaction: picking actions, buying, filling incremental resources, taking money, printing remaining resources, and exiting.
It is an educational project suitable for practicing:
- object-oriented design,
- simple state machines,
- defensive input handling,
- small CLI interactions.
- Three predefined recipes with resource consumption and prices.
- Track of machine resources and money collected.
- Interactive mode with step-by-step prompts.
- Fill action walks through adding water → milk → beans → cups.
- Clear user prompts and friendly messages when resources are insufficient.
- Easy to extend (add recipes, commands, or tests).
-
Program instantiates a
CoffeeMachinewith anInventory. -
It repeatedly prints a prompt (based on the current internal state) and reads user input.
-
Input is processed by the machine's
handlemethod which:- changes state when starting
buyorfill, - makes coffee if enough resources exist,
- performs
take/remaining/exit.
- changes state when starting
-
The
fillcommand advances through sub-states (fill_water,fill_milk,fill_coffee.beans,fill_disposable.cups) to add resources in sequence.
- Python 3.9+ (uses modern typing such as
tuple[bool, str]; for older Python versions replace withTuple[...]fromtyping). - No external dependencies.
Save your Python file (e.g. coffee_machine.py) and run:
python coffee_machine.pyThe program runs in the terminal until you type exit (or the machine enters the exit state).
Top-level actions (when machine is in action state):
Write action (buy, fill, take, remaining, exit):
-
buy→ moves tobuystate and prompts:What do you want to buy? 1 - espresso, 2 - latte, 3 - cappuccino, back - to main menu:Enter
1,2,3, orback. -
fill→ begins the fill sequence:Write how many ml of water you want to add:Write how many ml of milk you want to add:Write how many grams of coffee beans you want to add:Write how many disposable cups you want to add:After the last entry, the state returns toaction.
-
take→ empties the money and prints how much was given:I gave you $X -
remaining→ prints current resources (multi-line). -
exit→ exits the program loop.
Below is a short example interaction illustrating behavior.
Write action (buy, fill, take, remaining, exit):
> remaining
The coffee machine has:
400 ml of water
540 ml of milk
120 g of coffee beans
9 disposable cups
$550 of money
Write action (buy, fill, take, remaining, exit):
> buy
What do you want to buy? 1 - espresso, 2 - latte, 3 - cappuccino, back - to main menu:
> 1
I have enough resources, making you a coffee!
Write action (buy, fill, take, remaining, exit):
> buy
What do you want to buy? 1 - espresso, 2 - latte, 3 - cappuccino, back - to main menu:
> 2
Sorry, not enough water!
Write action (buy, fill, take, remaining, exit):
> fill
Write how many ml of water you want to add:
> 200
Write how many ml of milk you want to add:
> 100
Write how many grams of coffee beans you want to add:
> 50
Write how many disposable cups you want to add:
> 5
Write action (buy, fill, take, remaining, exit):
> take
I gave you $554
Write action (buy, fill, take, remaining, exit):
> exit
Main classes and responsibilities:
-
Recipe- Wraps a recipe entry from
CoffeeMachine.RECIPES. - Holds
name,water,milk,beans, andprice. - Instantiated with a recipe key (
'1','2','3').
- Wraps a recipe entry from
-
Inventory- Stores resource amounts as attributes:
water,milk,beans,cups,money. - Provides
has_enough_for(recipe)returning(True, "")or(False, "<resource>"). - Provides helper methods to adjust resources (
add_water,add_milk, ...), andwithdraw_moneywhich sets money to 0.
- Stores resource amounts as attributes:
-
CoffeeMachineRECIPESstatic mapping of recipe keys to tuples:(name, water, milk, beans, price).- Maintains
inventoryandstate. get_prompt()chooses which prompt to display according tostate.handle(line)processes a single line of input depending onstate.define_action,fill_resources,buy_coffeeprovide the logic for actions.__str__prints a human-readable description of the inventory.
Note about current code: The code is functional and implements the required behavior. There are some design choices that can be addressed for clarity (see the "Possible improvements" section).
- Unit tests for
Inventory.has_enough_forwith edge cases (exact resources, zero, negative). - Unit tests for
fill_resourcessequence to ensure state transitions happen correctly. - Integration test for a full interactive session simulated by a sequence of inputs.
- Test error handling when non-integer values are entered during
fill.
If you want to iterate or polish the project, consider:
- Move initial resource defaults into
Inventory.__init__(currently class attributes). This avoids resource sharing between instances and fits typical OOP practices. - Improve
Recipeto accept the recipe tuple on construction instead of instantiatingCoffeeMachine()insideRecipe.__init__(creating a new CoffeeMachine there is wasteful/unexpected). - Add persistence (save/load inventory to disk) so the machine keeps state between runs.
- Add CLI flags to run scripted sessions or to run tests automatically.
- Add more drinks, configurable recipes loaded from a JSON/YAML file.
- Add better input validation and explicit error messages.
- Add localization or language support for prompts.
- Add logging (instead of prints) to facilitate debugging and testing.
- Replace
tuple[bool, str]typing for older Python compatibility withTuplefromtypingif you need to support Python < 3.9.
- Fork the repository and create a feature branch.
- Create tests for any new behaviors.
- Open a pull request describing the change and why it's useful.
If you're using this repository as a learning exercise, try refactoring with the suggestions in the previous section and run through unit tests.
- Hyperskill project: https://hyperskill.org/projects/68
- Author (profile): https://hyperskill.org/profile/386978127