Help With Dice Roll Histogram

Hello. I am in need of assistance; currently working on a program that ask the user to input a certain amount of dice rolls to be simulated (in my case 500) using 4-dice. The program then keeps track of how many times a certain number between 4 and 24, the minimum amount all four dice could sum up to and the maximum, and print a bar graph histogram using "X"s. I have managed to get the program to compile and run but when it comes to printing the correct number of rolls in the form of "X"s, it goes on. Here is one example of what the program could look like:


How many rolls do you want? 500 (this is the user input)
4:
5:X
6:XXX
7:XXXXXXXXXXXXXX
8:XXXXXXXXXXXXXX
9:XXXXXXXXXXXXXXXXXXXXXXX
10:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
11:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
12:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
13:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
14:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
16:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
17:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
18:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
19:XXXXXXXXXXXXXXXXXX
20:XXXXXXXXXX
21:XXX
22:XX
23:
24:X

In this example, we asked the program to simulate 500 4-dice rolls. The sum was
never four, the sum was five on 1 of the 500 trials, the sum was six on 3 of the trials,
etc.


Help would be greatly appreciated! Thank you in advance. Below is my current code. The idea behind it is, it calls function roll(), takes that result and stores it in variables roll(1-4), using a random roll function. Afterwards it adds each up and gets a sum. This rollsum would then go into an if-else-if, that checks to see which of the possible 4-24 totals it landed on. Once the correct number is found, a counter is incremented and stored in an array. Once the total rolls are simulated and all counters are incremented correctly in their correct array index, the array is passed on to function printHistogram(). Here is where the printing of a similar image found above is suppose to take place.



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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>

using namespace std; 

//MAY NEED TO SEED THE PROGRAM IF THE NUMBERS DO NOT CHANGE..
int roll()
{
int d1 = 0; 
srand(time(0));

for(int i=1;i<21; i++)
	{
	d1 = rand() % 6+1;
	}
	cout << endl;
	return d1;
	
}


void printHistogram(int list[21], int z)
{
	for(int j = 0; j < 21; j++)
	{
		int k = 0;
		int counts[21];
		counts[j] = list[j];
		k = counts[j];
	
		 if(counts[j] == 0)
		 {	
		 	cout << j+4 << ": ";
		 	
		 }
		 else
		{
		 	cout << j+4 << ": " << endl;
		 	for(int p = 0; p < k; p++)
		 		cout << "X";
		}
	}
	
	
}


