Build a Mental Math Trainer in C++

Build a timed arithmetic trainer in C++ modeled after the Zetamac tool.

Introduction

30 Second Summary

Quantitative trading firms like Jane Street test candidates with timed mental math screenings. 60 questions in 8 minutes, no calculator, no scratch paper.

In this project, you will build a terminal-based speed-arithmetic trainer in C++ that generates random problems, times each answer, and compares your pace against the Jane Street benchmark.

What You'll Build

A compiled C++ program modeled after Zetamac that generates random addition, subtraction, multiplication, and division problems with real-time per-question timing and a final performance summary.

By the end of this project, you'll have:

  • A compiled C++ program you built from scratch, understanding every line of code.
  • A working mental math trainer that generates 20 timed arithmetic questions with instant feedback on each answer.
  • A performance summary that benchmarks your pace against the Jane Street target of under 8 seconds per question.
  • Secret Mission: Add percentage questions and adaptive difficulty scaling that increases number ranges as you build a correct streak.

Are there any prerequisites?

This project assumes you have Homebrew installed on macOS. You do not need any prior C++ experience.

Set Up Your C++ Environment

Before you can build the math trainer, you need a working C++ toolchain on your machine. On macOS, Apple ships a C++ compiler called Clang as part of the Xcode Command Line Tools.

In this step, you will verify your compiler is installed, create a project folder, and write a minimal test program to confirm everything compiles and runs.

In this step, get ready to:
  • Verify your C++ compiler is installed.
  • Create a project directory and source file.
  • Compile and run a test program.
Verify your C++ compiler

The Xcode Command Line Tools package includes clang++, the compiler you will use to turn your .cpp source file into a runnable program. Let's check whether it is already on your machine.

  • Press Cmd+Space to open your search bar.
  • Type Terminal and press Enter to open it.
  • Check if the compiler is installed by running this command:
clang++ --version

Look at the output in your terminal and pick the tab that matches what you see.

✔️ I see a version number

You should see output starting with Apple clang version followed by a version number. This means your compiler is ready to go.

ⓧ Command not found

This means Xcode Command Line Tools are not installed yet.

  • Install Xcode Command Line Tools by running this command:
xcode-select --install
  • A dialog window will appear asking you to confirm the installation. Click Install and wait for it to finish (this may take a few minutes).
  • After installation completes, verify the compiler is now available by running:
clang++ --version

You should now see output starting with Apple clang version followed by a version number.

Create your project directory and source file

You need a dedicated folder to hold your trainer source code and the compiled executable.

  • Create the project folder and move into it by running these commands:
mkdir -p ~/projects/math-trainer && cd ~/projects/math-trainer

What does this command do?

mkdir -p creates the folder ~/projects/math-trainer along with any parent directories that do not exist yet. The && chains a second command that moves your terminal into that new folder.

  • Open this folder in VS Code by running:
code .

VS Code didn't open?

If you see command not found: code, you need to install the shell command from inside VS Code. Open VS Code manually (Cmd+Space, type Visual Studio Code, press Enter). Then press Cmd+Shift+P, type Shell Command: Install, and select the option to install the code command in PATH.

help me set up the VS Code shell command on macOS

  • In the VS Code Explorer sidebar on the left, right-click in the empty file area and select New File.
  • Name the file trainer.cpp and press Enter.

Your file is now open and ready for code.

Write and compile your first program

A quick Hello World program confirms that your compiler, editor, and project folder are all wired together correctly.

  • In trainer.cpp, paste the following code:
#include <iostream>

int main() {
    // Print the program title to confirm the build works
    std::cout << "Mental Math Trainer" << std::endl;
    return 0;
}

What does this code do?

  • #include <iostream> pulls in the standard input/output library so you can use std::cout to print text.
  • int main() is the entry point of every C++ program. The operating system calls this function when you run your executable.
  • std::cout << "Mental Math Trainer" << std::endl; sends the text to your terminal, followed by a newline.
  • return 0; tells the operating system the program finished successfully.
  • Save the file with Cmd+S.
  • Switch back to your Terminal. Make sure you are in the math-trainer directory (the same terminal session from earlier).
  • Compile the program by running this command:
clang++ -std=c++17 -o trainer trainer.cpp

