C++ Rock Paper Scissors Game Project
Face the computer in the classic hand game, with a running score.
What you will build
Pick rock, paper, or scissors; the computer picks its own; the winner takes the round. Tiny as it is, this project teaches enum types, clean win-condition logic, and how to make a game feel alive with a scoreboard and rematch loop.
What you will learn
- Modelling choices with enum class
- Random opponent moves
- Encoding win/lose rules without spaghetti ifs
- Keeping score across rounds
Feature checklist
- Player picks R, P, or S
- Computer picks randomly each round
- Winner announced per round (with the reason: 'Rock crushes Scissors')
- Running score for player, computer, and draws
- Play until the player quits
Starter code
#include <iostream>
#include <random>
enum class Move { Rock, Paper, Scissors };
// returns 1 if a beats b, -1 if b beats a, 0 for draw
int outcome(Move a, Move b) {
if (a == b) return 0;
bool aWins = (a == Move::Rock && b == Move::Scissors) ||
(a == Move::Paper && b == Move::Rock) ||
(a == Move::Scissors && b == Move::Paper);
return aWins ? 1 : -1;
}
int main() {
std::mt19937 rng(std::random_device{}());
std::uniform_int_distribution<int> dist(0, 2);
char c;
std::cout << "Pick (R)ock, (P)aper, (S)cissors, (Q)uit: ";
while (std::cin >> c && c != 'Q' && c != 'q') {
Move you = (c == 'R' || c == 'r') ? Move::Rock
: (c == 'P' || c == 'p') ? Move::Paper : Move::Scissors;
Move cpu = static_cast<Move>(dist(rng));
int r = outcome(you, cpu);
std::cout << (r > 0 ? "You win!\n" : r < 0 ? "CPU wins!\n" : "Draw!\n");
std::cout << "Pick again: ";
}
}Full walkthrough
Step 1: Model the moves with an enum class
enum class Move { Rock, Paper, Scissors } turns three magic numbers into named values the compiler can type-check — you cannot accidentally compare a Move to 7. The starter converts the player's char into a Move with a ternary chain, and draws the computer's move by casting a random 0–2 to Move.
Step 2: Write outcome() as a pure rules function
outcome(Move a, Move b) returns 1 if a wins, -1 if b wins, 0 for a draw. The trick is one boolean, aWins, with three OR'd conditions: Rock crushes Scissors, Paper covers Rock, Scissors cut Paper. Every other unequal pairing is a loss, so there is no fourth case to write — and no way for the rules to contradict themselves.
int outcome(Move a, Move b) {
if (a == b) return 0;
bool aWins = (a == Move::Rock && b == Move::Scissors) ||
(a == Move::Paper && b == Move::Rock) ||
(a == Move::Scissors && b == Move::Paper);
return aWins ? 1 : -1;
}Step 3: Set up the random opponent
Use std::mt19937 seeded from std::random_device and std::uniform_int_distribution<int> dist(0, 2), exactly like the guessing game. Seed ONCE before the loop: reseeding every round from a fast clock can repeat the same 'random' move several rounds in a row.
Step 4: Build the round loop
main() prints the prompt 'Pick (R)ock, (P)aper, (S)cissors, (Q)uit: ' and loops while (std::cin >> c && c != 'Q' && c != 'q'). Each round: convert c to your Move, draw the CPU's Move, call outcome(), and print the verdict. The loop condition handles quitting, so the body never checks for Q.
Step 5: Announce the moves and the reason, not just the winner
'You win!' alone feels empty; the checklist wants the story: 'Rock crushes Scissors'. Print both moves by name (a small nameOf(Move) helper returning strings) and pick the reason phrase from the winning pairing. This is presentation code, and it is what separates a game people replay from a logic exercise.
Step 6: Keep score across rounds
Three counters — you, cpu, draws — incremented from outcome()'s return value, printed after each round and once more as a final tally when the player quits. Score is just state carried across loop iterations; declaring the counters INSIDE the loop would reset them every round, the classic bug here.
Step 7: Test all nine pairings
There are only 3 × 3 = 9 combinations: force each one (temporarily rig the CPU move if needed) and verify outcome() against the real rules, especially the three draws. Nine cases is small enough to test exhaustively — a luxury most programs do not give you, so take it.
Common mistakes
- Declaring the score counters inside the game loop, so the score resets to 0-0-0 after every round.
- Writing the win rules as nested ifs per move and flipping one pairing (Paper vs Scissors is the usual casualty).
- Reseeding the random engine inside the loop, making the computer throw the same move repeatedly.
Compile and run
g++ -std=c++17 -o rps rps.cppSample run
$ ./rps
Pick (R)ock, (P)aper, (S)cissors, (Q)uit:
> R
You: Rock | CPU: Scissors — Rock crushes Scissors. You win!
Score: you 1 · cpu 0 · draws 0
Pick again:
> P
You: Paper | CPU: Scissors — Scissors cut Paper. CPU wins!
Score: you 1 · cpu 1 · draws 0
Pick again:
> S
You: Scissors | CPU: Scissors — same move. Draw!
Score: you 1 · cpu 1 · draws 1
Pick again:
> R
You: Rock | CPU: Paper — Paper covers Rock. CPU wins!
Score: you 1 · cpu 2 · draws 1
Pick again:
> Q
Final score: you 1 · cpu 2 · draws 1. Thanks for playing!Try it in your browser
This project has a browser demo in the classic site. The demo is a browser re-implementation of the same logic, not the compiled C++ program: Rock crushes scissors, scissors cut paper, paper covers rock. First to bragging rights.