int main()
{
	//OUTPUT TO USER TO INPUT NUMBER OF DICE ROLLS
	cout << "Please enter how many roll simluations: " << endl;
	int input;
	cin >> input; //INPUT FROM USER FOR DICE ROLLS
	

	int list[21];	//CREATES ARRAY OF 21 SLOTS
	for(int i=0; i<21; i++)
		list[i]; //CATEGORIZES ARRAY
		
	int count4, count5, count6, count7, count8, count9, count10, count11, count12, count13, count14, count15, count16, count17, count18, count19, count20, count21, count22, count23, count24 = 0;
	
	for(int x = 0; x<= input; x++) //FOR ARRAY COUNTER
	{
		int y = 0;
		int roll1 = roll();
		int roll2 = roll();
		int roll3 = roll();
		int roll4 = roll();
		int rollsum = roll1 + roll2 + roll3 + roll4; 
		//cout << rollsum; this was merely an output to ensure function roll() was being called correctly
		
		if(rollsum == 4)
			{
			count4++;		
			y = 0;
			list[y] = list[y]+count4;
			}	
		else if(rollsum == 5)
				{
			count5++;		
			y = 1;
			list[y] = list[y]+count5;
				}	
			 else if(rollsum==6)
			 		{
				count6++;		
				y = 2;
				list[y] = list[y]+count6;
					}	
			 	  else if(rollsum == 7)
			 			{
						count7++;		
						y = 3;
						list[y] = list[y]+count7;
						}	
			 			else if(rollsum==8)
			 				{
							count8++;		
							y = 4;
							list[y] = list[y]+count8;
							}	
			 				 else if(rollsum==9)
			 				 	{
								count9++;		
								y = 5;
								list[y] = list[y]+count9;
								}	
			 				 	else if(rollsum == 10)
										{
										count10++;
										y = 6;
										list[y]=list[y]+count9;
										}
									else if(rollsum == 11)
										    {
											count11++;		
											y = 7;
											list[y] = list[y]+count10;
											}	
										 else if(rollsum==12)
										 			{
													count12++;		
													y = 8;
													list[y] = list[y]+count12;
													}	
										 	  else if(rollsum == 13)
										 				{
														count13++;		
														y = 9;
														list[y] = list[y]+count13;
														}	
										 			else if(rollsum==14)
										 						{
																count14++;		
																y = 10;
																list[y] = list[y]+count14;
																}	
										 				 else if(rollsum==15)
										 				 			{
																	count15++;		
																	y = 11;
																	list[y] = list[y]+count15;
																	}	
										 				 	  if(rollsum == 16)
											 				 			{
																		count16++;		
																		y = 12;
																		list[y] = list[y]+count16;
																		}
																else if(rollsum == 17)
													 				 			{
																				count17++;		
																				y = 13;
																				list[y] = list[y]+count17;
																				}
																	 else if(rollsum==18)
																					{
																					count18++;		
																					y = 14;
																					list[y] = list[y]+count18;
																					}
																	 	  else if(rollsum == 19)
																						{
																						count19++;		
																						y = 15;
																						list[y] = list[y]+count19;
																						}
																	 			else if(rollsum==20)
																							{
																							count20++;		
																							y = 16;
																							list[y] = list[y]+count20;
																							}
																	 				 else if(rollsum==21)
																								{
																								count21++;		
																								y = 17;
																								list[y] = list[y]+count21;
																								}
																	 				 	else if(rollsum == 22)
																									{
																									count22++;		
																									y = 18;
																									list[y] = list[y]+count22;
																									}
																							else if(rollsum == 23)
																										{
																										count23++;		
																										y = 19;
																										list[y] = list[y]+count23;
																										}
																								 else
																								 	 		{
																											count24++;		
																											y = 20;
																											list[y] = list[y]+count24;
																											}
																								 	  
		
		}
	
	printHistogram(list, 21);	
	
	return 0;

}




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
#include <iostream>
#include <cstdlib>
#include <string>
#include <ctime>

using namespace std; 

//MAY NEED TO SEED THE PROGRAM IF THE NUMBERS DO NOT CHANGE..
int roll()
{
	int dice = 0;
	
	while (dice == 0)
	{
		dice = rand() % 7;
	}
	
	return dice;	
}


void printHistogram(int list[], const int NUMROLLS)
{
	for (int count = 0; count < NUMROLLS; count++)
	{
		cout << "\n" << count + 4 << ": ";
		
		for (int index = 0; index < list[count]; index++)
		{
			cout << "X";
		}
	}
}


int main()
{
	srand(time(0));
	const int NUMBER_OF_POSSIBLE_ROLLS = 21;
	
	//OUTPUT TO USER TO INPUT NUMBER OF DICE ROLLS
	cout << "Please enter how many roll simluations: " << endl;
	int input;
	cin >> input; //INPUT FROM USER FOR DICE ROLLS
	
		
	//CREATES ARRAY OF 21 SLOTS	
	int countHistory[NUMBER_OF_POSSIBLE_ROLLS];
	for (int count = 0; count < NUMBER_OF_POSSIBLE_ROLLS; count ++)
	{
		countHistory[count] = 0;
	}
	
	for(int count = 0; count < input; count++) //FOR ARRAY COUNTER
	{		
		int dice_1 = roll();
		int dice_2 = roll();
		int dice_3 = roll();
		int dice_4 = roll();
		int sum = dice_1 + dice_2 + dice_3 + dice_4; 

		
		for (int index = 4; index <= NUMBER_OF_POSSIBLE_ROLLS + 4; index++)
		{
			if (sum == index)
			{
				countHistory[index - 4]++;
				break;
			}
		}
			 	  		
	}
	
	printHistogram(countHistory, NUMBER_OF_POSSIBLE_ROLLS);	
	
	cout <<"\n\n ";
	system("pause");
	
	return 0;
}