What does this command do?

  • clang++ invokes the C++ compiler.
  • -std=c++17 tells the compiler to use the C++17 standard.
  • -o trainer names the output executable trainer (instead of the default a.out).
  • trainer.cpp is the source file to compile.
  • If the command returns with no output and no errors, your compilation succeeded.
  • Run the compiled program:
./trainer
  • You should see Mental Math Trainer printed in your terminal.

Seeing an error when compiling?

  • If you see no such file or directory, make sure your terminal is in the ~/projects/math-trainer folder. Run cd ~/projects/math-trainer to get there.
  • If you see a syntax error, double-check that your code in trainer.cpp matches the snippet above exactly. Watch for missing semicolons or mismatched angle brackets.

help me fix a clang++ compilation error for my trainer.cpp file

✔️ Awesome, I've got everything!

Great. Double check you have saved trainer.cpp and you are ready to move on.

ⓧ I'd like to double check the full code

Here is the complete trainer.cpp file at this point:

#include <iostream>

int main() {
    std::cout << "Mental Math Trainer" << std::endl;
    return 0;
}

Your environment is ready and your first C++ program is running. Next up, you will build the problem generator that creates random arithmetic questions.

Build the Problem Generator

Your compiler is working and your project folder is ready. Now it's time to build the core of your math trainer: random problem generation.

Every good math trainer needs unpredictability. If the problems were the same every time, you'd memorize answers instead of building speed. C++'s standard library includes a powerful random number engine that gives you a different problem on each run.

In this step, get ready to:
  • Set up random number generation using the C++ standard library.
  • Build a switch-based problem generator for four arithmetic operations.
  • Read user input and check answers against the correct result.
Set up random number generation

C++ provides the <random> header with industrial-strength random number generators. You'll use std::mt19937, the Mersenne Twister engine, which is the standard modern approach to generating random numbers in C++.

  • Replace all the content in trainer.cpp with this code:
#include <iostream>
#include <random>
#include <string>

int main() {
    // Seed a random number engine
    std::random_device rd;
    std::mt19937 gen(rd());

    // Distribution for picking which operation (0-3)
    std::uniform_int_distribution<int> op_dist(0, 3);
    // Small numbers for multiplication and division
    std::uniform_int_distribution<int> small_dist(2, 12);
    // Larger numbers for addition and subtraction
    std::uniform_int_distribution<int> large_dist(10, 99);

    return 0;
}

What does this code do?

std::random_device rd grabs a seed from your system's hardware randomness. This ensures a different sequence every time you run the program.

std::mt19937 gen(rd()) creates a Mersenne Twister engine seeded with that random value. Think of it as a number-shuffling machine that produces a new random number each time you call it.

The three distributions define the ranges you'll draw from. small_dist(2, 12) keeps multiplication manageable. large_dist(10, 99) makes addition and subtraction challenging without being tedious.

  • Save trainer.cpp.
  • Compile by running this command in your terminal:
clang++ -std=c++17 -o trainer trainer.cpp
  • You should see no errors or warnings. The file compiles cleanly, but running ./trainer produces no visible output yet.

Seeing a compile error?

  • Check that you have both angle brackets in #include <random> and #include <string>. Missing a bracket is a common typo.
  • Make sure your main() function has matching opening and closing braces.
  • Confirm you're running the compile command from inside the math-trainer folder.

Still stuck? Help me fix a compile error in my random number setup code.

Generate a single random problem

Now you'll use those distributions to generate an actual arithmetic problem. A switch statement will branch into four operation types: addition, subtraction, multiplication, and division.

  • In trainer.cpp, find the line that says return 0; and add the following code above it:
    // Pick a random operation and initialize variables
    int op = op_dist(gen);
    int a = 0;
    int b = 0;
    int answer = 0;
    std::string op_symbol;

    switch (op) {
        case 0:
            // Addition: two large numbers
            a = large_dist(gen);
            b = large_dist(gen);
            answer = a + b;
            op_symbol = "+";
            break;
        case 1:
            // Subtraction: swap so result is never negative
            a = large_dist(gen);
            b = large_dist(gen);
            if (a < b) {
                int temp = a;
                a = b;
                b = temp;
            }
            answer = a - b;
            op_symbol = "-";
            break;
        case 2:
            // Multiplication: two small numbers
            a = small_dist(gen);
            b = small_dist(gen);
            answer = a * b;
            op_symbol = "x";
            break;
        case 3:
            // Division: construct from divisor * quotient for clean answers
            b = small_dist(gen);
            answer = small_dist(gen);
            a = b * answer;
            op_symbol = "/";
            break;
    }

