Looking over Rock, Paper, Scissors. C++ from a beginning programmer prompted me to think about how I'd program this simple game in C++. The RockPaperScissors class contains the essential features of printing and evaluating each round of the game, as well as generation of randomly selected moves. I didn't encode a player input because that didn't seem too interesting; instead the sample program just plays 20 rounds against itself and prints the result of each. No statistics are gathered or stored.

One feature that does exist is that there is a #define to select whether the classic game or the enhanced "Rock-Paper-Scissors-Lizard-Spock" version is used. (With a bit more work, which I chose not to invest, one could allow for a fairly generic \$n\$-ary relation as the basis of the game.) The other feature worth noting is that the operator< is deliberately private because the \$<\$ operation implemented here does not represent a partially ordered set because the operation is not transitive. In simple terms, we would normally expect that \$(a < b) \land (b < c) \implies (a < c)\$ but this is expressly not true here.

I'd be interested in comments on those or any other design decisions or implementation details.

#include <iostream>
#include <string_view>
#include <array>
#include <stdexcept>
#include <random>

// define ENHANCED to non-zero for enhanced version with Spock, Lizard
#ifndef ENHANCED
#define ENHANCED 0

class RockPaperScissors 
    RockPaperScissors(std::size_t num) {
        if (num >= words.size()) {
            throw std::range_error("invalid choice");
        choice = num;
    static RockPaperScissors random();
    const std::string_view& vs(const RockPaperScissors &b) const;
    friend std::ostream& operator<<(std::ostream& out, const RockPaperScissors &rps);
    bool operator==(const RockPaperScissors &b) const;
    bool operator<(const RockPaperScissors &b) const;
    static constexpr std::array<std::string_view, 5> words{ "Rock", "Paper", "Scissors", "Spock", "Lizard", };
    static constexpr std::array<std::string_view, 3> words{ "Rock", "Paper", "Scissors" };
    static constexpr std::array<std::string_view, 3> results{"LOSE", "TIE", "WIN" };
    std::size_t choice;

std::ostream& operator<<(std::ostream& out, const RockPaperScissors &rps) {
    return out << rps.words[rps.choice];

RockPaperScissors RockPaperScissors::random() {
    static std::random_device rd;
    static std::mt19937 gen{rd()};
    static std::uniform_int_distribution<> dis(0, RockPaperScissors::words.size()-1);
    std::size_t which{static_cast<std::size_t>(dis(gen))};
    return RockPaperScissors{which};

bool RockPaperScissors::operator==(const RockPaperScissors &b) const {
    return choice == b.choice;

bool RockPaperScissors::operator<(const RockPaperScissors &b) const {
    return (choice + 1) % words.size() == b.choice || (choice + 3) % words.size() == b.choice;

const std::string_view& RockPaperScissors::vs(const RockPaperScissors &b) const {
    if (*this == b) {
        return results[1];
    } else if (*this < b) {
        return results[0];
    return results[2];

int main()
    for (int trials = 20; trials; --trials) {
        auto a = RockPaperScissors::random();
        auto b = RockPaperScissors::random();
        std::cout << a << " vs. " << b << ": " << a << " " << a.vs(b) << "S!\n";
  • \$\begingroup\$ the-big-bang-theory.com/rock-paper-scissors-lizard-spock \$\endgroup\$ – Martin York Feb 19 at 23:10
  • \$\begingroup\$ int trials... unsigned int trials maybe? \$\endgroup\$ – Dair Feb 20 at 0:14
  • \$\begingroup\$ Were you trying to golf your for loop a bit or why the somewhat unusal design? \$\endgroup\$ – yuri Mar 12 at 17:30
  • 1
    \$\begingroup\$ I see nothing unusual about the for loop. \$\endgroup\$ – Edward Mar 12 at 17:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.