In your roll function you only need to generate one number, doing it 21 times is useless really. You don't check if your random number is equal to 0 though, which a dice can't do. Also producing a seed for your rand() function, is only really needed once, not for every number.

Instead of so many if statements, you can use a single for loop, it makes your code more readable and much easier to work with.

The reason you're getting so little results on the high and low end of your graph: take for instance
the probability that a dice rolls a 1: 1/6 = 0.166 which means 166 chances out of 1,000 tries
that two dices in a row roll a 1: (1/6)² = 0.027 which means 27 chances out of 1,000 tries
that four dices in a row roll a 1: (1/6)^4 =0.0007 which means 7 chances out of 10,000 tries, this is the same for rolling four 24 in a row.
EDIT: for 23 and 2 as a sum for instance, your chances are: ((1/6)^3 * (2/6)) = 0.0015 which translates to roughly one chance in 1,000 tries

So the most probable is that the sum will be around the average of the highest possible sum and the lowest possible sum, hence the > shape on your graph
Last edited on
Do not call srand() multiple times (inside roll). srand() sets the RNG to a particular starting point. Calling srand() repeatedly can cause the RNG to return the same random numbers. srand() should be called ONCE at the beginning of main().
http://www.cplusplus.com/reference/cstdlib/srand/
H00GO - thank you very much for clearing that up for me! That makes total sense now that I think about it!

AbstractionAnon - thank you also!

I appreciate the feedback and will use it wisely!


Louie
Last edited on
Glad to have been of any help Louie, you're very welcome!
EDIT: for 23 and 2 as a sum for instance, your chances are: ((1/6)^3 * (2/6)) = 0.0015 which translates to roughly one chance in 1,000 tries


You can't get 2 as a sum.

For 23 (and also for 5) as a sum, your chances are 4 * (1/6)4 = 0.00309... which translates to roughly 3 in 1000.


 1: 
 2: 
 3: 
 4: *
 5: ***
 6: ********
 7: ***************
 8: ***************************
 9: *******************************************
10: *************************************************************
11: **********************************************************************************
12: ***********************************************************************************************
13: **************************************************************************************************************
14: ***************************************************************************************************************
15: **************************************************************************************************************
16: ************************************************************************************************
17: *********************************************************************************
18: *************************************************************
19: *******************************************
20: ***************************
21: ****************
22: ********
23: ***
24: *



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
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;

//======================================================================

class Histogram
{
   vector<int> freq;
   vector<string> names;
public:
   Histogram( vector<int> f, vector<string> n ) : freq( f ), names( n ) { }
   void plotH( double factor = 1, int nwidth = 2, char marker = '*' );
};

void Histogram::plotH( double factor, int nwidth, char marker )
{
   vector<int> f = freq;
   for ( int &e : f ) e = factor * e + 0.5;

   for ( int b = 0; b < f.size(); b++ )
   {
      cout << setw( nwidth ) << names[b] << ": " << string( f[b], marker ) << '\n';
   }
}

//======================================================================

int multiRoll( int n, int faces )
{
   int sum = 0;
   for ( int r = 1; r <= n; r++ ) sum += 1 + rand() % faces;
   return sum;
}

//======================================================================

int main()
{
   const int FACES = 6;
   const int SAMPLES = 1000000;
   const int ROLLS_PER_SAMPLE = 4;

   int bins = FACES * ROLLS_PER_SAMPLE;

   vector<int> f( bins, 0 );
   vector<string> n( bins );   for ( int i = 1; i <= bins; i++ ) n[i-1] = to_string( i );

   srand( time(0) );
   for ( int i = 0; i < SAMPLES; i++ ) f[ multiRoll( ROLLS_PER_SAMPLE, FACES ) - 1 ]++;


   Histogram h( f, n );
   double factor = 1000.0 / SAMPLES;
   h.plotH( factor, 2 );
}


Last edited on
My bad I meant 5 and 23 as a sum!

My calculation for those was messed up too, I can't think of what I was thinking then. How do you explain
4 * (1/6)4 = 0.00309...
It's been a long time since I've studies probabilities at school, I'm a bit rusty..
H00G0 wrote:
How do you explain ...


Any particular sequence of dice has probability (1/6)4 (as you implicitly recognised).

