A webcam-based Rock-Paper-Scissors game built with OpenCV and MediaPipe Hands.
Features:
- Single-player vs. adaptive frequency-based AI
- Two-player mode (two hands side by side)
- Rule-based gesture classifier from 21 MediaPipe hand landmarks (no ML training required)
- 3-2-1 countdown with gesture stability enforcement (~1 s hold before lock)
- Live HUD: FPS, gesture confidence, stability signal, round state, score, AI stats
- CSV logging for offline evaluation
- Gesture classifier accuracy + confusion matrix tool
cd SmartRPS_py314
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install -r requirements.txtPython 3.14 note: On first run the app auto-downloads
models/hand_landmarker.taskfrom the official MediaPipe model storage (~8 MB). Requires an internet connection once.
python main.py| Key | Action |
|---|---|
q |
Quit |
m |
Toggle single-player ↔ two-player mode |
r |
Reset score and AI history |
Space |
Force-play current gesture immediately |
-
Show one hand clearly to the camera:
- Rock — closed fist
- Paper — all five fingers extended
- Scissors — index + middle finger extended, rest closed
-
Hold the gesture steady. The HUD shows Stable / Unstable.
-
Once stable, a 3-2-1 countdown begins on screen.
-
At 0 the gesture locks and the round is played automatically.
-
The result is shown for ~1.5 seconds, then the next round begins.
Two-player mode: place two hands in front of the camera — left hand = P1, right hand = P2.
python collect_samples.py --label rock --count 100
python collect_samples.py --label paper --count 100
python collect_samples.py --label scissors --count 100Press s to save a frame, q to quit early.
Samples are appended to data/samples.csv.
python evaluate_logs.pyPrints:
- Game log analysis — outcome distribution, move frequency, AI win rate
- Gesture classifier accuracy — per-class accuracy, confusion matrix, precision
(requires
data/samples.csvfrom the collection step)
Override paths:
python evaluate_logs.py --log data/game_log.csv --samples data/samples.csv| Constant | Default | Effect |
|---|---|---|
STABLE_FRAMES |
8 | Frames buffer required for a stable gesture |
COUNTDOWN_SECONDS |
3.0 | Countdown duration (mapped to 3-2-1 display) |
MIN_GESTURE_CONFIDENCE |
0.70 | Confidence threshold to accept a gesture |
RESULT_DISPLAY_SECONDS |
1.5 | How long the result screen is shown |
AI_HISTORY_SIZE |
20 | Window of recent player moves the AI tracks |
AI_RANDOM_ROUNDS |
5 | Warm-up rounds before AI uses frequency data |
SmartRPS_py314/
├── main.py # game loop & round state machine
├── collect_samples.py # interactive sample collection
├── evaluate_logs.py # accuracy + confusion matrix
├── requirements.txt
├── data/
│ ├── game_log.csv # auto-created on first round
│ └── samples.csv # created by collect_samples.py
├── models/
│ └── hand_landmarker.task # auto-downloaded on first run
└── smartrps/
├── config.py # all tunable constants
├── gesture.py # rule-based landmark classifier
├── hand_tracking.py # MediaPipe compat wrapper (legacy + Tasks API)
├── ai.py # frequency-based predictor
├── game.py # score tracking + CSV logging
└── dashboard.py # OpenCV HUD
The AI is a frequency-based predictor, not a Markov chain. It counts how often
the player has played each move over the last AI_HISTORY_SIZE rounds and counters
the most frequent one. After AI_RANDOM_ROUNDS rounds of random warm-up, it exploits
patterns with 85 % probability and plays randomly 15 % of the time to stay unpredictable.
- Single-hand assumption in single-player mode — only the leftmost detected hand is used; if two hands appear, P1 is the left one.
- Thumb ambiguity — some users' thumbs may trigger false rock/paper conflicts depending
on hand size and camera angle. Adjust
MIN_GESTURE_CONFIDENCEif needed. - Lighting sensitivity — low or strongly directional lighting degrades MediaPipe detection. Use a bright, even light source facing the hand.
- Classifier is rule-based — it is intentionally simple and explainable for a CV course project; it does not use a trained model.