What does the switch statement do?

  • Case 0 (addition): Picks two numbers between 10-99 and sums them.
  • Case 1 (subtraction): Picks two large numbers but swaps them if needed so the result is never negative.
  • Case 2 (multiplication): Uses the smaller 2-12 range so products stay mentally doable.
  • Case 3 (division): Generates the divisor and quotient first, then multiplies them to create the dividend. This guarantees every division problem has a clean integer answer.
  • Save trainer.cpp and compile again:
clang++ -std=c++17 -o trainer trainer.cpp
  • You should see no errors. The program still produces no output when you run it because you haven't added the display logic yet.

Getting an undeclared identifier error?

  • Make sure #include <string> is at the top of your file. The std::string type lives in that header.
  • Check that your new code is placed above return 0; but below the distribution declarations.
  • Verify every case ends with break; and the switch block has matching braces.

Still stuck? Help me debug a compile error in my switch statement code.

Display the problem and check the answer

The final piece connects your problem generator to the user. You'll print the problem, read their answer, and tell them whether they got it right.

  • In trainer.cpp, find return 0; again and add this code above it (below the switch block):
    // Display the problem and wait for input
    std::cout << a << " " << op_symbol << " " << b << " = ";

    // Read the user's answer
    int user_answer;
    std::cin >> user_answer;

    // Check and display feedback
    if (user_answer == answer) {
        std::cout << "  Correct!" << std::endl;
    } else {
        std::cout << "  Wrong. Answer was " << answer << std::endl;
    }

What does this code do?

The std::cout line prints the problem in a readable format like 42 + 57 = and leaves the cursor waiting on the same line.

std::cin >> user_answer pauses the program until the user types a number and presses Enter. The if/else compares their input to the correct answer and prints the result.

  • Save trainer.cpp and compile:
  • Run your program:
clang++ -std=c++17 -o trainer trainer.cpp
./trainer
  • You should see a random arithmetic problem appear. Type your answer and press Enter. The program tells you whether you got it right or shows the correct answer.
  • Run ./trainer a few more times to confirm you get a different problem each run.

Program hangs or shows no output?

  • If you see nothing at all, make sure the std::cout line is placed after the switch block's closing brace, not inside a case.
  • If the cursor just blinks, that's correct. The program is waiting for you to type a number and press Enter.
  • If you see garbled output, check that op_symbol is declared as std::string (not char).

Still stuck? Help me figure out why my math trainer isn't displaying problems correctly.

✔️ Awesome, I've got everything!

Great. Double check you've saved trainer.cpp before moving on.

ⓧ I'd like to double check the full code

Here's the complete trainer.cpp at this point:

#include <iostream>
#include <random>
#include <string>

int main() {
    std::random_device rd;
    std::mt19937 gen(rd());

    std::uniform_int_distribution<int> op_dist(0, 3);
    std::uniform_int_distribution<int> small_dist(2, 12);
    std::uniform_int_distribution<int> large_dist(10, 99);

    int op = op_dist(gen);
    int a = 0;
    int b = 0;
    int answer = 0;
    std::string op_symbol;

    switch (op) {
        case 0:
            a = large_dist(gen);
            b = large_dist(gen);
            answer = a + b;
            op_symbol = "+";
            break;
        case 1:
            a = large_dist(gen);
            b = large_dist(gen);
            if (a < b) {
                int temp = a;
                a = b;
                b = temp;
            }
            answer = a - b;
            op_symbol = "-";
            break;
        case 2:
            a = small_dist(gen);
            b = small_dist(gen);
            answer = a * b;
            op_symbol = "x";
            break;
        case 3:
            b = small_dist(gen);
            answer = small_dist(gen);
            a = b * answer;
            op_symbol = "/";
            break;
    }

    std::cout << a << " " << op_symbol << " " << b << " = ";

    int user_answer;
    std::cin >> user_answer;

    if (user_answer == answer) {
        std::cout << "  Correct!" << std::endl;
    } else {
        std::cout << "  Wrong. Answer was " << answer << std::endl;
    }

    return 0;
}

