C++ OOP - Brainstorming what class and what we need

Hey guys, I am practicing and learning. I tried to make a simple game which was Rock, Paper, Scissors which I did okay and completed.

But now I want to do the same game in Object Oriented which is the type of style I am aiming for. I have a few questions.


1) When coding in OOP. How do you brainstorm or visualize what classes we need, what interface etc? Can anyone share some techniques on how to think of the things we need?


2) What classes would I need for a simple Rock, Paper, Scissors game?



Thanks.
1) find requirements. What exact problem does your new program solve? What does it need to do this? It needs some inputs, what are they? It needs some outputs, what are they? Some sort of interface, and processing in the middle. What do those look like?

for RPS game, ... you probably want to have a menu that lets the person play again or quit. You may want to keep score, how many games, how many won by each, draws, etc. You need input of the player's symbol. You need some sort of random selection by the 'AI'. You need some sort of way to process the AI vs Human and determine a winner.

Which of those are classes? Which are methods? What relationships do objects have?

Basicallly, 1) comes down to asking questions until you can sketch it out and answer them all (even crude answers)... RPS class has a random method using <random> or a random class that wraps <random>'s details up for you and it may have a 'find winner method' and so on. May have a score/game class that plays and keeps score. Maybe set that up such that RPS is one game of many so if you add tic-tac-toe later the user can select which game, play it, etc... pretend that were a requirement, what do you need now (this is planning for future growth).

2) when you do 1), you will know. I gave some suggestions or hints, but DO #1 yourself and see what you get when you answer the questions and sketch out the design.

Like @jonnin said, find the requirements. Write down a description of everything you want the program to do.

Read through your description and find all of the nouns and verbs. The nouns are good candidates for classes in your design, and verbs are good candidates for methods. It won't be a perfect correlation, but it's a place to start.

So, you might write "There will be a menu to select the type of opponent." Menu might be a good class to consider, and one of its methods might be selectOpponentType().

Also, class to represent the competitors (both human and computer) will be nice.
1) When coding in OOP. How do you brainstorm or visualize what classes we need, what interface etc? Can anyone share some techniques on how to think of the things we need?
I follow a simple approach: All 'physical' objects are getting their classes.
So if you want to deal with a printer create a class printer.
If you want a chess game create a class game, chessboard, chesspiece.
Etc.

2) What classes would I need for a simple Rock, Paper, Scissors game?
So that OOP makes sense you need a certain complexity. Such a simple game is probably easier solved without.
-- don't make class rock, class paper, and class scissors by taking the noun/physical object advice too literally. youll see this if you do 1 as I said, asking questions on what you *need* to solve the problem. That would be breaking it down too fine and making unnecessary pieces, causing code bloat and a confusing overthunk design. It might come out in the brainstorm/think phase, but should be eliminated when you start putting it all together, when you realized that R/P/S are just instances of a higher level class, not their own thing. If rock and paper had different methods and attributes that you needed to implement and describe, they would get their own class, but your requirements will show that you don't have anything that radically differentiates them, so they can be the same object type with different values instead. You will see this with practice. Unfortunately, the best way to really get why things are done the way they are for design is to make something with a bad design and get about 2/3 of the way into it and realize you have a huge overly complicated mess.
Last edited on
I wanted to see if I can attempt what you were trying to achieve, I know this can be improved much more..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
#include <iostream>
#include <string>
#include <random>
//io_rico @ yahoo 
//rock paper scissors oop cli
class RockPaperScissors{
	const std::string RPS[3] = {"Rock","Paper","Scissors"};
	
	std::mt19937 rng;

	int userEntry, userPoints, botPoints;

  bool isLooping;

public:
  RockPaperScissors();
  
  int initGame();
};

RockPaperScissors::RockPaperScissors(){
	userEntry = 0;
	isLooping = true;
  userPoints = 0;
  botPoints = 0;
}

