Design an ATM System
Design an ATM system that allows a customer to insert a card, enter a PIN, check the account balance, deposit cash, withdraw cash, cancel a session, and eject the card.
The ATM supports notes with denominations 100, 200, 500, and 2,000. Only one card can be active in the ATM at a time.
Constructor
ATMSystem()
- Creates an ATM with no accounts and no cash.
- The ATM initially has no active card.
Add an Account
boolean addAccount(String cardNumber, String pin, int openingBalance)
- Adds an account identified by
cardNumber.
pin must contain exactly four digits.
- Returns
true when the account is added.
- Returns
false if the card number already exists or any parameter is invalid.
Load Cash
boolean loadCash(int denomination, int count)
- Adds
count notes of the given denomination to the ATM.
- Repeated calls add to the existing number of notes.
- Returns
true when the notes are added.
- Returns
false if the denomination is unsupported or count is not positive.
Customer Operations
Insert a Card
boolean insertCard(String cardNumber)
- Returns
true and starts a session when the card exists, is not blocked, and no other card is active.
- Returns
false otherwise.
- A newly inserted card is not authenticated until the correct PIN is entered.
Enter a PIN
boolean enterPin(String pin)
- Returns
true and authenticates the active card when the PIN is correct.
- Returns
false when there is no active card or the PIN is incorrect.
- An incorrect PIN increases the failed-attempt count for that card.
- After three consecutive incorrect attempts, the card is permanently blocked and automatically ejected.
- Failed attempts are counted across sessions for the same card.
- A successful PIN entry resets the card's failed-attempt count to zero.
- If the current session is already authenticated, this method returns
false and makes no changes.
Check Balance
int getBalance()
- Returns the balance of the authenticated account.
- Returns
-1 if there is no authenticated session.
Withdraw Cash
List<Integer> withdrawCash(int amount)
- Withdraws exactly
amount from the authenticated account.
- The amount must be positive and divisible by
100.
- The account must have enough balance, and the ATM must be able to dispense the exact amount using its available notes.
- The ATM uses the smallest possible number of notes.
- If multiple selections use the same number of notes, prefer more notes of higher denominations in this order:
2,000, 500, 200, 100.
- Returns the dispensed note values in descending order.
- Returns
List.of(-1) if the withdrawal cannot be completed, the amount is not positive, or the amount is not divisible by 100.
- A failed withdrawal does not change the account balance or ATM cash.
Deposit Cash
boolean depositCash(List<Integer> notes)
- Deposits the supplied notes into the authenticated account.
- Every value in
notes must be a supported denomination.
- Returns
true after adding the total value to the account balance and adding the notes to the ATM.
- Returns
false if there is no authenticated session, the list is empty, or any note is invalid.
- Returns
false and makes no changes if the resulting account balance would exceed 1,000,000.
- A failed deposit does not change the account balance or ATM cash.
Cancel the Session
boolean cancelSession()
- Cancels the current session, clears authentication, and ejects the active card.
- It can be called before or after the card is authenticated.
- Returns
true when an active session is cancelled.
- Returns
false when no card is active.
- It does not reverse any deposit or withdrawal that already completed successfully.
- It does not reset failed PIN attempts for the card.
Eject the Card
boolean ejectCard()
- Ejects the active card, clears authentication, and ends the current session.
- Returns
true when a card is ejected.
- Returns
false when no card is active.
- It does not reset failed PIN attempts for the card.
Method Call Order
enterPin returns false if no card is active.
enterPin returns false and makes no changes if the current session is already authenticated.
getBalance returns -1 if the active card has not been authenticated.
withdrawCash returns List.of(-1) if the active card has not been authenticated.
depositCash returns false if the active card has not been authenticated.
insertCard returns false if another card is already active.
cancelSession and ejectCard return false if no card is active.
- After a session is cancelled or a card is ejected, customer operations fail until another card is inserted and authenticated.
- A method called in the wrong order makes no changes, except that an incorrect PIN entered before authentication increases the card's failed-attempt count.
Rules
- Card numbers are case-sensitive.
- Account balances and ATM note counts must never become negative.
- An account balance must never exceed
1,000,000.
- Deposit, withdrawal, and balance operations require an authenticated session.
- Every deposit and withdrawal is completed as one atomic operation.
- A failed operation must not make partial changes.
- Calls are processed sequentially in the order received.
- Deposited notes are immediately available for later withdrawals.
- Cancelling or ejecting a card does not reset its failed PIN attempts.
- Cancelling a session cannot undo an operation that has already completed.
- Administrative methods may be called at any time because all calls are processed sequentially.
Constraints
1 ≤ cardNumber.length() ≤ 20
cardNumber contains only uppercase English letters and digits.
pin.length() == 4
pin contains only digits.
0 ≤ openingBalance ≤ 1,000,000
1 ≤ amount ≤ 1,000,000
denomination is one of 100, 200, 500, or 2,000.
1 ≤ count ≤ 1,000,000
0 ≤ notes.size() ≤ 1,000
- At most
100,000 method calls will be made.
Examples
Parameter names in the examples are shown only for readability and are not part of Java syntax.
Example 1: Deposit and Withdrawal
ATMSystem atm = new ATMSystem()
atm.addAccount(cardNumber = "CARD81", pin = "4826", openingBalance = 1600) -> true
atm.loadCash(denomination = 500, count = 1) -> true
atm.loadCash(denomination = 200, count = 3) -> true
atm.insertCard(cardNumber = "CARD81") -> true
atm.enterPin(pin = "4826") -> true
atm.enterPin(pin = "9999") -> false
atm.withdrawCash(amount = 600) -> [200, 200, 200]
atm.getBalance() -> 1000
atm.depositCash(notes = [500, 100]) -> true
atm.withdrawCash(amount = 600) -> [500, 100]
atm.getBalance() -> 1000
atm.ejectCard() -> true
The second PIN call returns false because the session is already authenticated. The first withdrawal uses three 200 notes because the available 500 note cannot form 600. After the deposit, the second withdrawal uses the minimum of two notes.
Example 2: Card Blocking
ATMSystem atm = new ATMSystem()
atm.addAccount(cardNumber = "CARD25", pin = "7314", openingBalance = 900) -> true
atm.insertCard(cardNumber = "CARD25") -> true
atm.enterPin(pin = "1111") -> false
atm.enterPin(pin = "2222") -> false
atm.enterPin(pin = "3333") -> false
atm.getBalance() -> -1
atm.insertCard(cardNumber = "CARD25") -> false
The third consecutive incorrect PIN blocks and ejects the card, so it cannot be inserted again.
Example 3: Failed Withdrawal
ATMSystem atm = new ATMSystem()
atm.addAccount(cardNumber = "CARD92", pin = "5081", openingBalance = 1500) -> true
atm.loadCash(denomination = 500, count = 1) -> true
atm.loadCash(denomination = 200, count = 1) -> true
atm.insertCard(cardNumber = "CARD92") -> true
atm.enterPin(pin = "5081") -> true
atm.withdrawCash(amount = 800) -> [-1]
atm.getBalance() -> 1500
Although the account has enough balance, the ATM contains only 700, so the withdrawal fails without changing the balance.
Example 4: Cancel a Session
ATMSystem atm = new ATMSystem()
atm.addAccount(cardNumber = "CARD47", pin = "6492", openingBalance = 2400) -> true
atm.loadCash(denomination = 500, count = 4) -> true
atm.insertCard(cardNumber = "CARD47") -> true
atm.enterPin(pin = "6492") -> true
atm.getBalance() -> 2400
atm.cancelSession() -> true
atm.getBalance() -> -1
atm.withdrawCash(amount = 500) -> [-1]
atm.ejectCard() -> false
Cancelling the session clears authentication and ejects the card. Therefore, later customer operations fail until a card is inserted and authenticated again.
Example 5: Deposit Exceeds the Balance Limit
ATMSystem atm = new ATMSystem()
atm.addAccount(cardNumber = "CARD63", pin = "2750", openingBalance = 999500) -> true
atm.insertCard(cardNumber = "CARD63") -> true
atm.enterPin(pin = "2750") -> true
atm.depositCash(notes = [500, 100]) -> false
atm.getBalance() -> 999500
The deposit would increase the balance to 1,000,100, which exceeds the maximum balance of 1,000,000. The deposit fails without changing the account balance or ATM cash.