Your problem generator is working. Next up, you'll wrap it in a game loop with a timer so you can race through 20 questions and track your speed.

Add Timing and a Game Loop

Your trainer generates a single random problem and checks the answer. That's a good foundation, but a real speed-training session needs volume.

In this step, you'll turn your single-question program into a 20-question timed drill. You'll measure how long each answer takes and track your score across the session.

In this step, get ready to:
  • Add a game intro with a "Press Enter to start" prompt.
  • Wrap problem generation in a 20-question for loop with a score counter.
  • Time each question and the full session using the chrono library.
Add the chrono header and game setup

The C++ standard library includes <chrono>, a header for precise time measurement. You'll use it to clock each answer and the full session.

  • In VS Code, select all the contents of trainer.cpp and replace them with this game setup code:
#include <iostream>
#include <random>
#include <chrono>
#include <string>

int main() {
    const int total_questions = 20;

    std::random_device rd;
    std::mt19937 gen(rd());

    std::uniform_int_distribution<int> op_dist(0, 3);
    std::uniform_int_distribution<int> small_dist(2, 12);
    std::uniform_int_distribution<int> large_dist(10, 99);

    int correct = 0;

    std::cout << "=== Mental Math Trainer ===" << std::endl;
    std::cout << "Answer " << total_questions << " questions as fast as you can!" << std::endl;
    std::cout << "Press Enter to start..." << std::endl;
    std::cin.get();

    auto session_start = std::chrono::steady_clock::now();

What does this code do?

  • #include <chrono> gives you access to high-resolution clocks and duration types for timing.
  • const int total_questions = 20 defines how many problems the session will run. Using a constant means you change it in one place.
  • int correct = 0 will track how many answers the user gets right across the session.
  • std::cin.get() waits for the user to press Enter before the timer starts. This prevents the clock from ticking while the user reads the welcome message.
  • std::chrono::steady_clock::now() captures the current time. steady_clock is monotonic, meaning it never jumps backwards due to system clock changes.

You won't be able to compile yet. The next section adds the game loop that completes the function.

Build the timed game loop

Now you need the core game logic: a loop that generates 20 problems, times each response, and records the session duration.

  • Add this code directly below the session_start line in trainer.cpp:
    for (int i = 1; i <= total_questions; i++) {
        int op = op_dist(gen);
        int a = 0;
        int b = 0;
        int answer = 0;
        std::string op_symbol;

        switch (op) {
            case 0:
                a = large_dist(gen);
                b = large_dist(gen);
                answer = a + b;
                op_symbol = "+";
                break;
            case 1:
                a = large_dist(gen);
                b = large_dist(gen);
                if (a < b) {
                    int temp = a;
                    a = b;
                    b = temp;
                }
                answer = a - b;
                op_symbol = "-";
                break;
            case 2:
                a = small_dist(gen);
                b = small_dist(gen);
                answer = a * b;
                op_symbol = "x";
                break;
            case 3:
                b = small_dist(gen);
                answer = small_dist(gen);
                a = b * answer;
                op_symbol = "/";
                break;
        }

        std::cout << "[" << i << "/" << total_questions << "] "
                  << a << " " << op_symbol << " " << b << " = ";

        auto q_start = std::chrono::steady_clock::now();

        int user_answer;
        std::cin >> user_answer;

        auto q_end = std::chrono::steady_clock::now();
        double q_seconds = std::chrono::duration<double>(q_end - q_start).count();

        if (user_answer == answer) {
            std::cout << "  Correct! (" << q_seconds << "s)" << std::endl;
            correct++;
        } else {
            std::cout << "  Wrong. Answer was " << answer
                      << " (" << q_seconds << "s)" << std::endl;
        }
    }

    auto session_end = std::chrono::steady_clock::now();
    double total_seconds = std::chrono::duration<double>(session_end - session_start).count();

    return 0;
}