int RockPaperScissors::initGame(){

  rng.seed(std::random_device()());
  std::uniform_int_distribution<std::mt19937::result_type> shoot(0,2);

	std::cout<<"WELCOME TO ([T H E  R P S  G A M E])\n"<<std::endl;
	std::cout<<"Entry Options: 1 [Rock] | 2 [Paper] | 3 [Scissors] | ANY OTHER KEY to Quit"<<std::endl;
	
	std::cin>>userEntry;

  if(userEntry == 1){

    std::cout<<"You chose: Rock"<<std::endl;
    std::string botChose = RPS[shoot(rng)];
    std::cout<<"Bot chose: "<<botChose<<std::endl;
    //0Rock, 1Paper, 2Scissors

    if(botChose == RPS[0]){
      std::cout<<"Even"<<std::endl;
    }
     
    if(botChose == RPS[1]){
      std::cout<<"You Lost, Bot Won!"<<std::endl;
      ++botPoints;
    }

    if(botChose == RPS[2]){
      std::cout<<"You Won!, Bot lost"<<std::endl;
      ++userPoints;
    }

  }else if(userEntry == 2){

    std::cout<<"You chose: Paper"<<std::endl;
    std::string botChose = RPS[shoot(rng)];
    std::cout<<"Bot chose: "<<botChose<<std::endl;
    //0Rock, 1Paper, 2Scissors

    if(botChose == RPS[0]){
      std::cout<<"You Won!, Bot Lost"<<std::endl;
      ++userPoints;
    }
     
    if(botChose == RPS[1]){
      std::cout<<"Even"<<std::endl;
    }

    if(botChose == RPS[2]){
      std::cout<<"You Lost, Bot Won!"<<std::endl;
      ++botPoints;
    }

  }else if(userEntry == 3){

    std::cout<<"You chose: Scissors"<<std::endl;
    std::string botChose = RPS[shoot(rng)];
    std::cout<<"Bot chose: "<<botChose<<std::endl;
    //0Rock, 1Paper, 2Scissors

    if(botChose == RPS[0]){
      std::cout<<"You Lost, Bot Won!"<<std::endl;
      ++botPoints;
    }
     
    if(botChose == RPS[1]){
      std::cout<<"You Won!, Bot Lost"<<std::endl;
      ++userPoints;
    }

    if(botChose == RPS[2]){
      std::cout<<"Even"<<std::endl;
    }

  }else{ 
    std::cout<<" \tUser Score: #"<<userPoints<<std::endl;
    std::cout<<"  \tBot Score: #"<<botPoints<<std::endl;
    return 0; 
  }

	while(!isLooping == false){

    std::cin>>userEntry;

    if(userEntry == 1){

    	std::cout<<"You chose: Rock"<<std::endl;
      std::string botChose = RPS[shoot(rng)];
      std::cout<<"Bot chose: "<<botChose<<std::endl;

      if(botChose == RPS[0]){
        std::cout<<"Even"<<std::endl;
      }
     
      if(botChose == RPS[1]){
        std::cout<<"You Lost, Bot Won!"<<std::endl;
        ++botPoints;
      }

      if(botChose == RPS[2]){
        std::cout<<"You Won!, Bot lost"<<std::endl;
        ++userPoints;
      }

    }else if(userEntry == 2){

    	std::cout<<"You chose: Paper"<<std::endl;
      std::string botChose = RPS[shoot(rng)];
      std::cout<<"Bot chose: "<<botChose<<std::endl;

      if(botChose == RPS[0]){
        std::cout<<"You Won!, Bot Lost"<<std::endl;
        ++userPoints;
      }
     
      if(botChose == RPS[1]){
        std::cout<<"Even"<<std::endl;
      }

      if(botChose == RPS[2]){
        std::cout<<"You Lost, Bot Won!"<<std::endl;
        ++botPoints;
      }

    }else if(userEntry == 3){

    	std::cout<<"You chose: Scissors"<<std::endl;
      std::string botChose = RPS[shoot(rng)];
      std::cout<<"Bot chose: "<<botChose<<std::endl;

      if(botChose == RPS[0]){
        std::cout<<"You Lost, Bot Won!"<<std::endl;
        ++botPoints;
      }
     
      if(botChose == RPS[1]){
        std::cout<<"You Won!, Bot Lost"<<std::endl;
        ++userPoints;
      }

      if(botChose == RPS[2]){
        std::cout<<"Even"<<std::endl;
      }

    }else{
      std::cout<<" User Score: #"<<userPoints<<std::endl;
      std::cout<<"  Bot Score: #"<<botPoints<<std::endl;
      isLooping = false; 
      return 0; 
    }

	}
  return 0;
}

int main(){

  RockPaperScissors rps;
  rps.initGame();

	return 0;
}
Here is me trying to brainstorm.

What I want my game to do:

- Menu will be displayed
- User will select option for Rock, Paper, Scissors
- Then computer will return their selection
- Whoever wins will get +1 points
- Game will keep looping until 10 points is reached, winner is whoever with 10 points


Now to make classes in OOP manner:

- Class Game which will have menu selection, choose winner, human points, computer points.




Or

would it be better to make classes like below? If so could someone please help on what I would put in these classes?

- Class Game
- Class Human
- Class Computer
Last edited on
you can do it that way.
I would make the game class contain a RSP game subclass that JUST does the RSP game, and the outer game class manages the menu, score, and other stuff.

