It is time to turn your attention back to the development of this chapter's game project, the Dice Poker game. You will create this game by following the five basic development steps that you've used to create all the previous chapter projects.
The Dice Poker game is played on a single window and is made up of one form and the 20 controls listed in Table 7.1.
Control Type | Control Name | Description |
---|---|---|
Label | lblWelcome | Displays the game's welcome message |
PictureBox | pbxDie1 | Displays the image for the first die |
PictureBox | pbxDie2 | Displays the image for the second die |
PictureBox | pbxDie3 | Displays the image for the third die |
PictureBox | pbxDie4 | Displays the image for the fourth die |
PictureBox | pbxDie5 | Displays the image for the fifth die |
CheckBox | chkDie1 | Determines whether the first die should be held at the end of the first roll |
CheckBox | chkDie2 | Determines whether the second die should be held at the end of the first roll |
CheckBox | chkDie3 | Determines whether the third die should be held at the end of the first roll |
CheckBox | chkDie4 | Determines whether the fourth die should be held at the end of the first roll |
CheckBox | chkDie5 | Determines whether the fifth die should be held at the end of the first roll |
CheckBox | chkKeepAll | Determines whether the CheckBox controls representing all five dice should be checked |
Button | btnRollDice | Controls the logic that rolls the dice for the game |
Button | btnbreak | Controls the termination of the game |
GroupBox | grpScoring | Provides a container for storing the Label control that displays the game's scoring rules |
GroupBox | grpOutput | Provides a container for storing the TextBox control that displays the output messages |
Label | lbllegend | Displays the game's scoring rules |
TextBox | txtoutput | Displays status messages during the game's execution |
Imagelist | imlDiceList | Stores a list of bitmap images representing each of the six sides of a die |
Timer | tmrRoll | Controls the logic that spins each of the game's five dice |
The first step in creating the Dice Poker game is to open Visual C++ and create a new project, as outlined here:
If you have not already done so, start Visual C++ 2005 Express and then click on File and select New Project. The New Project dialog box appears.
Select the Windows Application template.
Type Dice Poker as the name of your new application in the Name field at the bottom of the New Project window.
Click on OK to close the New Project dialog box.
Visual C++ creates a new project for you and displays a new form upon which you can design the game's user interface.
The first step in laying out the user interface is to add controls to the form and move and resize them to the appropriate locations. As you go through each step, make sure that you reference Figure 7.7 so that you know where to place each control and what size to make it.
Figure 7.7: Completing the interface design for the Dice Poker game.
Begin by resizing form1 until its size is 660, 480.
Next, add a Label control near the top of the form. You'll use this control to display the game's welcome message.
Add five PictureBox controls to the form. Space them evenly just under the Label control.
Hint | A PictureBox control allows you to display a graphical image on a Visual C++ form. A PictureBox control can display graphics files stored in any of the following formats: .gif, jpg, .jpeg, .bmp, .wmf, and .png. |
Add six Checkbox controls to the form. Place each of the first five controls under one of the five PictureBox controls. Place the sixth CheckBox control in the middle of the form about .5 inches below the third Checkbox control.
Add two Button controls, one on each side of the sixth CheckBox control.
Next, add two GroupBox controls under the two Button controls. Enlarge and resize each GroupBox control until it takes up most of the space at the bottom of the form.
The first GroupBox control displays a text string showing the player how winning hands are scored. Therefore, you need to add three Label controls inside the first GroupBox control.
The second GroupBox control displays informational messages that identify winning and losing hands and tell the player how many dollars are in the account. To display this text, you need to add a TextBox control inside the second GroupBox control and resize it until it almost fills the GroupBox control. To resize the control vertically, you must set the Multiline property to true.
To control the rolls of each die, you need to add a Timer control to your application. After you've added this control, it is displayed in the Component Tray.
Finally, you need to add an ImageList control to your application. After you've added it, it is displayed in the Component Tray.
Hint | An ImageList control stores images, which other controls, such as the PictureBox control, can then display. |
At this point, the overall layout of the Dice Poker game's user interface is now complete, and you can begin modifying form and control properties.
Let's begin by making the required changes to properties belonging to the Form object, as listed in Table 7.2.
Property | Value |
---|---|
Name | frmMain |
Cursor | Hand |
FormBorderStyle | Fixed3D |
MaximizeBox | false |
StartPosition | CenterScreen |
Size | 660, 480 |
Text | Dice Poker |
Make the property changes shown in Table 7.3 to the Label controls.
Control | Property | Value |
---|---|---|
Label1 | Name | lblWelcome |
ForeColor | Desktop | |
Text | Welcome to Visual C++ Dice Poker | |
Font.Bold | true | |
Font.Size | 12 | |
Label2 | Name | lblLegend1 |
Text | 5 of a kind = $4 Full House = $2 | |
Label3 | Name | lblLegend2 |
Text | 4 of a kind = $3 High Straight = $3 | |
Label4 | Name | lblLegend3 |
Text | 3 of a kind = $1 Low Straight = $3 |
Make the property changes shown in Table 7.4 to the PictureBox controls.
Control | Property | Value |
---|---|---|
PictureBox1 | Name | pbxDiel |
BorderStyle | Fixed3D | |
Size | 75,75 | |
Size Mode | StretchImage | |
PictureBox2 | Name | pbxDie2 |
BorderStyle | Fixed3D | |
Size | 75,75 | |
Size Mode | StretchImage | |
PictureBox3 | Name | pbxDie3 |
BorderStyle | Fixed3D | |
Size | 75,75 | |
Size Mode | StretchImage | |
PictureBox4 | Name | pbxDie4 |
BorderStyle | Fixed3D | |
Size | 75,75 | |
Size Mode | StretchImage | |
PictureBox5 | Name | pbxDie5 |
BorderStyle | Fixed3D | |
Size | 75, 75 | |
Size Mode | StretchImage |
Make the property changes shown in Table 7.5 to the CheckBox controls.
Control | Property | Value |
---|---|---|
CheckBox1 | Name | chkDiel |
Text | Keep | |
Visible | False | |
CheckBox2 | Name | chkDie2 |
Text | Keep | |
Visible | False | |
CheckBox3 | Name | chkDie3 |
Text | Keep | |
Visible | False | |
CheckBox4 | Name | chkDie4 |
Text | Keep | |
Visible | False | |
CheckBox5 | Name | chkDie5 |
Text | Keep | |
Visible | False | |
CheckBox6 | Name | chkKeepAll |
Text | Keep All | |
Visible | False |
Make the property changes shown in Table 7.6 to the Button controls.
Control | Property | Value |
---|---|---|
Button1 | Name | btnRollDice |
Font.Bold | true | |
Text | Roll Dice | |
Button 2 | Name | btnbreak |
Text | Quit | |
Font.Bold | true |
Make the property changes shown in Table 7.7 to the GroupBox controls.
Control | Property | Value |
---|---|---|
GroupBox1 | Name | grpScoring |
Text | Scoring: | |
GroupBox2 | Name | grpOutput |
Text | Game Status: |
Make the property changes shown in Table 7.8 to the TextBox control.
Property | Value |
---|---|
Name | txtOutput |
Multiline | true |
ReadOnly | true |
Change the Name property for the Timer control to tmrRoll, and change the Name property of the ImageList control toimlDliceList. At this point, all that remains is to load the images representing each side of a die into the ImageList control's Images property (Collection), as outlined in Table 7.9. You can find copies of each of the bitmap image files along with the source code for this chapter's game project at this book's companion Web site, located at http://www.courseptr.com. The dice images were created using Adobe Photoshop and saved as 24-bit color images.
Property | File | Index No. |
---|---|---|
Name | 1.bmp | 0 |
Name | 2.bmp | 1 |
Name | 3.bmp | 2 |
Name | 4.bmp | 3 |
Name | 5.bmp | 4 |
Name | 6.bmp | 5 |
Trick | Afteryou have Loaded the application's bitmap images into the ImageList control, you can programmatically reference those images, as demonstrated next.
PbxDiel.Image = imlDiceList.Images(3) In this example, the fourth bitmap image stored in the ImageList control named imlDiceList is loaded into a PictureBox control named pbxDie1 using the PictureBox control's Image property. |
Trick | You don't have to worry about packaging and distributing the six bitmap images of the game's dice. When you add the .bmp files to the Images property of the ImageList control, Visual C++ automatically saves copies of the bitmap files inside your application's binary file. |
The first task to perform in putting together the program code for the Dice Poker game is to define class (or module)-level constants and variables, as shown in bold in the following code:
private: /// <summary> /// Required designer variable /// </summary> //Game timer counter Int32 intTimerCntr; //Handle for random number generator Random^ randNumGen; //Used to control display of checkbox controls Boolean blnExcludeList; //Number of dollars in player's account Int32 intTotalDollars; Int32 intNoOfRolls; //Tracks the number of die rolls Int32 intDice1; //Number 1 die Int32 intDice2; //Number 2 die Int32 intDice3; //Number 3 die Int32 intDice4; //Number 4 die Int32 intDice5; //Number 5 die
Next, access the Load event procedure for the form and add the statements shown here:
private: System::Void frmMain_Load(System::Object^ sender, \ System::EventArgs^ e) { //Seed random number generator with current time DateTime moment = DateTime::Now; randNumGen = gcnew Random( moment.Millisecond ); // Set starting game variables pbxDie1->Image = imlDiceList->Images[0]; pbxDie2->Image = imlDiceList->Images[0]; pbxDie3->Image = imlDiceList->Images[0]; pbxDie4->Image = imlDiceList->Images[0]; pbxDie5->Image = imlDiceList->Images[0]; blnExcludeList = false; intTimerCntr = 0; //Set starting money intTotalDollars = 20; //Set beginning text txtOutput->Text = \ String: :Concat( "Welcome! Are you ready " "to play Dice Poker?\r\r\nYou have ", intTotalDollars.ToString(), " dollars in your account." ); }
The first two statements prime the game to generate random numbers, first getting the time, and then using that value to drive the Random method. The next five statements load an image of the die into the five PictureBox controls. Next, the variable controlling whether dice are excluded from being rerolled is set, followed by a clearing of the variable that track the timer. The player's starting money is also initialized. Finally, a welcome message is displayed in the txtOutput TextBox control. This message is created by joining strings using the String::Concat method, which allows numbers to be inserted into a text string.
Next, modify the click event procedure for the form's first Button control, as shown here:
private: System::Void btnRollDice_Click(System::Object^ \ sender, System::EventArgs^ e) { //See if the die have been rolled twice if( intNoOfRolls == 2 ) { //Change button display text btnRollDice->Text = "Roll Dice"; //Reset to 0 to get game ready for new hand IntNoOfRolls = 0; } //If the first roll has been made, toggle the display of the //CheckBox control and keep track of the number of rolls if( btnRollDice->Text == "Roll Dice" ) { blnExcludeList = false; intNoOfRolls += 1; } else { blnExcludeList = true; intNoOfRolls += 1; } //Start the Timer control tmrRoll->Enabled = true; }
These statements use a variable named intNoOfRolls to keep track of whether the player is about to make the first or second roll. If the value of intNoOfRolls is equal to 2, the text string Roll Dice is displayed on the first Button control, and the value of intNoOfRolls is reset to 0. The following if...else statement checks to see whether a Boolean type variable named blnExcludeList should be set to true or false based on the text displayed on the first Button control and increments the value of intNoOfRolls by 1. The btnExcludeList variable is used later in the application to determine whether to display the CheckBox controls that allow the player to hold on to dice before making the second roll. The last statement begins the dice rolling process by enabling the Timer control (tmrRoll).
Next, create the following subroutine procedure, as shown next. Visual C++ doesn't create this procedure for you. You have to create it from scratch:
private: Void RollTheDice( Int32 intDiceNo ) { //Stores randomly generated number representing the role of a die Int32 intRoll; //These variables are used to track which die the player chooses //to hold onto over his first roll Boolean blnSkipCase1 = false, blnSkipCase2 = false, blnSkipCase3 = false, blnSkipCase4 = false, blnSkipCase5 = false; //Flag die the player wants to hold if( blnExcludeList = true ) { if( chkDie1->Checked == true ) blnSkipCase1 = true; if( chkDie2->Checked == true ) blnSkipCase2 = true; if( chkDie3->Checked == true ) blnSkipCase3 = true; if( chkDie4->Checked == true ) blnSkipCase4 = true; if( chkDie5->Checked == true ) blnSkipCase5 = true; } //Simulate a six-sided die intRoll = randNumGen->Next( 6 ) + 1; //For each die number, display the correct graphic switch( intDiceNo ) { case 1 : //Update image for the first die as it spins //Player elected not to hold if( blnSkipCase1 == false ) { pbxDie1->Image = imlDiceList->Images[intRoll - 1]; intDice1 = intRoll; } break; case 2 : //Update image for the second die as it spins //Player elected not to hold if( blnSkipCase2 == false ) { pbxDie2->Image = imlDiceList->Images[intRoll - 1]; intDice2 = intRoll; } break; case 3 : //Update image for the third die as it spins //Player elected not to hold if( blnSkipCase3 == false ) { pbxDie3->Image = imlDiceList->Images[intRoll - 1]; intDice3 = intRoll; } break; case 4 : //Update image for the fourth die as it spins //Player elected not to hold if( blnSkipCase4 == false ) { pbxDie4->Image = imlDiceList->Images[intRoll - 1]; intDice4 = intRoll; } break; case 5 : //Update image for the fifth die as it spins //Player elected not to hold if( blnSkipCase5 == false ) { pbxDie5->Image = imlDiceList->Images[intRoll - 1]; intDice5 = intRoll; } break; } }
Note that when this function is called, it is passed a variable identifying which die (1 through 6) is being rolled. It stores this in a variable of its own named intDiceNo. The function begins by defining local variables needed for the function's execution.
An if statement that contains five embedded if statements is then executed. All of these statements execute only if the value assigned to blnExcludeList is equal to true, indicating that the player has already taken the first roll. Each of the nested if statements checks to see if the player has elected to hold on to any of the dice from the first roll (by selecting the CheckBox control for the associated die).
Next, the randNumGen object's Next method is executed. This causes a new random number with a value between 1 and 6 to be created. This number is then assigned to a variable named intRoll. A switch statement associates the randomly generated number to the die passed to the function (represented by the variable named intDiceNo). For example, if the randomly generated number is 2, the program statements associated with case 2 : are executed.
Or if the number is 3, the statements associated with case 3 : are executed. After each case statement, an if statement determines whether the die in question should be held. If it should, the appropriate image is assigned to the correct PictureBox control using intRoll. Note that because intRoll reflects the die value, which is 1 through 6, and the image list runs from 0 through 5, 1 must be subtracted from the array index.
Next, access the Tick event procedure for the Timer1 control and modify it as shown here:
private: System::Void tmrRoll_Tick(System::Object^ sender, \ System::EventArgs^ e) { //Set amount of time to show dice rolling const Int32 cintRollPeriod = 5; //Roll each die for( Int32 intDiceCntr = 1; intDiceCntr <= 5; intDiceCntr++ ) RollTheDice( intDiceCntr ); intTimerCntr += 1; //Increment counter by one //Disable timer and display CheckBox controls at the // end of each roll if( intTimerCntr > cintRollPeriod ) { intTimerCntr = 0; //Reset timer counter //Disable timer control tmrRoll->Enabled = false; if( intNoOfRolls == 1 ) { //Prepare game for second roll btnRollDice->Text = "Roll Again"; //Enable CheckBox controls chkDie1->Visible = true; chkDie2->Visible = true; chkDie3->Visible = true; chkDie4->Visible = true; chkDie5->Visible = true; chkKeepAll->Visible = true; } if( intNoOfRolls == 2 ) { //Prepare game for a new hand btnRollDice-XText = "Roll Dice"; chkDie1->Checked = false; //Disable CheckBox controls chkDie2->Checked = false; chkOie3->Checked = false; chkDie4->Checked = false; chkDle5->Checked = false; chkKeepAll->Checked = false; chkDie1->Visible = false; //Hide CheckBox controls chkDie2->Visible = false; chkDie3->Visible = false; chkDie4->Visible = false; chkDie5->Visible = false; chkKeepAll->Visible = false; //Keeps track of score Total TheScore(); //Display the player's account status UpdateAccountStatus(); //See if player has gone broke if( intTotalDollars <= 0 ) //End the game EndOfGame(); } } }
This function begins by declaring a variable that will control the amount of time that the dice roll when the timer is activated. Because this is a fixed value that will not change, the variable is declared constant. Next, a loop iterates once for each die, calling the RollTheDice function and passing it intDiceCntr. Then the variable intTimerCntr is incremented by 1. This ultimately governs how long the Timer control is enabled.
An if block is executed to determine whether it's time to disable the Timer control. In addition, two embedded if blocks execute, based on whether the player has made his first roll. This enables or disables the display of the CheckBox controls.
Next, the function calls on the TotalTheScore and UpdateAccountStatus functions before checking the number of dollars in the player's account. If intTotalDollars is less than or equal to 0, the function EndOfGame is called.
The next function that you'll work on is the TotalTheScore function. This is another function that you must create from scratch, as shown here:
private: Void TotalTheScore( Void ) { //Declare array to be used to keep count of number // in each hand const Int32 intMaxNoDie = 7; Int32 intDieArray[intMaxNoDie]; //Declare variable used to control loop execution Int32 intCounter = 1; //Clear the dice array for( intCounter = 0: intCounter < intMaxNoDie; intCounter++ ) intDieArray[intCounter] = 0; //Iterate six times and keep count of the total number of //1s, 2s, 3s, 4s, 5s, and 6s that has been rolled for( intCounter = 1; intCounter <- 6; intCounter++ ) { if( intDicel == intCounter ) intDieArray[intCounter] += 1; if( intDice2 == intCounter ) intDieArray[intCounter] += 1; if( intDice3 == intCounter ) intDieArray[intCounter] += 1; if( intDice4 == intCounter ) intDieArray[intCounter] += 1; if( intDice5 == intCounter ) intDieArray[intCounter] += 1; } //Iterate six times looking for winning hands for( intCounter = 1; intCounter <= 6; intCounter++ ) { //See if the player has 5 of a kind if( intDieArray[intCounter] =- 5 ) { //Update player's account intTotalDollars += 4; txtOutput->Text = "\r\r\nWinner: 5 of a kind!" " You win 4 dollars!"; return; } //See if the player has 4 of a kind if( intDieArray[intCounter] == 4 ) { //Update player's account IntTotalDollars += 3; txtOutput->Text = "\r\r\nWinner: 4 of a kind!" " You win 3 dollars."; return; } //See if the player has 3 of a kind or a full house if( intDieArray[intCounter] == 3 ) { //Player has 3 of a kind for( Int32 intCounter2 = 1; intCounter2 <= 6; intCounter2++ ) { //See if player has a full house if( intDleArray[intCounter2] — 2 ) { //Update player's account intTotalDollars += 2; txtOutput->Text = "\r\r\nWinner:" " Full house! You win 2 dollars."; return; } } intTotalDollars += 1; //Update player's account txtOutput->Text = "\r\r\nWinner: 3 of a " "kind! You win 1 dollar."; return; } } //Iterate through die 2 - 6 looking for a high straight for( intCounter = 2; intCounter <= 6; intCounter++ ) { if( intDieArray[intCounter] != 1 ) break; //No need to keep looking any further else { if( intCounter == 6 ) { //Update player's account intTotalDollars += 3; txtOutput->Text = "\r\r\nWinner: " "High Straight! You win 3 dollars."; return; } } } //Iterate through die 1 - 5 looking for a low straight for( intCounter = 1; intCounter <= 5; intCounter++ ) { if( intDieArray[intCounter] != 1 ) break; //No need to keep looking any further else { if( intCounter == 5 ) { //Update player's account intTotalDollars += 3; txtOutput->Text = "\r\r\nWinner: Low" " Straight! You win 3 dollars."; return; } } } //Update player's account intTotalDollars -= 2; txtOutput->Text = "\r\r\nSorry, You lost this " "hand! You lose 2 dollars."; }
This function begins by defining an array that will store the total number of 1s, 2s, 3s, 4s, 5s, and 6s in the player's hand. Then a local variable named intCounter is set up for use as a counter in the for statement that follows. All elements in the array are then set to 0 using a for loop to ensure that they are not filled with some random value. The for loop is then again used to tally up how many of each die the player has.
Next, another for loop executes a series of embedded if statements. The first if code block checks to see if the player's hand is 5 of a kind by examining the value stored in each element of the intDieArray. The second if code block looks for 4 of a kind, and the third if block first looks for 3 of a kind. If the player does have 3 of a kind, a second for loop is executed to see if the player has a full house.
Trick | If you look closely at the code in the TotalTheScore function, you see that even though the intDieArray has six elements, the function does not store data in the first element of the array. I did this to simplify things by pairing up the index position I in the array with the die value of 1, the index position of 2 in the array with the die value of 2, and so on. Then, when I set up the function's for loops, I made sure that I specified the correct starting and ending index numbers so that the loops never processed the array's first index value (for example, intDieArray[0]). |
If the player does not have 3, 4, or 5 of a kind, the function checks to see if the player has a high straight (for example, 2-6) using another for loop to iterate through each of the values stored in the intDieArray. If the player does not have a high straight, another for loop is set up to look for a low straight. If the player doesn't have a winning hand, $2 is subtracted from his account.
The next function that you'll work on is EndOfGame. Again, you'll need to create this function from scratch, as shown here:
private: Void EndOfGame( Void ) { //Variable used to hold player response Windows::Forms::DialogResult drPlayAgain; //Clear out any status messages txtOutput->Text = ""; //Prompt player to try again drPlayAgain = MessageBox::Show( "Sorry, you are " "broke. The game is over. Would you like to " "play again?", "Dice Poker", MessageBoxButtons::YesNo, MessageBoxIcon::Question, MessageBoxDefaultButton::Button1 ); //if( player clicks on Yes set up a new game if( drPlayAgain == Windows::Forms::DialogResult::Yes ) { //Reset the bank account intTotalDollars = 20; pbxDie1->Image = imlDiceList->Images[0]; pbxDie2->Image = imlDiceList->Images[1]; pbxDie3->Image = imlDiceList->Images[2]; pbxDie4->Image = imlDiceList->Images[3]; pbxDie5->Image = imlDiceList->Images[4]; //Set beginning text txtOutput->Text = \ String::Concat( "Welcome! Are you ready " "to play Dice Poker?\r\r\nYou have ", intTotalDollars.ToString(), " dollars in your account." ); } else //player wants to quit this->Close(); }
This function begins by declaring a variable, drPlayAgain, to hold the player's response when he's asked to playa new game. The player is prompted to play again using the MessageBox::Show method. If the player clicks Yes, his bank account is reset to $20, and die images are loaded into each of the PictureBox controls. If the player clicks No, the application is closed.
The next function that you'll work on is the UpdateAccountStatus function, which you have to create from scratch, as shown here:
private: Void UpdateAccountStatus( Void ) { txtOutput->Text = \ String::Concat( txtOutput->Text, "\r\r\nYou have ", intTotalDollars.ToString(), " dollars in your account." ); }
This function is called from the Timer1_Tick function to append a string of text, showing the player's account balance, to the text displayed in the txtOutput TextBox control. It does this using the String::Concat method, which allows numbers to be inserted into strings.
Next, access the click event function for the game's Exit button, and modify it as shown here:
private: System::Void btnExit_Click(System::Object^ sender,\ System::EventArgs^ e) { this->Close(); }
Finally, access the CheckedChange event function for the chkKeepAll CheckBox control, and modify it as shown here:
private: System::Void chkKeepAll_CheckedChanged(System::Object^\ sender, System::EventArgs^ e) { //if( the player selects the CheckBox // labeled KeepAll, select the other // CheckBox controls if( chkKeepAll->Checked = true ) { btnRollDice->Text = "Stick"; chkDie1->Checked = true; chkDie2->Checked = true; chkDie3->Checked = true; chkDie4->Checked = true; chkDie5->Checked = true; } else { //if( the player clears the CheckBox // labeled KeepAll. clear the other // CheckBox controls btnRollDice->Text = "Roll Again"; chkDie1->Checked = false; chkDie2->Checked = false; chkDie3->Checked = false; chkDie4->Checked = false; chkDie5->Checked = false; } }
This function enables the player to hold onto all of the dice from the first roll, which makes sense when the player gets a high or low straight or 5 of a kind on the first roll. The function also changes the text display on the first Button control to either Stick or Roll Again, based on whether the player selected the chkKeepAll button.
The Dice Poker game is now ready to run. Press F5 to make sure that everything works as it is supposed to. If you run into any problems, go back and double-check your typing. Once the game is working, try it out a bit and see how much virtual money you can make.