Kelick Tech Hub

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

C++ Chess Clock Project

Intermediate•1 day•<chrono>, state machine, timing

Two countdown clocks, one button — flag fall decides the game.

Share on X

What you will build

Each player has a countdown; making a move stops your clock and starts your opponent's, and running out of time loses the game. It's a study in precise real-time state: whose clock is running, exactly how much remains, and what happens at zero.

What you will learn

Feature checklist

Starter code

#include <iostream>
#include <chrono>

using Clock = std::chrono::steady_clock;
using Ms = std::chrono::milliseconds;

struct ChessClock {
    Ms left[2];
    int turn = 0;                    // 0 = White, 1 = Black
    Ms increment{0};
    Clock::time_point stamp;
    bool over = false;

    ChessClock(Ms start, Ms inc) : increment(inc) {
        left[0] = left[1] = start;
        stamp = Clock::now();
    }
    // Charge the running player for time since last press.
    void tick() {
        auto now = Clock::now();
        left[turn] -= std::chrono::duration_cast<Ms>(now - stamp);
        stamp = now;
        if (left[turn] <= Ms{0}) { left[turn] = Ms{0}; over = true; }
    }
    void press() {                   // current player ends their move
        tick();
        if (over) return;
        left[turn] += increment;
        turn = 1 - turn;
        stamp = Clock::now();
    }
};

Full walkthrough

Step 1: Model the clock as a small class

class ChessClock holds: two remaining times (as chrono durations or milliseconds), whose turn is running, a move counter per player, an optional increment, and the start timestamp of the current turn. The key insight: only ONE clock runs at a time, and it switches on press — the class exists to make 'the running side loses time' impossible to get wrong.

Step 2: tick(): charge time to the side to move

tick() computes now - turnStart, subtracts it from the running player's remaining time, and resets turnStart to now. Called a few times a second by the main loop, it keeps the display fresh without ever storing a 'current displayed time' that could drift — derived state once more.

Step 3: Detect flag fall the moment it happens

If the running player's remaining time hits zero inside tick(), the game is over: that player loses on time, the other is declared the winner. Check on EVERY tick, not on press — a player whose flag falls while thinking loses even if nobody touches the clock. Freeze both clocks at that moment.

Step 4: press(): switch sides, add increment, count the move

On a press (Enter key in the console version): settle the presser's time via tick(), add the increment to the PRESSER's remaining time if configured, increment their move count, flip the running side, and reset turnStart. Order is crucial — settle first, then bonus, then switch; adding increment to the wrong player is the classic implementation slip.

Step 5: Choose the time control at startup

Prompt for base minutes (5, 10, 15 or custom) and increment seconds (0 for none). Both clocks start equal. Starting White's clock immediately on game start matches real tournament behaviour — White's time runs while they think about move one.

Step 6: Draw a clear two-clock display

Each redraw shows both remaining times as MM:SS (tenths under a minute, if you want the drama), a marker on the side whose clock is running, and the move counts. Redraw in place with \r or a clear-screen code like the clock and snake projects — the information is the state, formatted.

Step 7: Test with a blitz game

Set 1 minute per side for testing. Press alternately and watch only the mover's clock fall; sit on one side for the full minute and confirm the flag falls, the winner is announced, and further presses do nothing. Then a game with a 2-second increment: a press must visibly refund time to the player who just moved.

Common mistakes

Compile and run

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

Sample run

$ ./chess_clock
Time control — minutes per side:
> 5
Increment seconds per move (0 = none):
> 2

White 5:00  ·  Black 5:00  — White to move, White's clock runs
(White thinks for 8 seconds, then presses — Enter)
> 
White 4:54 (+2s increment)  ·  Black 5:00 — Black to move · moves: W1 B0
(Black replies quickly, presses)
> 
White 4:54  ·  Black 4:57 (+2s increment) — White to move · moves: W1 B1
(...the game continues, clocks only ever fall for the side to move...)
White 0:41  ·  Black 1:12 — White to move · moves: W38 B38
(White's time runs out while thinking)
FLAG FALL — White's clock hit 0:00. Black wins on time!

Try it in your browser

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

Related Intermediate projects

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

Download Fake GPS