What's happening here?

  • The for loop runs from 1 to total_questions. Each iteration generates a fresh problem with new random numbers.
  • The question counter [1/20] shows progress so the user knows how far they are into the session.
  • q_start captures the time right before std::cin reads input. q_end captures it immediately after. The difference is how long the user took to type their answer.
  • std::chrono::duration<double>(q_end - q_start).count() converts the clock difference into a double representing seconds.
  • correct++ increments the score only on right answers. After the loop, session_end captures the finish time and total_seconds stores the full session duration.
  • Save trainer.cpp.
  • Compile your updated trainer by running:
clang++ -std=c++17 -o trainer trainer.cpp
  • Run your trainer by running:
./trainer
  • Press Enter when prompted, then answer a few questions. You should see a counter like [1/20] before each problem and a time in seconds after each answer.

Seeing a compile error?

  • Check that #include <chrono> is listed at the top alongside the other headers.
  • Make sure the closing brace } of the for loop appears before session_end, and the closing brace of main() is the very last line.
  • Verify that return 0; is outside the for loop but inside main().

Help me fix a compile error in my C++ math trainer

✔️ Awesome, I've got everything!

Great. Double check you've saved your file before moving on.

ⓧ I'd like to double check the full code

#include <iostream>
#include <random>
#include <chrono>
#include <string>

int main() {
    const int total_questions = 20;

    std::random_device rd;
    std::mt19937 gen(rd());

    std::uniform_int_distribution<int> op_dist(0, 3);
    std::uniform_int_distribution<int> small_dist(2, 12);
    std::uniform_int_distribution<int> large_dist(10, 99);

    int correct = 0;

    std::cout << "=== Mental Math Trainer ===" << std::endl;
    std::cout << "Answer " << total_questions << " questions as fast as you can!" << std::endl;
    std::cout << "Press Enter to start..." << std::endl;
    std::cin.get();

    auto session_start = std::chrono::steady_clock::now();

    for (int i = 1; i <= total_questions; i++) {
        int op = op_dist(gen);
        int a = 0;
        int b = 0;
        int answer = 0;
        std::string op_symbol;

        switch (op) {
            case 0:
                a = large_dist(gen);
                b = large_dist(gen);
                answer = a + b;
                op_symbol = "+";
                break;
            case 1:
                a = large_dist(gen);
                b = large_dist(gen);
                if (a < b) {
                    int temp = a;
                    a = b;
                    b = temp;
                }
                answer = a - b;
                op_symbol = "-";
                break;
            case 2:
                a = small_dist(gen);
                b = small_dist(gen);
                answer = a * b;
                op_symbol = "x";
                break;
            case 3:
                b = small_dist(gen);
                answer = small_dist(gen);
                a = b * answer;
                op_symbol = "/";
                break;
        }

        std::cout << "[" << i << "/" << total_questions << "] "
                  << a << " " << op_symbol << " " << b << " = ";

        auto q_start = std::chrono::steady_clock::now();

        int user_answer;
        std::cin >> user_answer;

        auto q_end = std::chrono::steady_clock::now();
        double q_seconds = std::chrono::duration<double>(q_end - q_start).count();

        if (user_answer == answer) {
            std::cout << "  Correct! (" << q_seconds << "s)" << std::endl;
            correct++;
        } else {
            std::cout << "  Wrong. Answer was " << answer
                      << " (" << q_seconds << "s)" << std::endl;
        }
    }

    auto session_end = std::chrono::steady_clock::now();
    double total_seconds = std::chrono::duration<double>(session_end - session_start).count();

    return 0;
}

Your trainer now runs a full 20-question session with per-question timing and a score counter. Next up, you'll add a results summary that compares your pace against the Jane Street mental math benchmark.

Display Results and Jane Street Benchmark

Your trainer now fires 20 random questions and tracks how many you get right. But when the last question ends, the program just quits silently.

In this step, you'll print a results summary and compare your speed against the Jane Street mental math test benchmark. The target is under 8 seconds per question, derived from 60 questions in 8 minutes.

In this step, get ready to:
  • Display a results summary with score, accuracy, and timing stats.
  • Compare your pace against the Jane Street mental math benchmark.
Display the results summary

