It's time to turn your attention back to the development of this chapter's game project, the Rock, Paper and Scissors 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 Rock, Paper and Scissors game is played on a single window and is made up of one form and the 22 controls listed in Table 9.1.
Control Type | Control Name | Description |
---|---|---|
PictureBox | pcbLeft | Displays an image showing the computer's selection |
PictureBox | pcbRight | Displays an image showing the player's selection |
Label | lblComputer | Identifies the PictureBox control where the computer's selection is displayed |
Label | lblPlayer | Identifies the PictureBox control where the player's selection is displayed |
Label | lblCountDown | Identifies the function of the ProgressBar control |
ProgressBar | pgbCountDown | Displays a graphical 1.5-second countdown |
Button | btnPlay | Starts gameplay by enabling the Timer control |
Button | btnRock | Allows the player to select Rock |
Button | btnPaper | Allows the player to select Paper |
Button | btnScissors | Allows the player to select Scissors |
GroupBox | grpStatus | Contains Label and TextBox controls that identify and display game statistics |
Label | lblWins | Identifies the TextBox control that displays the number of games won |
Label | lblLosses | Identifies the TextBox control that displays the number of games lost |
Label | lblTies | Identifies the TextBox control that displays the number of games tied |
TextBox | txtWins | Displays the number of games the player has won |
TextBox | txtLosses | Displays the number of games the player has lost |
TextBox | txtTies | Displays the number of games the player has tied |
Label | lblResults | Identifies the TextBox control where the results of each game are displayed |
TextBox | lblOutput | Displays a test string detailing the results of each game |
ToolTip | tltControl | Allows you to assign ToolTips to individual controls |
Timer | tmrControl | Controls the 1.5-second countdown sequence and the half-second windows in which the player gets to make a selection |
ImageList | imlHands | Stores the bitmap images that are displayed in the PictureBox controls during gameplay |
The first step in creating the Rock, Paper and Scissors 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, New Project. The New Project dialog box appears.
Select Windows Application template.
Type Rock Paper and Scissors as the name of your new application in the Name field located at the bottom of the New Project window. You need to type the name of the project without a comma, or Visual C++ might not recognize some of the project's file names during compiling.
Click on OK to close the New Project dialog box.
Visual C++ now creates a new project and displays a new form upon which you will design the game's user interface.
Let's begin the development of the Rock, Paper and Scissors game by laying out and resizing all the controls that you need to set up the game's user interface. As you go about configuring the game's user interface, refer to Figure 9.6 so that you know where to place each control and when and how to resize it.
Begin by clicking on the form and setting its Size property to 490, 460.
Add two PictureBox controls, one in the upper-left corner of the form and the other in the upper-right corner. Set the Size property for both PictureBox controls to 150, 150.
Next, add a pair of Label controls to the form. Place the first one under the first PictureBox control and the second one under the second PictureBox control.
Add another Label control about an inch below the left side of the first PictureBox control, and then add a ProgressBar control beneath it, setting its Size property to 250, 40.
Add four Button controls to the form.
Add a GroupBox control to the lower-left corner of the form and set its Size property to 190, 100.
Add three Label controls and three TextBox controls inside the GroupBox control.
Add another Label control just to the right of the GroupBox control and then add a TextBox control beneath it. Set the Multiline property for the TextBox control to true and the Size property to 260, 80.
Finally, add ToolTip, Timer, and ImageList controls.
Figure 9.6: Completing the interface design for the Rock, Paper and Scissors game.
At this point, you have added all the controls you will need to develop the Rock, Paper and Scissors game. In addition, you have completed the overall layout of the game's user interface and are ready to start making property modifications to the game's form and controls.
Okay, let's get to work on making the required changes to the game's form and control properties, as listed in Table 9.2.
Property | Value |
---|---|
BackColor | LightYellow |
FormBorderStyle | Fixed3D |
MaximizeBox | False |
MinimizeBox | False |
Size | 490, 460 |
StartPosition | CenterScreen |
Text | Rock, Paper and Scissors |
Make the property changes shown in Table 9.3 to the PictureBox controls.
Control | Property | Value |
---|---|---|
PictureBox1 | Name | pcbleft |
BorderStyle | Fixed3D | |
Size | 150, 150 | |
SizeMode | StretchImage | |
PictureBox2 | Name | pcbRight |
BorderStyle | Fixed3D | |
Size | 150, 150 | |
SizeMode | StretchImage |
Make the property changes shown in Table 9.4 to the Label controls.
Control | Property | Value |
---|---|---|
Label 1 | Name | lblComputer |
Font.Bold | True | |
Text | Computer's Pick | |
Label 2 | Name | lblPlayer |
Font.Bold | True | |
Text | Player's Pick | |
Label 3 | Name | lblCountDown |
Font.Bold | True | |
Text | Countdown | |
Label 4 | Name | lblWins |
Font.Bold | True | |
Text | Wins: | |
Label 5 | Name | lblLosses |
Font.Bold | True | |
Text | Losses: | |
Label 6 | Name | lblTies |
Font.Bold | True | |
Text | Ties: | |
Label 7 | Name | lblResults |
Font.Bold | True | |
Text | Game Results |
Next, change the name of the ProgressBar control to pgbCountDown and set its Size property to 250, 40. Then make the property changes shown in Table 9.5 to the four Button controls.
Control | Property | Value |
---|---|---|
Button1 | Name | btnPlay |
BackColor | ButtonFace | |
Font.Bold | True | |
Text | Play | |
ToolTip | Begin 1.5 second countdown | |
Button2 | Name | btnRock |
BackColor | ButtonFace | |
Enabled | False | |
Font.Bold | True | |
Text | Rock | |
ToolTip | Select Rock | |
Button3 | Name | btnPaper |
BackColor | ButtonFace | |
Enabled | False | |
Font.Bold | True | |
Text | Paper | |
ToolTip | Select Paper | |
Button4 | Name | btnScissors |
BackColor | ButtonFace | |
Enabled | False | |
Font.Bold | True | |
Text | Scissors | |
ToolTip | Select Scissors |
Trick | The ProgressBar control displays the status of a process. After you've added this control to a form, you can update its appearance by settings its Value property to 0 to represent a process that has not started. You can then set the Value property anywhere between 0 and 100 to represent the current status of a process. Finally, you can set the Value property to 100 to represent a process that has completed. |
Make the property changes shown in Table 9.6 to the four TextBox controls.
Control | Property | Value |
---|---|---|
TextBox 1 | Name | txtWins |
Font.Bold | True | |
ReadOnly | True | |
TextAlign | Right | |
TextBox2 | Name | txtLosses |
Font.Bold | True | |
ReadOnly | True | |
TextAlign | Right | |
TextBox3 | Name | txtTies |
Font.Bold | True | |
ReadOnly | True | |
TextAlign | Right | |
TextBox4 | Name | txtoutput |
Font.Bold | True | |
ReadOnly | True | |
Size | 260, 80 |
Next, change the name of the ToolTip control to tltControl. Then make the property changes shown in Table 9.7 to the Timer control.
Control | Property | Value |
---|---|---|
Timer1 | Name | tmrControl |
Interval | 500 |
Finally, change the name of the Name property for the ImageList control to imlHands and add the following bitmap images to its Images property (col1ection) as shown in Table 9.8. You'll find copies of the bitmap images required to complete this project, along with the source code, on this book's companion Web site.
Property | File | Index No. |
---|---|---|
Name | Left-Paper.bmp | 0 |
Name | Right-Paper.bmp | 1 |
Name | Left-Rock.bmp | 2 |
Name | Right-Rock.bmp | 3 |
Name | Left-Scissors.bmp | 4 |
Name | Right-Scissors.bmp | 5 |
Name | Blank.bmp | 6 |
That's it. The user interface for the Rock, Paper and Scissors game is now complete. Let's move on to the next step and begin adding program code.
To put together the code for the Rock, Paper and Scissors game, start by double-clicking on the game's form. This activates the Code Editor. In response, Visual C++ automatically defines a new class for you called frmMain (or whatever name you have assigned to Form1). This derived class inherits properties, methods, and events that are associated with the System.Windows.Forms base class.
Your class requires the following member variables:
private: /// <summary> /// Required designer variable. /// </summary> Int32 intCounter; Int32 intWins; Int32 intLosses; Int32 intTies; enum class GameChoice : Int16 { CHOICEROCK, CHOICEPAPER, CHOICESCISSORS }; //Handle for random number generator Random^ randNumGen;
The first variable, intCounter, tracks the timer and implements the 1.5-second countdown. The next three variables—intWIns, intLosses, and intTies—keep track of whether the computer or the player is winning. Notice also the enum class GameChoice, which defines three possible game choices. GameChoice derives from the Int16 class and exists only to create meaningful state information for some functions to operate on. This section also includes a handle to a random number generator, randNumGen, which gives the computer the illusion of making a choice.
The Rock, Paper and Scissors game needs to execute a few tasks when it first loads. It must initialize its member variables. Then it needs to load a blank bitmap for each of the two PictureBox controls. Following that, the game's text boxes are initialized, and the random number generator is seeded with the current time:
private: System::Void Form1_Load(System::Object^ sender, \ System::EventArgs^ e) { intCounter = 0; intWins = 0; intLosses = 0; intTies = 0; //Load blank bitmap in picture boxes pcbLeft->Image = imlHands->Images[6]; pcbRight->Image = imlHands->Images[6]; //Set game statistics txtWins->Text = intWins.ToString(); txtLosses->Text = intLosses.ToString(); txtTies->Text = intTies.ToString(); //Seed random number generator with current time DateTime moment = DateTime::Now; randNumGen = gcnew Random( moment.Millisecond ); }
The game starts when the player clicks the Play button. This begins the game's 1.5-second countdown by setting the Value property of the ProgressBar control to 0 and enabling the Timer control. Any leftover text in the text box is cleared, and the two picture boxes are again loaded with black images to clear out the pictures from the previous game:
private: System::Void btnPlay_Click(System::Object^ sender, \ System::EventArgs^ e) { //Set ProgressBar to show 0 percent progress pgbCountdown->value = 0; //Start countdown by enabling the Timer tmrControl->Enabled = true; //Clear out any test stored in the text box txtOutput->Text = ""; //Load blank bitmap in picture boxes pcbLeft->Image = imlHands->Images[6]; pcbRight->Image = imlHands->Images[6]; }
The Timer control handles timing for the game. Because its Interval property is set to 500, the code in this function executes every 0.5 seconds. Each time it does, the counter is incremented by 1. When the counter exceeds 4, the game is over.
The Timer control also updates the progress bar each time it is called.
The tmrControl_Tick function, shown next, uses a switch block to control what happens at certain intervals. When the first half-second has passed, the value of intCounter is 1, and the ProgressBar control's Value property is set to 33 to give the player a visual indicator that a third of the countdown has elapsed. After the passage of a full second, the second case statement is triggered, and the ProgressBar control's Value property is updated to show that two-thirds of the countdown has elapsed:
private: System::Void tmrControl_Tick(System::Object^ \ sender, System::EventArgs^ e) { intCounter += 1; //Increment counter //Set progress bar based on counter switch( intCounter ) { case 1 : //At .5 seconds //Set progress bar to show 33% pgbCountdown->value = 33; break; case 2 : //At 1 second //Set progress bar to show 66% pgbCountdown->Value = 66; break; case 3 : //At 1.5 seconds //Set progress bar to show 100% pgbCountdown->Value = 100; //Enable the 3 game choice buttons btnRock->Enabled = true; btnPaper->Enabled = true; btnScissors->Enabled = true; break; case 4 : //At 2 seconds intCounter = 0; //Reset counter //Disable the Timer control tmrControl->Enabled = false; //Disable the 3 game choice buttons btnRock->Enabled = false; btnPaper->Enabled = false; btnScissors->Enabled = false; //Set ProgressBar to show 0% pgbCountdown->Value = 0; break; } }
At 1.5 seconds, the ProgressBar control's Value property is updated to indicate that the countdown is complete. When this occurs, the buttons representing the rock, paper, and scissors options are enabled, and the player has a half-second to decide which button to click. But should the half-second interval elapse, execution reaches the final case statement. This causes the game's buttons to be disabled, the progress bar to be reset, and the timer to be disabled.
The next listing shows the code for the Rock Button control. When clicked, the right picture box, pcbRight, is loaded with the appropriate image. An instance of the GameChoice enum class is created and initialized with the player's choice. Notice that instead of a number, which might be hard to understand, or a string, which would be wasteful, the use of an enum makes the code clearer. It is easy to understand that the player's choice is rock because of how the assignment to GameChoice::CHOICEROCK reads.
After the player's choice is instantiated, another GameChoice object is created to represent the computer's choice. This is assigned the results of the GetComputerChoice function. When the computer's choice is known, both are used as parameters in the CheckGameResults function:
private: System::Void btnRock_Click(System::Object^ sender,\ System::EventArgs^ e) { //Load rock picture into right picture box pcbRight->Image = imlHands->Images[3]; //Set the game choices for both players GameChoice gmcPlayerPick = GameChoice::CHOICEROCK; //Get computer choice GameChoice gmcComputerPick; gmcComputerPick = GetComputerChoice(); //Run function that determines who has won the game CheckGameResults(gmcPlayerPick, gmcComputerPick); }
The click event function for the Button control representing the paper option is shown next. The code is nearly identical, except all references reflect the player's choice of paper as his move instead of rock:
private: System::Void btnPaper_Click(System::Object^ sender,\ System::EventArgs^ e) { //Load paper picture into right picture box pcbRight->Image = imlHands->Images[1]; //Set the game choices for both players GameChoice gmcPlayerPick = GameChoice::CHOICEPAPER; //Get computer choice GameChoice gmcComputerPick; gmcComputerPick = GetComputerChoice(); //Run function that determines who has won the game CheckGameResults(gmcPlayerPick, gmcComputerPick); }
The code for the Button control representing the scissors choice is shown next. Again, it varies only slightly from the two previous functions:
private: System::Void btnScissors_Click(System::Object^ sender,\ System::EventArgs^ e) { //Load scissors picture into right picture box pcbRight->Image = imlHands->Images[5]; //Set the game choices for both players GameChoice gmcPlayerPick = GameChoice::CHOICESCISSORS; //Get computer choice GameChoice gmcComputerPick; gmcComputerPick = GetComputerChoice(); //Run function that determines who has won the game CheckGameResults(gmcPlayerPick, gmcComputerPick); }
The GetComputerTurn() function, shown next, is a custom-developed function that you must type from scratch. Within the function, the randNumGen object serves as an argument to a switch statement. The Next() method of randNumGen generates a number between (but not including) the upper and lower bounds. If the number falls between 1 and 2, the value that the method returns matches one of the two case labels. Within each label, the appropriate graphic is set for the computer's choice, and the correct enumerated value is returned. If neither is met, the function assumes that the only logical choice is paper and performs the appropriate steps:
private: GameChoice GetComputerChoice(System::Void) { //Get the computer's random choice switch( randNumGen->Next(0, 4) ) { case 1 : //Computer chooses rock //Load picture of rock into left box pcbLeft->Image = imlHands->Images[2]; //Return computer's choice return GameChoice::CHOICEROCK; break; case 2 : //Computer chooses scissors //Load picture of scissors into left box pcbLeft->Image = imlHands->Images[4]; //Return computer's choice return GameChoice::CHOICESCISSORS; break; } //Only possible remaining choice is paper //Load picture of paper into left box pcbLeft->Image = imlHands->Images[0]; //Return computer's choice return GameChoice::CHOICEPAPER; }
The logic within the Check Game Results () function is responsible for arbitrating between the computer and player moves. Each player's choice is used as the basis of comparison and causes execution to seek the nearest matching case label. Within each case block, three if statements determine how the player's choice stacks up against the computer's choice. The result is assigned to the Text property of txtOutput, the appropriate variable tracking the player's score is updated, and the results are translated to a string so that they can be stored in the correct corresponding text box:
private: System::Void CheckGameResults(GameChoice gmcPlayer,\ GameChoice gmcComputer ) { //Check winner by comparing moves switch( gmcPlayer ) { case GameChoice::CHOICEROCK : if( gmcComputer == GameChoice::CHOICEROCK ) { //Display results txtOutput->Text = \ "Rock versus Rock is a Tie!"; intTies++; txtTies->Text = intTies.ToString(); } if( gmcComputer == GameChoice::CHOICEPAPER ) { //Display results txtOutput->Text = \ "Paper covers Rock. You Lose!"; intLosses++; txtLosses->Text = intLosses.ToString(); } if( gmcComputer == GameChoice::CHOICESCISSORS ) { //Display results txtOutput->Text = \ "Rock crushes Scissors. You Win!"; intWins++; txtWins->Text = intLosses.ToString(); } break; case GameChoice::CHOICEPAPER : if( gmcComputer == GameChoice::CHOICEROCK ) { //Display results txtOutput->Text = \ "Paper covers Rock. You Win!"; intWins++; txtWins->Text = intWins.ToString(); } if( gmcComputer == GameChoice::CHOICEPAPER ) { //Display results txtOutput->Text = \ "Paper versus Paper is a Tie!"; intTies++; txtTies->Text = intTies.ToString(); } if( gmcComputer == GameChoice::CHOICESCISSORS ) { //Display results txtOutput->Text = \ "Scissors cuts Paper. You Lose!"; intLosses++; txtLosses->Text = intLosses.ToString(); } break; case GameChoice::CHOICESCISSORS : if( gmcComputer == GameChoice::CHOICEROCK ) { //Display results txtOutput->Text = \ "Rock breaks Scissors. You Lose!"; intLosses++; txtLosses->Text = intLosses.ToString(); } if( gmcComputer == GameChoice::CHOICEPAPER ) { //Display results txtOutput->Text = \ "Scissors cuts Paper. You Win!"; intWins++; txtWins->Text = intWins.ToString(); } if( gmcComputer == GameChoice::CHOICESCISSORS ) { //Display results txtOutput->Text = \ "Scissors versus Scissors is a Tie!"; intTies++; txtTies->Text = intTies.ToString(); } break; } }
The Rock, Paper and Scissors game is ready to run. Start the game by pressing F5 and test the code to make certain it works as it should. If you run into problems, be sure to double-check your typing.