SDE Path

ATM State Machine

Medium

ATM State Machine

Insert card, enter PIN, withdraw, deposit — the ATM is the canonical finite-state machine, made judge-able.

The state machine

The ATM moves through three states; each command's effect depends on the current state.

| State | Meaning | |---|---| | NO_CARD | No card inserted (the initial state). | | CARD | A card is inserted but the PIN is not yet verified. | | AUTH | The inserted card's PIN is verified — the transactional state. |

Rules

  • createAccount registers an account in the bank in any state and never changes the current session.
  • insertCard works only from NO_CARD: it succeeds (ok, → CARD) when the card exists, otherwise returns error and stays put. A second insertCard while a card is already in is an error.
  • enterPin compares against the inserted card's PIN. A match authenticates the session (→ AUTH) and returns true; a mismatch returns false and stays in CARD. Called with no card in, it returns false.
  • withdraw, deposit, and a real balance require AUTH. Without it, withdraw returns error, deposit is a no-op, and balance returns -1.
  • withdraw amount dispenses only when amount ≤ balance (returns the amount as a string and debits it); otherwise insufficient with no debit.
  • ejectCard always returns to NO_CARD and clears authentication.
  • Balances never go negative, so -1 from balance unambiguously means "no authenticated session".

API to implement

Implement the class ATM. The I/O driver is already written in the starter code — it parses the command script below and calls your methods. You only implement the class.

| Command | Returns | Behavior | |---|---|---| | createAccount <cardId> <pin> <balance> | — | Register a bank card cardId with pin and starting balance. Overwrites any existing account for that card and does not touch the current session. | | insertCard <cardId> | string | Insert card cardId. Returns ok when no card is currently inserted and the card exists (session moves to CARD); otherwise error and nothing changes. | | enterPin <pin> | boolean | Verify the inserted card's PIN. Returns true and authenticates the session when pin matches; a mismatch returns false (retries allowed). Returns false if no card is inserted. | | withdraw <amount> | string | Withdraw from the authenticated card. Returns the dispensed amount as a string on success (and debits it), insufficient when the balance is too low (no debit), or error with no authenticated session. | | deposit <amount> | — | Add amount to the authenticated card's balance. Does nothing without an authenticated session. | | balance | int | Return the authenticated card's current balance, or -1 when there is no authenticated session. | | ejectCard | — | Eject any inserted card and end the session, returning the machine to the no-card state. |

Input format

The first line contains Q, the number of operations. Each of the next Q lines is one command as listed above. String arguments contain no spaces.

Output format

For each command with a non-void return, print its result on its own line, in order. The driver does this for you — your methods just return values.

This is a design problem: the hidden tests exercise edge cases and interaction sequences, not performance tricks. Model the state cleanly and the tests will pass.

Sample cases

Example 1
Input
12
createAccount 1 1234 500
insertCard 1
enterPin 9999
enterPin 1234
balance
withdraw 200
balance
withdraw 1000
deposit 100
balance
ejectCard
balance
Expected output
ok
false
true
500
200
300
insufficient
400
-1
Example 2
Input
10
createAccount 7 4321 1000
withdraw 50
insertCard 9
insertCard 7
insertCard 7
withdraw 50
enterPin 4321
withdraw 400
balance
ejectCard
Expected output
error
error
ok
error
error
true
400
600