Right now, total_seconds is calculated after the loop but never shown to the user. You already have the correct counter and total_seconds variable ready to go.

  • In trainer.cpp, find the line that says double total_seconds = ... (just before return 0;).
  • Replace return 0; with the following results output, then add return 0; back at the very end:
    std::cout << std::endl;
    std::cout << "=== Results ===" << std::endl;
    std::cout << "Score: " << correct << "/" << total_questions << std::endl;
    std::cout << "Accuracy: " << (correct * 100 / total_questions) << "%" << std::endl;
    std::cout << "Total time: " << total_seconds << "s" << std::endl;
    std::cout << "Avg per question: " << (total_seconds / total_questions) << "s" << std::endl;
    std::cout << std::endl;

    return 0;

What does this code do?

  • std::endl at the top and bottom adds blank lines to visually separate the results from the questions.
  • correct * 100 / total_questions calculates accuracy as a percentage using integer arithmetic.
  • total_seconds / total_questions gives the average seconds spent per question across the whole session.
  • Save trainer.cpp.
  • Compile and run your program by running these commands in Terminal:
clang++ -std=c++17 -o trainer trainer.cpp
./trainer
  • Answer the 20 questions. After the last one, you should see a === Results === block showing your score, accuracy percentage, total time, and average time per question.

Not seeing the results summary?

  • Make sure the results code sits after the double total_seconds = ... line and before return 0;. If it's inside the for loop, it will print after every question instead of once at the end.
  • If you see a compile error, check that every << operator has a space on each side and that each line ends with << std::endl;.

Why isn't my results summary appearing after the questions?

Add the Jane Street pace benchmark

Jane Street's mental math screening gives candidates roughly 60 questions in 8 minutes. That works out to about 8 seconds per question.

You'll compare your average pace against this target so you can track your improvement over time.

  • In trainer.cpp, find the last std::cout << std::endl; line you just added (the blank line after the average per question output).
  • Add the following benchmark comparison between that line and return 0;:
    double target_pace = 8.0;
    double your_pace = total_seconds / total_questions;
    if (your_pace <= target_pace) {
        std::cout << "Jane Street pace: PASSING (under 8s/question)" << std::endl;
    } else {
        std::cout << "Jane Street target: <" << target_pace << "s per question" << std::endl;
        std::cout << "Your pace: " << your_pace << "s per question" << std::endl;
    }

What does this code do?

  • target_pace = 8.0 sets the benchmark at 8 seconds per question (480 seconds divided by 60 questions).
  • your_pace divides your total session time by the number of questions to get your actual average.
  • The if/else branch prints a passing message if you beat the target, or shows both the target and your pace if you need more practice.
  • Save trainer.cpp.
  • Compile and run the final version by running:
clang++ -std=c++17 -o trainer trainer.cpp
./trainer
  • Answer all 20 questions. After the results summary, you should see either Jane Street pace: PASSING (under 8s/question) or your pace compared against the 8-second target.

Seeing a compile error?

  • Check that the benchmark code sits outside the closing brace } of the for loop but inside main()'s closing brace.
  • Make sure you have matching opening and closing braces for the if and else blocks.

Help me fix my C++ compile error in the benchmark section.

✔️ Awesome, I've got everything!

Great. Double check you've saved trainer.cpp and that your program compiles without errors.

ⓧ I'd like to double check the full code

Here is the complete trainer.cpp file. Compare yours against it line by line.

#include <iostream>
#include <random>
#include <chrono>
#include <string>