You can get 5 from any of four sequences, each with the same probability (1/6)4:
2111
1211
1121
1112

That's 4 lots of (1/6)4 to get 5 somehow.

Similarly for 23 made of one five and three sixes.


As the code shows, it works surprisingly well for a large number (e.g. 1 million) samples, despite the flak people throw at rand().


BTW. You replied very well to the original post.
Last edited on
Any particular sequence of dice has probability (1/6)4 (as you implicitly recognised).

You can get 5 from any of four sequences, each with the same probability (1/6)4:
2111
1211
1121
1112


Of course! I was missing the different ways you could get 5 as a sum with four dices! My bad!

You have a very interesting way of structuring your code, never seen it done that way before (maybe i'm the one doing it in a weird way haha).

What was your intention behind rolling 4 times before getting the dice face?

for ( int &e : f ) e = factor * e + 0.5; Could you explain what you're doing here in your for statement, I'm not sure I'm very familiar with or understand it well enough?


BTW. You replied very well to the original post.

Thanks. I try my best when I feel I could help or provide a constructed answer.. :)
What was your intention behind rolling 4 times before getting the dice face?

Not sure what you are asking: I got n(=4) throws and added each of them to sum.
for ( int r = 1; r <= n; r++ ) sum += 1 + rand() % faces;


for ( int &e : f ) e = factor * e + 0.5; Could you explain what you're doing here in your for statement, I'm not sure I'm very familiar with or understand it well enough?

I'm making sure it fits on the screen! The frequencies for a million samples are quite large, so I factor them down. The "+0.5" is just to ensure it rounds to the nearest integer when it truncates down to assign to an integer.
Not sure what you are asking: I got n(=4) throws and added each of them to sum.
Oh yeah, my bad, thought you were doing it four times for each dice, read too quickly.

I guess what I should have asked more precisely is about this: int &e : f Allow me to sound stupid or ignorant if I do. But I've only ever used '&' for passing arguments as reference or get the address of a variable. What is it's use here in declaring int e and what does it do in your for statement, I assume it must check for some condition?
for ( int &e : f ) e = factor * e + 0.5;
The reference &e is a bit like passing arguments to functions as references rather than passing by value. If you don't have the & (try it and you will see!) then just a copy will be made and transformed ... but the elements of f themselves won't be changed (and so will remain far too large for the screen!).

I know that you are aware of passing function arguments by value or reference: it does the same here.
C++ Standard Library has alternative RNGs:
http://www.cplusplus.com/reference/random/uniform_int_distribution/

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
#include <iostream>
#include <random>
#include <chrono>
#include <algorithm>    // std::minmax_element

int main()
{
  constexpr int FACES {6};
  constexpr int DICE {4};

  // obtain a seed from the system clock:
  unsigned seed1 = std::chrono::system_clock::now().time_since_epoch().count();

  std::mt19937 generator( seed1 ); // mt19937 is a standard mersenne_twister_engine
  std::uniform_int_distribution<int> distribution( 1, FACES );

  int p[ DICE*FACES+1 ] {};
  const int nrolls {500}; // number of rolls

  for (int i=0; i < nrolls; ++i) {
    int number {};
    for ( int die=0; die < DICE; ++die ) {
        number += distribution(generator);
    }
    ++p[number];
  }

  for (int i=DICE; i < DICE*FACES+1; ++i)
    std::cout << i << ": " << std::string(p[i], '*') << '\n';

  auto result = std::minmax_element( p+DICE, p+DICE*FACES+1 );
  std::cout << "min is " << *result.first;
  std::cout << ", at position " << (result.first-p) << '\n';
  std::cout << "max is " << *result.second;
  std::cout << ", at position " << (result.second-p) << '\n';
}
The reference &e is a bit like passing arguments to functions as references rather than passing by value. If you don't have the & (try it and you will see!) then just a copy will be made and transformed ... but the elements of f themselves won't be changed (and so will remain far too large for the screen!).


Thanks for your explanation, I just figured it out! Somehow I hadn't learned that you could initialise a vector that way, my brain was reading the colon as a mathematical sign, hence the brainfarts!
Topic archived. No new replies allowed.