Kelick Tech Hub

Build, learn & keep up — tech, Linux & sports news included.

C++ ATM Simulation Project

Beginner•Half a day•classes, state, validation

PIN login, balance checks, deposits, and withdrawals with safety limits.

Share on X

What you will build

Simulate an ATM: enter a PIN, check your balance, deposit and withdraw — with the machine refusing overdrafts and wrong PINs. It's a first lesson in stateful programs and defensive validation, where every rule exists because a real machine enforces it.

What you will learn

Feature checklist

Starter code

#include <iostream>

class Account {
    double balance_;
    int pin_;
public:
    Account(double start, int pin) : balance_(start), pin_(pin) {}
    bool checkPin(int p) const { return p == pin_; }
    double balance() const { return balance_; }
    bool withdraw(double amt) {
        if (amt <= 0 || amt > balance_) return false;
        balance_ -= amt; return true;
    }
    void deposit(double amt) { if (amt > 0) balance_ += amt; }
};

int main() {
    Account acct(500.0, 1234);
    int pin, tries = 0;
    while (tries < 3) {
        std::cout << "Enter PIN: "; std::cin >> pin;
        if (acct.checkPin(pin)) break;
        std::cout << "Wrong PIN.\n"; ++tries;
    }
    if (tries == 3) { std::cout << "Card retained.\n"; return 1; }

    int choice; double amt;
    do {
        std::cout << "\n1) Balance  2) Deposit  3) Withdraw  0) Exit\n> ";
        std::cin >> choice;
        if (choice == 1) std::cout << "Balance: $" << acct.balance() << "\n";
        if (choice == 2) { std::cin >> amt; acct.deposit(amt); }
        if (choice == 3) {
            std::cin >> amt;
            std::cout << (acct.withdraw(amt) ? "Take your cash.\n"
                                             : "Transaction refused.\n");
        }
    } while (choice != 0);
}

Full walkthrough

Step 1: Model the account as a class with private state

class Account holds the PIN and balance as private members, with deposit() and withdraw() as the only doors in. That privacy is the lesson: no code outside the class can set the balance directly, so every change is forced through functions that can check the rules. main() gets an Account and a menu, nothing more.

Step 2: Gate everything behind a PIN with three attempts

Loop up to 3 times reading a PIN and comparing it to the stored one. Success breaks into the menu; three failures print 'Card locked. Visit your bank.' and end the program. The attempts counter must live OUTSIDE the loop — declare it inside and it resets forever, giving unlimited guesses.

Step 3: deposit() validates before it adds

A deposit must be positive: if (amount <= 0) refuse with a message. Then balance += amount and print the new balance as the confirmation. Printing the resulting balance after every transaction is what makes a simulation feel like a machine that agrees with you.

Step 4: withdraw() checks two rules, in order

First the daily limit: track withdrawnToday and refuse if this withdrawal would push it past the limit (the starter uses 20000). Then sufficient funds: amount > balance is refused with 'Insufficient funds'. Order matters — check the limit before the balance and your messages stay truthful about WHICH rule fired.

bool withdraw(double amount) {
    if (amount <= 0) return false;
    if (withdrawnToday + amount > DAILY_LIMIT) return false;
    if (amount > balance) return false;
    balance -= amount;
    withdrawnToday += amount;
    return true;
}

Step 5: Build the menu loop

Once authenticated: 1) Balance 2) Deposit 3) Withdraw 0) Exit, looping until 0. Each branch reads an amount, calls the method, and reports success or the refusal reason. Keep ALL rule checks inside the methods — the menu should not re-implement them.

Step 6: Start the machine in a known state

Construct the demo account with PIN 1234 and balance 25000 directly in main(). A simulation needs a discoverable starting state so anyone running it can get in — print the demo PIN in your README or a comment, never in the program's own prompts.

Step 7: Attack your own machine

Test: three wrong PINs (must lock), a deposit of -500 (refused), a withdrawal of 999999 (insufficient funds), withdrawals of 15000 then 10000 in one session (the second trips the 20000 daily limit even though the balance covers it). If all five attacks fail safely, the simulation is honest.

Common mistakes

Compile and run

g++ -std=c++17 -o atm atm.cpp

Sample run

$ ./atm
Welcome. Enter your PIN:
> 1111
Wrong PIN. Attempts left: 2
Enter your PIN:
> 1234
Welcome back.
1) Balance  2) Deposit  3) Withdraw  0) Exit
> 3
Amount to withdraw:
> 5000
Please take your cash. New balance: 20000.00
1) Balance  2) Deposit  3) Withdraw  0) Exit
> 3
Amount to withdraw:
> 50000
Insufficient funds.
> 2
Amount to deposit:
> 2500
Deposited. New balance: 22500.00
> 0

Try it in your browser

No browser demo is included for this project. Build the C++ version below — that is the real program.

Related Beginner projects

← All 60 projects · Classic site (Learn & Sandbox)

Download Fake GPS