Doing this project 1 step @ a time but the multiple files are starting to get confusing. Lemme start by telling you all what I need to do, then show you what I've done, and then maybe you can tell me if I'm on track with this or if I need to scrap it and start over. I don't necessarily want you to give me any code - I can do my own homework, but a point in the right direction.

Simulate Dice Rolling Game
1. Two classes used (Die, DiceRoll)
Note: Die is an abstraction of a single physical entity
DiceRoll is an abstraction of three dice rolled together
DiceRoll contains no constructor member function
class Die and class DieRoll must be in separate files
2. Three global functions required
void GatherStats(int RollsArray[], int RollsArraySize, int ResultsArray[]); // prototype
3. Loop for the size of the RollsArray include switch
Note: case clause increment ResultsArray cell, count all possible dice rolls
Frequency of each total is recorded in ResultsArray
4. Main driver global function declare two arrays and call the gloval functions
Note: Min size of 200
5. Pass RollsArray to GatherStats function and call DisplayResults
6. Display a horizontal histogram of asterisk characters.
7. Display a vertical histogram of asterisk characters.

Code(This is broken up into multiple files cuz it's easier to work on and compile.)

Dice.h

#ifndef DieRoll					// allows for additional
#define DieRoll	

class Die

{
public:										// available outside of class
		Die();								// SMF to set value
		void Roll();						// member functions
		int GetFaces();

private:									// not available outside class
		int Face;
};
            	
#endif

Dice.cpp

#include <iostream>  	  			// for cin,cout
#include <cstdlib>					// for the library rand() function
#include <cmath>
#include <ctime>

using std::cout;
using std::cin;
using std::endl;
Die::Die()						//Initializes Face data member
{
Face = 1,2,3,4,5,6;
}

void Die::Roll()				//Sets Face to a randomly generated number 1-6
{
	srand(time_t(NULL));
	
}

int Die::GetFaces()				// returns the die face value
{
	return rand() %6 + 1;
}

DiceRoll.h

class DiceRoll					// classs that specifies a collection of 3 contained Die objects

{
public:
		void RollDice();		// Calls Roll() function on each contained die
		int GetRollFaces();		// Returns the sum of the current Face value of Die1, Die2, & Die3

private:
		Die Die1;				// The three die contained in this -i.e
		Die Die2;				// objects of this class automatically contain
		Die Die3;				// three dice
};

Function.h

void GatherStats(int RollsArray[], int RollsArraySize, int ResultsArray[]); // 3 global functions

#endif

Function.cpp (mostly pseudo-code at this point)

using std::cout;
using std::cin;
using std::endl;
void GatherStats(int RollsArray[], int RollsArraySize, int ResultsArray[])
{
	for (int i =0;i < RollsArraySize; i++)
	{
		switch (RollsArray[i])
	{
		case 3: ResultsArray[0]=ResultsArray[0]+1; break;
		case 4: ResultsArray[1]=ResultsArray[1]+1; break;

	}
	}
	
}
Nick Evan commented: Excellent first post. Bravo! +15

Recommended Answers

All 13 Replies

The first issue that I see is your headers and header guards. You haven't properly #included them where necessary and you have only guarded "Dice.h", but you forgot to guard "DieRoll.h". This is generally how you want to do it:

Object.h

#ifndef OBJECT_H
#define OBJECT_H

class Object {
private:
  int m_someValue;  //a member variable
public:
  Object();         //constructor prototype
};

#endif              //OBJECT_H

Object.cpp

#include "Object.h" //notice the #include, you don't have these right

Object::Object() {  //constructor implementation
  m_someValue = 0;
}

Container.h

#ifndef CONTAINER_H
#define CONTAINER_H

#include <vector>
#include "Object.h" //again, notice the #include

class Container {
private:
  std::vector<object> m_objects;  //a member variable (a vector of Objects)
public:
  Container();      //constructor prototype
};

#endif //CONTAINER_H

Container.cpp

#include "Container.h"  //there's that #include again
//#includes for Object.h and <vector> not required
//the #include statement for Container.h handles them for you

Container::Container() {  //constructor implementation
  m_objects.reserve(10);
}

main.cpp

/*
 * standard #includes as necessary
 * #include <iostream>
 * #include <vector>
 * etc...
 *
 */

//other #includes
#include "Container.h"
// #include "Object.h"    //this #include not really needed, handled by "Container.h"

using namespace std;

int main() {
  /*
   *  whatever you need to do...
   */
  return 0;
}

Die::Die() //Initializes Face data member
{
Face = 1,2,3,4,5,6;
}

I am not sure what you wanted to achieve by this, but this assigns 6 to Face. The operator comma works like this - it evaluates all of its arguments from left to right then returns the rightmost argument, in this case 6. So, this is one thing.

Next, the .cpp file for your class where you define Die::Die etc. MUST include the appropriate .h file.

Then, why write using std::cout, std::endl etc. if you don't use them (I am talking about class .cpp file)

I suppose your code is not complete here, so I can't figure out how it works.

If you have any SPECIFIC questions, I'd be happy to help

I agree with Fbody, plus (and I'm a newbie so I may be wrong) it looks like you're doing the same steps over and over, but calling it different steps. Maybe go back through the specifications and really take them one at a time until that step is working before moving on to the next. I learned that lesson the hard way.

2. Three global functions required
void GatherStats(int RollsArray[], int RollsArraySize, int ResultsArray[]); // prototype
3. Loop for the size of the RollsArray include switch
Note: case clause increment ResultsArray cell, count all possible dice rolls
Frequency of each total is recorded in ResultsArray

I'd be interested to see how you would do this. And even more so to see how you make the histograms. Good luck though.

Upon further review, I also see that you seem to have trouble understanding the actions that should be performed by your various classes and their member functions, in particular, your Die object(s). I strongly suggest that you focus on building, and thoroughly testing, a working Die class. Once you have that, move on to building a working DieRoll class (I really don't like that name for some reason) and so on. Step-By-Step is the way to go. Don't try to do it all at the same time, it will easily overwhelm you if you're not familiar with the process, as you have now seen.

void Die::Roll()	//Sets Face to a randomly generated number 1-6
{
	srand(time_t(NULL));
 
}

Sorry, but no it doesn't...
The function srand() is the seed function for the RNG, it does not actually produce a random number. As a result, your Die::Roll() method really accomplishes nothing besides re-seeding the RNG over and over. You should only call srand() once in your entire program, but you are calling it potentially several hundred times. This has the potential to cause every rolled value to be identical depending on how fast your computer executes the instructions. To correct this, you need to move your seed from the class to the very beginning of main(). How do you expect it to modify the value of Die::Face if it never interacts with it?...

int Die::GetFaces()  // returns the die face value
{
	return rand() %6 + 1;
}

Again, I'm afraid not...
It returns the result of a random number generation, not the value of the Die::Face member variable.
As implemented, this method (sort of) does what Die::Roll() should do. You need to move your call to rand() (and all other associated portions of the statement, except return) from GetFaces() to Roll() and store the result to Die::Face then return the value of Die::Face from a call to Die::GetFace().

You can also improve your DiceRoll class if you are at all familiar with arrays or vectors. It sounds to me like this is the whole point of the lesson, getting familiar with arrays. It will be much simpler for you if you use an array (or vector) of Die objects instead of individual Die objects. Using an array will allow you to interact with all your objects more efficiently.

//continuing with example from previous post...

const int SET_SIZE = 3;
class Container {
private:
  Object myObjects[SET_SIZE];  //create an array of SET_SIZE Objects
public:
  Container();
};
Container::Container() {
  for(int currentObject = 0; currentObject < SET_SIZE; ++currentObject) {
    myObjects[currentObject].someFunction();
  }
}

This will allow you to interact with every Object identically with fewer maintenance headaches and less code to write.

commented: Good point about array/vector extensibility. +1

Die::Die() //Initializes Face data member
{
Face = 1,2,3,4,5,6;
}

I am not sure what you wanted to achieve by this, but this assigns 6 to Face. The operator comma works like this - it evaluates all of its arguments from left to right then returns the rightmost argument, in this case 6. So, this is one thing.

This is not quite correct. The comma operator ( http://msdn.microsoft.com/en-us/library/zs06xbxh%28VS.80%29.aspx -> please forgive the microsoft link. It seemed to be the most informative that I could find quickly ) is indeed evaluated from left to right. It also returns the right-most argument. However, you need to remember to check operator precedence. The comma operator allows two statements to be processed in one line of code. It gurantees that the left statement is completed from before the right, and that all the side-effects of the left statement will influence the right statement.

In this case, the assignment operator has a higher precedence than the comma operator, so it is evaluated first. So, Face is set to 1; The following statements are constant integer expressions that have no effect. The line does indeed return 6, but nothing is done with that value, so it is ignored.

Now, if the line read:

Face = ( Face = 1,2,3,4,5,6 );

Face would first be assigned to 1, then all of the constant expressions would be evaluated, then 6 would be returned from the last comma operator and Face would be set to this value. The final value of Face after this line is 6.

Sorry if this post seems pedantic, but I found that my own knowledge of the comma operator was deficient, so I read up and posted what I found here.

Cheers!

@OP

I see no reason for the Die object to store the result of its roll. There is nothing wrong with this idea. However, it seems that you will always first roll the die and then fetch its value. This could be accomplished in one step by having the roll() function return the result of the roll. You could still store the result, if you really wanted to, and fetch it again later with the getFace() function:

int Die::Roll()
{
    face = rand() % 6 + 1;
    return face;	
}

int Die::getFace()
{
    return face;
}

if you don't need to store the result (i.e. you will use it right away):

int Die::Roll()
{
    return rand() % 6 + 1;
}

The DiceRoll::rollDice() and DiceRoll::getRollFaces() could be consolidated in the same way.

commented: Both good points, but please see response. +2

@OP

I see no reason for the Die object to store the result of its roll. There is nothing wrong with this idea. However, it seems that you will always first roll the die and then fetch its value. This could be accomplished in one step by having the roll() function return the result of the roll. You could still store the result, if you really wanted to, and fetch it again later with the getFace() function:

int Die::Roll()
{
    face = rand() % 6 + 1;
    return face;	
}

int Die::getFace()
{
    return face;
}

if you don't need to store the result (i.e. you will use it right away):

int Die::Roll()
{
    return rand() % 6 + 1;
}

The DiceRoll::rollDice() and DiceRoll::getRollFaces() could be consolidated in the same way.

Those are both good points. I think that storing the face value is probably the better option though because the results of rolling all 3 Dice needs to be analyzed statistically later on. This will allow them to randomly access the Die::Face values multiple times before generating a new set of face values.

Thanks guys. I knew in my confusion with the two classes doing almost the same thing that I was going to get confused. I've implemented the suggestions and have a working Die set.

As it was explained to me in another email, the Die class is an abstraction of a single physical die entity, while the DiceRoll class is an abstraction of three dice being rolled together with their total spots being reported to a using client via the member function Dice::GetFace().

In other words, each roll of the dice is one call to the RollDice() member function of a DiceRoll object that contains three objects of class Die.

The class DiceRoll contains no constructor member function since the contained data members are, themselves, objects of the class Die. These contained objects should be automatically initialized via their constructors when a DiceRoll object is declared (i.e., the constructor for class Die will automatically be invoked three times to construct each of the three contained Die object data members).

And yeah, I need to store the values so that later on I can use that data to build the two histograms. We never went over how to do that, but I'll look it up in the book.

The class DiceRoll contains no constructor member function since the contained data members are, themselves, objects of the class Die. These contained objects should be automatically initialized via their constructors when a DiceRoll object is declared (i.e., the constructor for class Die will automatically be invoked three times to construct each of the three contained Die object data members).

I'm not sure if this is really relevant to this or not, but I think it's worth noting...

Because your DieRoll class has no constructors defined, the compiler automatically provides a "default" constructor. For this situation, the provided constructor is sufficient, but it may not be for other situations.

The code for Container::Container() from this post is all that would be needed for a DieRoll constructor (with some very minor modifications).

You could even simplify it to:

DieRoll::DieRoll() {
  this->RollDice();
}

then put the loop in the RollDice() method.

I know I shouldn't be thinking about the histograms yet, but I was wondering, if I took the values in the arrays and used <string> functions to replace the numbers with an * mark would that print out a histogram? I'd have to use pointers I think to move things around, but it just might work.

How's this looking now? I've got Die and DieRoll compiling smoothly now.

Updated Code:
Die.h

#ifndef DIE_H					
#define DIE_H

//              Class Object
class Die

{
public:							// available outside of class
		Die();					// SMF to set value
		void Roll();				// member functions
		int GetFaces();

private:						// not available outside class
		int Face;
};
#endif

Die.cpp

#include "Die.h"				        // for processing die face
#include <cstdlib>					// for the library rand() function

Die::Die()						//Initializes Face data member
{
 Face = 0;
}

void Die::Roll()                                        // pulls the value of that roll
{
    Face = rand() % 6 + 1;
	
}
 
int Die::GetFaces()                                     // returns the die value
{
    return Face;
}

DieRoll.h

#ifndef DIEROLL_H			
#define DIEROLL_H				

#include "Die.h"

class DiceRoll					        // classs that specifies a collection of 3 contained Die objects

{
public:
		DiceRoll();				// Calls Roll() function on each contained die
		int GetRollFaces();		        // Returns the sum of the current Face value of Die1, Die2, & Die3

private:
		Die Die1;				// The three die contained in this -i.e
		Die Die2;				// objects of this class automatically contain
		Die Die3;				// three dice
};
#endif

DieRoll.cpp

#include <cstdlib>					// for the library rand() function
#include "DieRoll.h"				       // for processing die roll
#include "Die.h"

	DiceRoll::DiceRoll()		
	{
		Die1.Roll();
		Die2.Roll();
		Die3.Roll();
	}
	int DiceRoll::GetRollFaces()
	{
		int result;
		result = (rand() % 6) + 1;
		return result;
	}

Next step are the 3 global functions for the array that collects the results of the rolls.

I was thinking of doing it something like this:
Loop: for the size of the RollsArray
switch on the contents of the RollsArray
case 3: ResultsArray[0] = ResultsArray[0] + 1; break;
case 4: ResultsArray[1] = ResultsArray[1] + 1; break;
etc.
end switch
go to the next RollsArray cell and loop again


Would that work for setting up to build on the next step (histograms)?

I think it would be more efficient to access the ResultsArray directly by using the produced value as the index.

const int MAX_VALUE = 18;
int results[MAX_VALUE + 1] = {0};  //add 1 to compensate for zero-based arrays

for (int i = 0; i < MAX_ITERATIONS; ++i) {
  dieSet.roll()
  results[dieSet.getResult()]++;
}

The problem with this approach is that the minimum value producible should be 3 and the max should be 18. That leaves a couple wasted elements (specifically 0-2) at the beginning of the array. In a program such as this, it's probably a non-issue.

Another option would be to translate the values while recording them. This result is similar to what you are doing with the switch, but more efficient. The problem is this may be confusing to someone reading your code:

const int MAX_VALUE = 18;
const int OFFSET = 3
int results[MAX_VALUE-OFFSET] = {0};

for (int i = 0; i < MAX_ITERATIONS; ++1) {
  dieSet.roll()
  results[dieSet.getResult()-OFFSET]++;
}

I would also consider the first option a better choice from a readability standpoint.

I think it would be more efficient to access the ResultsArray directly by using the produced value as the index.

const int MAX_VALUE = 18;
int results[MAX_VALUE + 1] = {0};  //add 1 to compensate for zero-based arrays

for (int i = 0; i < MAX_ITERATIONS; ++i) {
  dieSet.roll()
  results[dieSet.getResult()]++;
}

Thanks man. I worked on it over the weekend, and I've made progress. It compiles and runs. Now, how do I take the results and place them into a vertical and a horizontal histogram of the data?

Die.h

#ifndef DIE_H					// allows for additional
#define DIE_H

class Die

{
public:										// available outside of class
		Die();								// SMF to set value
		void Roll();							// member functions
		int GetFaces();

private:									// not available outside class
		int Face;
};

#endif

Die.cpp

#include "Die.h"				// for processing die face
#include <cstdlib>					// for the library rand() function

Die::Die()						//Initializes Face data member
{
 Face = 1;
}

void Die::Roll()
{
    Face = rand() % 6 + 1;
	
}
 
int Die::GetFaces()
{
    return Face;
}

DieRoll.h

#ifndef DIEROLL_H			// allows for additional
#define DIEROLL_H				

#include "Die.h"

class DiceRoll					// classs that specifies a collection of 3 contained Die objects

{
public:
		void RollDice();				// Calls Roll() function on each contained die
		int GetRollFaces();		// Returns the sum of the current Face value of Die1, Die2, & Die3

private:
		Die Die1;				// The three die contained in this -i.e
		Die Die2;				// objects of this class automatically contain
		Die Die3;				// three dice
};
#endif

DieRoll.cpp

#include <iostream>  				// for cin,cout
#include <cstdlib>					// for the library rand() function
#include "DieRoll.h"				// for processing die roll
#include "Die.h"

	void DiceRoll::RollDice()		
	{
		Die1.Roll();
		Die2.Roll();
		Die3.Roll();
	}
	int DiceRoll::GetRollFaces()
	{
		int result;
		result = Die1.GetFaces()+Die2.GetFaces()+Die3.GetFaces();
		return result;
	}

FreeFunction.h

#ifndef SHELL_H   // Avoid duplicate compilations
#define SHELL_H   //

void GatherStats(int RollsArray[], int RollsArraySize, int ResultsArray[]); // 1st global function prototype
void DisplayResults(int ResultsArray[ ], int ResultsArraySize ); //2nd global function prototype
void createHistogram(int frequency[], int range);				 // function to display histogram

#endif

FreeFunction.cpp

#include <iostream>  				// for cin,cout
#include <iomanip>
#include <cstdlib>					// for the library rand() function
#include <cmath>
#include <ctime>
#include "FreeFunction.h"			// for processing the roll
#include "Die.h"				// for processing die face
#include "DieRoll.h"				// for processing die roll


using namespace std;
void GatherStats(int RollsArray[], int RollsArraySize, int ResultsArray[])
{
	for ( int i = 0; i <200; i++)
	{
		switch ( RollsArray[ i ] ) 
		{
		case 3: ResultsArray[ 0 ] += 1; break;
		case 4: ResultsArray[ 1 ] += 1; break;
		case 5: ResultsArray[ 2 ] += 1; break;
		case 6: ResultsArray[ 3 ] += 1; break;
		case 7: ResultsArray[ 4 ] += 1; break;
		case 8: ResultsArray[ 5 ] += 1; break;
		case 9: ResultsArray[ 6 ] += 1; break;
		case 10: ResultsArray[ 7 ] += 1; break;
		case 11: ResultsArray[ 8 ] += 1; break;
		case 12: ResultsArray[ 9 ] += 1; break;
		case 13: ResultsArray[ 10 ] += 1; break;
		case 14: ResultsArray[ 11 ] += 1; break;
		case 15: ResultsArray[ 12 ] += 1; break;
		case 16: ResultsArray[ 13 ] += 1; break;
		case 17: ResultsArray[ 14 ] += 1; break;
		case 18: ResultsArray[ 15 ] += 1;
		}
	}
}

void DisplayResults(int ResultsArray[ ], int ResultsArraySize )
{
	for ( int i = 0; i < 200; i++)
	{
		cout<< "\n\nOut of 200 Rolls of 3 Dice: \n\n"<< endl;
		cout << "The number " << i + 1 << " was rolled " << ResultsArray[i] << " times." << endl;
	}
}

//Creates Histogram of scores
void createHistogram(int frequency[], int range)
{
       for(int i = 0;i<=range;i++)
	   {
		   cout<<setw(3)<<i<<setw(3)<<frequency[i];
		   for(int j = 1;j<=frequency[i];j++)
		   {
			   cout<<"*";
			   cout<<endl;
		   }
	   }
}

MAIN.cpp

#include <iostream>  				// for cin,cout
#include <iomanip>
#include <cstdlib>					// for the library rand() function
#include "FreeFunction.h"			// for processing the roll
#include "DieRoll.h"				// for processing die roll


using namespace std;
int main ()
{
	const int NUM_RANGE = 15;
	int frequency[NUM_RANGE+1];
	int RollsArray[100];
	DiceRoll TheIvories;
	for (int i = 0; i < 100; i++)
	{
	TheIvories.RollDice();						// Roll the dice
	RollsArray[i] = TheIvories.GetRollFaces(); // Record the result of the roll end loop
	}
		for (int j = 0; j< 100; j++)
	{
		cout << RollsArray[j]<< endl;
	
	}


return 0;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.