int main() {
    const int total_questions = 20;

    std::random_device rd;
    std::mt19937 gen(rd());

    std::uniform_int_distribution<int> op_dist(0, 3);
    std::uniform_int_distribution<int> small_dist(2, 12);
    std::uniform_int_distribution<int> large_dist(10, 99);

    int correct = 0;

    std::cout << "=== Mental Math Trainer ===" << std::endl;
    std::cout << "Answer " << total_questions << " questions as fast as you can!" << std::endl;
    std::cout << "Press Enter to start..." << std::endl;
    std::cin.get();

    auto session_start = std::chrono::steady_clock::now();

    for (int i = 1; i <= total_questions; i++) {
        int op = op_dist(gen);
        int a = 0;
        int b = 0;
        int answer = 0;
        std::string op_symbol;

        switch (op) {
            case 0:
                a = large_dist(gen);
                b = large_dist(gen);
                answer = a + b;
                op_symbol = "+";
                break;
            case 1:
                a = large_dist(gen);
                b = large_dist(gen);
                if (a < b) {
                    int temp = a;
                    a = b;
                    b = temp;
                }
                answer = a - b;
                op_symbol = "-";
                break;
            case 2:
                a = small_dist(gen);
                b = small_dist(gen);
                answer = a * b;
                op_symbol = "x";
                break;
            case 3:
                b = small_dist(gen);
                answer = small_dist(gen);
                a = b * answer;
                op_symbol = "/";
                break;
        }

        std::cout << "[" << i << "/" << total_questions << "] "
                  << a << " " << op_symbol << " " << b << " = ";

        auto q_start = std::chrono::steady_clock::now();

        int user_answer;
        std::cin >> user_answer;

        auto q_end = std::chrono::steady_clock::now();
        double q_seconds = std::chrono::duration<double>(q_end - q_start).count();

        if (user_answer == answer) {
            std::cout << "  Correct! (" << q_seconds << "s)" << std::endl;
            correct++;
        } else {
            std::cout << "  Wrong. Answer was " << answer
                      << " (" << q_seconds << "s)" << std::endl;
        }
    }

    auto session_end = std::chrono::steady_clock::now();
    double total_seconds = std::chrono::duration<double>(session_end - session_start).count();

    std::cout << std::endl;
    std::cout << "=== Results ===" << std::endl;
    std::cout << "Score: " << correct << "/" << total_questions << std::endl;
    std::cout << "Accuracy: " << (correct * 100 / total_questions) << "%" << std::endl;
    std::cout << "Total time: " << total_seconds << "s" << std::endl;
    std::cout << "Avg per question: " << (total_seconds / total_questions) << "s" << std::endl;
    std::cout << std::endl;

    double target_pace = 8.0;
    double your_pace = total_seconds / total_questions;
    if (your_pace <= target_pace) {
        std::cout << "Jane Street pace: PASSING (under 8s/question)" << std::endl;
    } else {
        std::cout << "Jane Street target: <" << target_pace << "s per question" << std::endl;
        std::cout << "Your pace: " << your_pace << "s per question" << std::endl;
    }

    return 0;
}

Secret mission

Add Percentage Questions and Difficulty Scaling

Your trainer works, but it is static. Real interview prep adapts to your skill level. Can you add percentage questions and make the difficulty scale automatically based on your performance streak?

Clean Up Your Resources

Clean Up Your Resources

This project runs entirely locally, so there are no ongoing costs. Decide whether to keep your project files to continue practicing, or delete them entirely.

Resources you used:

  • Project folder ~/projects/math-trainer (contains trainer.cpp and the compiled trainer executable).

Keep everything running

No action needed. Choose this if you want to keep practicing your mental math daily or continue tweaking the trainer.

  • Your project files stay in ~/projects/math-trainer. Run ./trainer any time to practice.
  • Xcode Command Line Tools remain installed on your system. These are useful for any future C++ or development work.

Pause - I'll come back to this later

Since this is a local project with no running processes, there is nothing to pause. Your files will stay on disk until you decide to delete them.

  • Close the math-trainer folder in VS Code if you have it open.
  • Your source code and compiled executable will be waiting in ~/projects/math-trainer when you return.

Delete - I don't want to use this again

Remove all project files to start fresh.

  • Delete the project folder by running this command in Terminal:
rm -rf ~/projects/math-trainer
  • If the ~/projects folder is now empty and you no longer need it, remove it too:
rmdir ~/projects
  • This removes your source file, compiled executable, and the project directory. Xcode Command Line Tools remain installed since they are useful for other development work.

That's a Wrap!

That's a Wrap!

Nice work! You just built a complete mental math trainer in C++ from a blank file to a compiled, timed, benchmarked training tool.

You've learned how to:

  • Build a C++ program from scratch: writing a source file, compiling with clang++, and running the executable from the terminal.
  • Use the C++ standard library for random number generation with mt19937 and precise elapsed-time measurement with chrono::steady_clock.
  • Create a daily-use speed arithmetic tool modeled after the Zetamac trainer used by Jane Street candidates, with a pace benchmark comparison.
  • Secret Mission: Added percentage questions and adaptive difficulty scaling that increases number ranges as you build a correct streak.

Ready to quiz yourself?