Case Study 1: Simulating a Cash Register
Overview
In this case study, we build a complete cash register simulation that exercises all three of Pascal's loop constructs in a single, cohesive program. A cashier scans items for a customer, the register prints a receipt, and then the next customer steps up. This mirrors the real-world flow of a retail checkout and demonstrates how different loop types naturally fit different aspects of the same problem.
Loop constructs used:
- REPEAT..UNTIL — scanning items for a single customer (at least one item per transaction)
- FOR loop — printing the itemized receipt
- WHILE..DO — processing the queue of customers (which might be empty)
Problem Statement
Write a program that simulates a cash register with the following behavior:
- The cashier enters the number of customers waiting in line.
- For each customer: a. The cashier scans items by entering item names and prices. b. Entering a price of 0 signals the end of that customer's items. c. The register prints an itemized receipt with subtotal, tax (8.5%), and total.
- After all customers are processed, the register prints a shift summary showing total customers served, total items scanned, and total revenue.
Design
Data Structures
We will store each customer's items in arrays (up to 50 items per transaction). Each item has a name and a price.
Loop Structure
Read CustomerCount
WHILE CustomerCount > 0:
For each customer:
REPEAT:
Read item name and price
UNTIL price = 0
FOR i := 1 to ItemCount:
Print receipt line
Decrement CustomerCount
Print shift summary
The outer WHILE loop handles the customer queue. If the cashier enters 0 customers, the program skips directly to the summary (correctly handling an empty queue). Inside it, the REPEAT..UNTIL loop handles item scanning — every transaction has at least one item. The FOR loop prints the receipt, iterating over the exact number of recorded items.
Implementation
program CashRegister;
const
TAX_RATE = 0.085;
MAX_ITEMS = 50;
var
{ Item arrays for current transaction }
ItemNames: array[1..MAX_ITEMS] of string;
ItemPrices: array[1..MAX_ITEMS] of Real;
{ Transaction variables }
ItemCount: Integer;
Price: Real;
Subtotal: Real;
Tax: Real;
Total: Real;
ItemName: string;
{ Queue variables }
TotalCustomers: Integer;
CustomersLeft: Integer;
CustomerNumber: Integer;
{ Shift summary variables }
ShiftRevenue: Real;
ShiftItems: Integer;
{ Loop variable }
i: Integer;
begin
WriteLn('================================');
WriteLn(' PASCAL MART CASH REGISTER');
WriteLn('================================');
WriteLn;
{ Initialize shift totals }
ShiftRevenue := 0;
ShiftItems := 0;
{ Get number of customers }
Write('How many customers in line? ');
ReadLn(CustomersLeft);
TotalCustomers := CustomersLeft;
CustomerNumber := 0;
{ ===== OUTER LOOP: Customer Queue (WHILE) ===== }
{ Using WHILE because there might be zero customers }
while CustomersLeft > 0 do
begin
CustomerNumber := CustomerNumber + 1;
WriteLn;
WriteLn('--- Customer ', CustomerNumber, ' ---');
WriteLn('Scan items (enter $0.00 to finish):');
ItemCount := 0;
{ ===== INNER LOOP 1: Item Scanning (REPEAT) ===== }
{ Using REPEAT because every transaction has at least one item }
repeat
Write(' Item name: ');
ReadLn(ItemName);
Write(' Price: $');
ReadLn(Price);
if Price > 0 then
begin
ItemCount := ItemCount + 1;
if ItemCount <= MAX_ITEMS then
begin
ItemNames[ItemCount] := ItemName;
ItemPrices[ItemCount] := Price;
end
else
WriteLn(' WARNING: Maximum items reached!');
end
else if Price < 0 then
WriteLn(' Price cannot be negative. Item not added.');
until Price <= 0;
{ ===== Print Receipt ===== }
if ItemCount > 0 then
begin
WriteLn;
WriteLn(' ==============================');
WriteLn(' PASCAL MART RECEIPT');
WriteLn(' ==============================');
Subtotal := 0;
{ ===== INNER LOOP 2: Receipt Printing (FOR) ===== }
{ Using FOR because we know exactly how many items }
for i := 1 to ItemCount do
begin
WriteLn(' ', ItemNames[i]:20, ' $', ItemPrices[i]:7:2);
Subtotal := Subtotal + ItemPrices[i];
end;
Tax := Subtotal * TAX_RATE;
Total := Subtotal + Tax;
WriteLn(' ------------------------------');
WriteLn(' ', 'Subtotal':20, ' $', Subtotal:7:2);
WriteLn(' ', 'Tax (8.5%)':20, ' $', Tax:7:2);
WriteLn(' ==============================');
WriteLn(' ', 'TOTAL':20, ' $', Total:7:2);
WriteLn(' ==============================');
WriteLn;
{ Update shift totals }
ShiftRevenue := ShiftRevenue + Total;
ShiftItems := ShiftItems + ItemCount;
end
else
WriteLn(' No items scanned. Transaction voided.');
CustomersLeft := CustomersLeft - 1;
if CustomersLeft > 0 then
WriteLn(' Next customer, please! (', CustomersLeft, ' remaining)');
end;
{ ===== Shift Summary ===== }
WriteLn;
WriteLn('================================');
WriteLn(' SHIFT SUMMARY');
WriteLn('================================');
WriteLn(' Customers served: ', TotalCustomers);
WriteLn(' Items scanned: ', ShiftItems);
WriteLn(' Total revenue: $', ShiftRevenue:0:2);
WriteLn('================================');
end.
Sample Run
================================
PASCAL MART CASH REGISTER
================================
How many customers in line? 2
--- Customer 1 ---
Scan items (enter $0.00 to finish):
Item name: Milk
Price: $3.49
Item name: Bread
Price: $2.99
Item name: Eggs
Price: $4.25
Item name: done
Price: $0
==============================
PASCAL MART RECEIPT
==============================
Milk $ 3.49
Bread $ 2.99
Eggs $ 4.25
------------------------------
Subtotal $ 10.73
Tax (8.5%) $ 0.91
==============================
TOTAL $ 11.64
==============================
Next customer, please! (1 remaining)
--- Customer 2 ---
Scan items (enter $0.00 to finish):
Item name: Coffee
Price: $8.99
Item name: done
Price: $0
==============================
PASCAL MART RECEIPT
==============================
Coffee $ 8.99
------------------------------
Subtotal $ 8.99
Tax (8.5%) $ 0.76
==============================
TOTAL $ 9.75
==============================
================================
SHIFT SUMMARY
================================
Customers served: 2
Items scanned: 4
Total revenue: $21.39
================================
Analysis
Why Each Loop Type Fits
| Task | Loop Used | Why This Loop |
|---|---|---|
| Customer queue | WHILE..DO |
The number of customers might be zero. If the cashier enters 0, we skip straight to the summary. A REPEAT..UNTIL loop would try to process a nonexistent customer. |
| Item scanning | REPEAT..UNTIL |
Every real transaction involves scanning at least one item before we can check whether the cashier is done. The "scan then check" pattern is a natural post-test situation. |
| Receipt printing | FOR |
After scanning, we know exactly how many items were recorded (ItemCount). A counted loop is the cleanest way to iterate through them. |
Patterns Used
- Accumulator pattern:
Subtotalaccumulates item prices;ShiftRevenueaccumulates transaction totals. - Counter pattern:
ItemCounttracks items per transaction;ShiftItemstracks items per shift. - Sentinel loop: Price of $0 serves as the sentinel for ending item entry.
- Input validation: Negative prices are caught and rejected (though a more robust version would use a
REPEAT..UNTILvalidation loop for each price).
Possible Enhancements
- Discount codes: Add a prompt after scanning that applies a percentage discount before tax.
- Payment processing: After printing the total, ask for the payment amount and compute change using a
REPEAT..UNTILloop (ensure payment >= total). - Item categories: Track whether items are taxable or tax-exempt (groceries vs. prepared food).
- Receipt numbering: Use
CustomerNumberas the receipt number.
Exercises
- Add a payment step after receipt printing. Use a
REPEAT..UNTILloop to ensure the customer pays enough, then display the change. - Add a "void last item" command. If the cashier enters "VOID" as the item name, remove the last scanned item from the arrays and decrement
ItemCount. - Modify the program so that the cashier does not need to enter the number of customers up front. Instead, after each customer, ask "Is there another customer? (Y/N)". Which loop type should you use for this outer loop?
- Add a running subtotal that displays after each item is scanned, so the cashier can see the accumulating total in real time.