I see no need for human/computer. what will those DO? Are they necessary? For this game, I don't see it. If you have multiple games, perhaps...

so... doodling, something like:
class game
{
RSP_GAME rsp; //class
MENU M; //class
int score;
run_game();
init/ctor sets up menu ?

}

a big choice is whether to has-a inherit (as I show) or use other inheritance for game.

One thing I can't stress enough to beginners is to keep I/O and logic apart. I/O should be by itself, not mixed into the code. Pretend in 6 months you need to redo this game in a GUI... how can you keep the game logic apart from the print/read stuff? How would you fix the provided game above to use a GUI by changing every cout and cin statement to work with another library call? And after that one, you need to change it again later to make it work on a second GUI because it was so popular.... and maybe translate it to Spanish and French after that... its not pretty to fix...

Last edited on
codecaine1 wrote:
What I want my game to do:
The problem of this list is that you are thinking in terms of implementation details. Which involves thinking about step 3 before step 1 etc.

Design is only interface. Having a clean interface will dramatically reduce the need for lines of code, I promise.

Thinking strictly 'physical' will simplify the design process.

So, what is physical in your game:

Rock, Paper, Scissors

Interface wise they are the same and they are rather passive. What do we want from them?
The name "Rock" and what it beats "Scissors". That applies to all that above:
1
2
3
4
5
6
7
8
9
struct object_tye
{
  std::string m_Name;
  std::string m_Beats;

  object_tye(const std::string& name, const std::string& beats) : m_Name{name}, m_Beats{beats}
  {
  }
};

Next: human, computer. Inverface wise they are the same. They are active. What are they doing?
They select one object out of a collection of objects:
1
2
3
4
struct player_type
{
  virtual object_type select(const std::vector<object_type>& object_vector) = 0; // We know human and computer do this differently
};

Next: We always need an environment for our actors: Game. What do we know about the game?
We have two actors and a collection of objects and we want to play:

1
2
3
4
5
6
7
8
9
10
11
12
class game_type
{
private:
  std::unique_ptr<player_type> m_PLayer1;
  std::unique_ptr<player_type> m_PLayer2;
  std::vector<object_type> m_ObjectVector;

public:
  game_type(const std::unique_ptr<player_type> p1, const std::unique_ptr<player_type> p2, const std::vector<object_type>& object_vector) : ....

  void play();
};


So we have a consequent design. Let's start implement:
1
2
3
4
5
6
7
8
9
10
11
12
void class game_type::play()
{
  // TODO: loop
  const object_type& o1 = m_PLayer1->select(m_ObjectVector);
  const object_type& o2 = m_PLayer2->select(m_ObjectVector);
  if(o1.m_Beats == o2.m_Name)
    std::cout << "Player 1 wins";
  else if(o2.m_Beats == o1.m_Name)
    std::cout << "Player 2 wins";
  else 
    std::cout << "Tie";
}

We need a human:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct human_type : player_type
{
  virtual object_type select(const std::vector<object_type>& object_vector) override
  {
    std::cout << "Choose one:\n":
    for(std::vector<object_type>::size_type i = 0; i < object_vector.size(); ++i)
    {
      std::cout << i + 1 << ") " << object_vector[i] << '\n';
    }
    std::vector<object_type>::size_type choice;
    std::cin >> choice; // TODO: Error handling
    return object_vector[choice];
  }
};

We need a computer:
1
2
3
4
5
6
7
struct computer_type : player_type
{
  virtual object_type select(const std::vector<object_type>& object_vector) override
  {
    return object_vector[rand() % object_vector.size()];
  }
};

So does that look difficult?
What you need to do is identify the physical part, how they interact, and then create an environment. And it can be scaled for bigger projects.
Last edited on
I like coder777's approach but I'd code the object differently:
class Object {
Object() : type(Rock) {}
enum {Rock, Paper, Scissors } type;
const char *text() const; // return "rock", "paper" or "scissors"
bool operator<(const Object &rhs) const; // a<b means b beats a
};

By storing the value as an enum, it's much harder to get it wrong (like entering "rock" vs. "Rock").

One other comment on OOP design: imagine having to extend the program:
- "I need the program to run 1000 simultaneous games"
- "Can you make it so two people play against each other?"
- "Can you make it so the computer plays against itself"

If you design it well, these would be fairly easy changes. So think about stuff like this while doing the design.
careful with the operator. R beats S but R<S in that enum.
you can put them in an array and circularly index it, or make your comparison handle it for the enum, etc
Last edited on
Topic archived. No new replies allowed.