The Slightly Haunted Cinema is ready for its first ticket desk. It will handle one booking per run. It will not process payments, save bookings, or exorcise the projector. A project becomes easier to finish when its boundaries are clear.
You need: lessons one through four. By the end: you can turn a short specification into a complete program, test it, and explain the choices. Allow 40–60 minutes. Try building it before opening the full solution.
Write the rules before the code
Here is our specification:
- Start with 12 available seats and a price of 850 cents per ticket.
- Ask for a customer’s name and a whole-number ticket count.
- Reject a name that is empty after removing surrounding whitespace.
- Reject a count of zero or less.
- Reject a count greater than the available seats.
- Otherwise, calculate the total, reduce the available count, and print a receipt.
We accept integer-formatted input as a prerequisite for this first version. Non-numeric answers will raise an error; we will address that in a later lesson on exceptions. Writing down a limitation is better than accidentally claiming we solved it.
When several rules fail, show the first applicable message in the order above. For example, a blank name and zero tickets should produce the name message. This is a small but real product decision: our code needs a defined answer.
Sketch the flow in ordinary language
Set starting values
Ask for a name and ticket count
Convert ticket text into a number
If the name is empty: explain the problem
Otherwise, if the count is not positive: explain the problem
Otherwise, if there are not enough seats: explain the problem
Otherwise:
Calculate the total
Reduce the available seats
Print the receipt
This is pseudocode: an informal description of the steps, not something Python can execute. It helps separate deciding what the program should do from remembering exactly where the colon goes.
Notice that we convert the ticket text before checking the name. Consequently, a non-integer ticket answer fails at conversion even if the name is blank. Our promised rule order applies once that conversion succeeds. To change that behaviour, we would need a different input flow.
Build in three small passes
Create ticket_desk.py in your course folder.
Pass one: collect the values. Assign the starting seat count and price, then ask for the name and count. Temporarily print the collected values. Check that " Mina " becomes "Mina" and that the ticket count behaves as a number.
Pass two: choose the outcome. Add the if / elif / else chain. An empty string is written "", so customer_name == "" checks for it explicitly. At first, print only Booking accepted. in the successful branch. Test the rejection rules before adding receipt formatting.
Pass three: calculate and report. Put the total calculation and seat update inside the successful branch. Reuse lesson three’s integer-cent formatting. Invalid requests must not reduce the seats.
This sequence makes mistakes easier to locate. If input worked before you added the branches, you have a smaller area to investigate than if you wrote forty lines and hoped for applause.
Show the complete program
available_seats = 12
ticket_price_cents = 850
customer_name = input("Name: ").strip()
ticket_text = input("Number of tickets (whole number): ")
ticket_count = int(ticket_text)
if customer_name == "":
print("Please enter a name.")
elif ticket_count <= 0:
print("Choose at least one ticket.")
elif ticket_count > available_seats:
print(f"Only {available_seats} seats are available.")
else:
total_cents = ticket_count * ticket_price_cents
available_seats = available_seats - ticket_count
euros = total_cents // 100
cents = total_cents % 100
print("The Slightly Haunted Cinema")
print(f"Booking for {customer_name}")
print(f"Tickets: {ticket_count}")
print(f"Total: EUR {euros}.{cents:02d}")
print(f"Seats remaining: {available_seats}")
With Mina and 3, the receipt is:
The Slightly Haunted Cinema
Booking for Mina
Tickets: 3
Total: EUR 25.50
Seats remaining: 9
Explain the choices
One source for each starting value. The price and seat count are assigned near the top. Repeating 850 throughout the program would make a later price change easy to get wrong. The descriptive name also documents the unit.
Whole cents for the calculation. Addition and multiplication of these integers are exact. We format euros and cents only at the display step. We have no percentage discounts or currency conversions to round yet.
Validation before state changes. The seat count changes only in the accepted branch. Indenting that update incorrectly could reduce availability for a rejected request. Whitespace can be a business bug.
A visible order for competing errors. The chain stops at the first matching condition. Independent if statements would need extra care to avoid accepting a request after rejecting it.
No advanced scaffolding yet. There are no classes, external libraries, or custom functions in this version. Each would add concepts without improving this one-request exercise enough to justify them. We will introduce functions when we need reusable tasks and clearer boundaries.
Test more than the happy path
Run the file again for every row. A new run starts with 12 seats.
| Name | Tickets | Expected result |
|---|---|---|
Mina | 3 | EUR 25.50; 9 seats remain |
Mina | 1 | EUR 8.50; 11 seats remain |
Mina | 12 | EUR 102.00; 0 seats remain |
Mina | 13 | Only 12 seats are available |
Mina | 0 | Choose at least one ticket |
Mina | -1 | Choose at least one ticket |
| Spaces only | 2 | Please enter a name |
Mina | 3 | Receipt uses Mina without surrounding spaces |
Mina | banana | Known limitation: ValueError at conversion |
If 12 is rejected, inspect > versus >=. If a rejected booking prints a receipt, inspect the indentation and whether you used a chain. If the displayed amount is wrong, temporarily print total_cents before changing the formatting.
Do not “fix” a failure by changing the test to match your program. Decide whether the rule or the implementation is wrong, then change the appropriate one.
A small extension: announce a full house
After an accepted booking, print Full house. Even the ghosts need tickets. when no seats remain. Keep this check inside the successful branch.
Show the extension
Add this after the receipt, at the same indentation level as its print() calls:
if available_seats == 0:
print("Full house. Even the ghosts need tickets.")
The inner print() has eight leading spaces because it belongs to an if inside the existing else. Test with 12 tickets and with 11. Only the first should show the extra message.
What this program does not do yet
Rerunning it forgets the previous booking and resets availability. Variables in this program live in the running process; they are not automatically a database. It also handles only one request and cannot recover from invalid integer input. It is a learning program, not a real booking service.
Those limitations give us a useful next sequence: loops for repeated actions; lists and dictionaries for groups of values; functions for reusable tasks; exceptions for recoverable failures; files for persistence. After that, we can introduce testing, modules, and libraries with an actual reason to use them.
You have reached the first project milestone. Before continuing to lesson six, close the solution and rebuild the ticket desk from the specification. Explain each condition aloud, run the test table, and change the seat count to 20 without scattering new numbers through the code. That is a better checkpoint than memorising syntax.