Now it is time to turn your attention back to this chapter's game project, the Hangman game. The Hangman game is based on a children's game in which the player attempts to guess a secret word. If the player guesses the word before making six incorrect guesses, the player wins. Otherwise, the game is lost.
You will create the Hangman game by following the same five basic development steps that you have followed when completing previous chapter projects.
The Hangman game will be played within a single window. Therefore, it will be made up of one form and the 25 controls listed in Table 8.1.
Control Type | Control Name | Description |
---|---|---|
Panel1 | pnlLetters | A Panel control used to group Textbox controls that will be used to display guessed word letters |
TextBox1 | txtLetter1 | Displays the first letter in the game's secret word |
TextBox2 | txtLetter2 | Displays the second letter in the game's secret word |
TextBox3 | txtLetter3 | Displays the third letter in the game's secret word |
TextBox4 | txtLetter4 | Displays the fourth letter in the game's secret word |
Textbox5 | txtLetter5 | Displays the fifth letter in the game's secret word |
TextBox6 | txtLetter6 | Displays the sixth letter in the game's secret word |
TextBox7 | txtLetter7 | Displays the seventh letter in the game's secret word |
TextBox8 | txtLetter8 | Displays the eighth letter in the game's secret word |
TextBox9 | txtLetter9 | Displays the ninth letter in the game's secret word |
TextBox10 | txtLetter10 | Displays the tenth letter in the game's secret word |
TextBox11 | txtMisses | Displays the number of misses that the player has made |
TextBox12 | txtGuesses | Displays the letters that the player has already guessed (both right and wrong) |
TextBox13 | txtGamesWon | Displays the number of games that the player has won |
TextBox14 | txtGamesLost | Displays the number of games that the player has lost |
GroupBox1 | grpStatus | Contains Label and TextBox controls that display game statistics |
Label1 | lblInput | Identifies the TextBox control where the player enters a letter guess |
Label2 | lblNoMissed | Identifies the TextBox control that displays the number of misses that the player has made |
Labe13 | lblGuessed | Identifies the TextBox control that displays the letters that the player has already guessed (both right and wrong) |
Labe14 | lblGamesWon | Identifies the TextBox control that displays the number of games that the player has won |
Labe15 | lblGamesLost | Identifies the TextBox control that displays the number of games that the player has lost |
Button1 | btnsubmit | Executes code that checks to see if the player has guessed a letter in the secret word |
PictureBox1 | pbcHangmanBmp | Displays a graphic that visually shows how many letter guesses the player has missed |
ImageList1 | imlHangmanBmps | Stores an indexed collection of the hangman images that are displayed in the PictureBox control |
The first step in creating the Hangman game is to open up Visual Basic and create a new project as outlined below.
If you have not already done so, start up Visual Basic 2005 Express and then click on File and select New Project. The New Project dialog will appear.
Select Windows Application template.
Type Hangman as the name of your new application in the Name field located at the bottom of the New Project window.
Click on OK to close the New Project dialog.
Visual Basic will now create a new project for you and display a new form upon which you will design the game's user interface.
Now let's begin work on laying out the game's user interface by adding controls to the form and moving and resizing them in the appropriate locations. Refer to Figure 8.14 as you go through each step so that you'll know how to resize and place the controls.
Figure 8.14: Examining the Layout of the Hangman game's user interface.
Begin by clicking on the form and setting its Size property to 655, 412.
Add a Panel control to the form and resize as shown in Figure 8.14.
Add 10 TextBox controls inside the Panel control. Set the Multiline property for each TextBox control equal to True and resize them as shown in Figure 8.14.
A Panel control is a control that stores and organizes other controls.
Add a Label control to the form. By default, Visual Basic will name it Label1.
Just to the right of the Label1 control, add a TextBox control and a Button control as shown in Figure 8.14. Set the Multiline property of the TextBox control equal to True.
Add a GroupBox control at the bottom of the form and resize it until it takes up the bottom 30 percent of the form.
Inside the GroupBox control, add four Label and four TextBox controls, as shown in Figure 8.14.
Add a PictureBox control to the upper right-hand corner of the form and resize it until it is approximately 2 inches tall by 2 inches wide.
Lastly, add an ImageList control, which will appear in a component tray, located at the bottom of the form designer.
Now that the overall layout for the Hangman game user interface has been laid out, you can begin form and control property customization.
Let's begin by modifying properties associated with the game's form, as shown in Table 8.2.
Property | Value |
---|---|
Name | frmMain |
Cursor | Hand |
FormBorderStyle | Fixed3D |
MaximizeBox | False |
MinimizeBox | False |
Size | 655, 412 |
StartPosition | CenterScreen |
Text | Hangman |
Next, modify the following properties associated with the Panel control, as shown in Table 8.3.
Property | Value |
---|---|
Name | pnlLetters |
BackColor | WhiteSmoke |
BorderStyle | Fixed3D |
Next, modify the following properties associated with the GroupBox control, as shown in Table 8.4.
Property | Value |
---|---|
Name | grpStatus |
Text | Game Stats: |
Now, modify the following properties associated with the TextBox controls, as shown in Table 8.5.
Control | Property | Value |
---|---|---|
TextBox1 | Name | txtLetter1 |
BackColor | WhiteSmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.Size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox2 | Name | txtLetter2 |
BackColor | WhiteSmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox3 | Name | txtLetter3 |
BackColor | WhiteSmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.Size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox4 | Name | txtLetter4 |
BackColor | WhiteSmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.Size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox5 | Name | txtLetter5 |
BackColor | WhiteSmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.Size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox6 | Name | txtLetter6 |
BackColor | Whitesmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.Size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox7 | Name | txtLetter7 |
BackColor | WhiteSmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.Size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox8 | Name | txtLetter8 |
BackColor | WhiteSmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.Size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox9 | Name | txtLetter9 |
BackColor | WhiteSmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.Size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox10 | Name | txtLetter10 |
BackColor | WhiteSmoke | |
BorderStyle | None | |
Font.Name | Courier New | |
Font.Size | 36 | |
Font.Bold | True | |
ForeColor | DarkBlue | |
ReadOnly | True | |
TabStop | False | |
TextBox11 | Name | txtInput |
CharacterCasing | Upper | |
Font.Size | 18 | |
Font.Bold | True | |
TextBox12 | Name | txtMisses |
ReadOnly | True | |
TabStop | False | |
TextBox13 | Name | txtGuesses |
ReadOnly | True | |
TabStop | False | |
TextBox14 | Name | txtGamesWon |
ReadOnly | True | |
TabStop | False | |
TextBox15 | Name | txtGamesLost |
ReadOnly | True | |
TabStop | False |
Now, modify the following properties associated with the Label controls, as shown in Table 8.6.
Control | Property | Value |
---|---|---|
Label1 | Name | lb1Input |
Text | Enter A Letter Guess: | |
Font.Bold | True | |
Label2 | Name | lblNoMissed |
Text | No. Of Misses: | |
Font.Bold | True | |
Label3 | Name | lblGuesses |
Text | Letters Guessed: | |
Font.Bold | True | |
Label4 | Name | lblGamesWon |
Text | No. Of Games Won: | |
Font.Bold | True | |
Label5 | Name | lblGamesLost |
Text | No. Of Games Lost: | |
Font.Bold | True |
Now, modify the following properties associated with the Button control, as shown in Table 8.7.
Property | Value |
---|---|
Name | btnSubmit |
Font.Bold | True |
Text | Submit |
Now, modify the following properties associated with the PictureBox control, as shown in Table 8.8.
Property | Value |
---|---|
Name | pbcHangmanBmp |
BorderStyle | Fixed3D |
SizeMode | StretchImage |
Finally, change the name property of the ImageList control to imlHangmanBmps. Next change the Imagesize property to 80, 80 and add the following bitmap images to the Images property (collection) as shown in Table 8.9. Don't worry about having to create these bitmap images yourself. I have included copies of them with the source for the game project on the book's companion Web site.
Property | File | Index No. |
---|---|---|
Name | 100.bmp | 0 |
Name | 80.bmp | 1 |
Name | 60.bmp | 2 |
Name | 40.bmp | 3 |
Name | 20.bmp | 4 |
Name | 10.bmp | 5 |
Name | 0.bmp | 6 |
Begin coding the Hangman game by double-clicking on the game's form, thus opening the code editor and creating the form's Load event procedure as shown below.
Public Class frmMain End Class
Just above the Load event procedure, add the following statements
Const cTitleBarMsg As String = "HANGMAN" 'Titlebar message Private astrWordList(19) As String 'Stores a list of game words Private strGameWord As String = "" 'Stores the word currently in play Private strWrong As String = "" 'Stores a list of missed guesses Private intMisses As Integer = 0 'Keeps count of missed guesses Private intNoRight As Integer = 0 'Keeps count of correct guesses Private intPlayAgain As Integer = 0 'Track the player's response Private intHangmanNo As Integer = 6 'Controls display of hangman image
These declaration statements define a constant, an array, and variables that are used by two or more procedures in the application. The array, strWordListArray, will be used to store secret words used to play the game. The rest of the variables will be used to hold the currently selected secret word and game statistics and to display a string of letters that have been guessed by the player.
Now, add the following statements to the Form1_Load procedure.
'This procedure executes when the game first starts Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load LoadWordArray() 'Populate the array with words InitializeGameStats() 'Set up the game's starting values StartTheGame() 'Start the game End Sub
The three statements inside the Forml_Load procedure set up game play by calling three custom procedures. The LoadWordArray procedure populates the strWordListArray array with 20 secret words. To create it, scroll down below the Form1_Load procedure and enter the following statements.
'This procedure loads words into the game's array Sub LoadWordArray() End Sub
Next, add the following statements inside the LoadWordArray procedure in order to populate each element of the array.
strWordListArray(0) = "ELEPHANT" StrWordListArray(1) = "CARRIAGE" StrWordListArray(2) = "ENVELOPE" StrWordListArray(3) = "PRESIDENT" StrWordListArray(4) = "ELLIPTICAL" StrWordListArray(5) = "MARKER" StrWordListArray(6) = "BATTLESHIP" StrWordListArray(7) = "TERMINATE" StrWordListArray(8) = "REJOICE" StrWordListArray(9) = "LIBERTY" strWordListArray(10) = "CLASSIC" strWordListArray(11) = "REFLEX" strWordListArray(12) = "AIRPLANE" strWordListArray(13) = "DWELLING" strWordListArray(14) = "APARTMENT" strWordListArray(15) = "RENTAL" strWordListArray(16) = "FAUCET" strWordListArray(17) = "BEDROOM" strWordListArray(18) = "VOLLEY" strWordListArray(19) = "WHISTLE"
The InitializeGameStats Sub procedure will set the game statistics displayed in the GroupBox control to zeroes. You'll need to create this Sub procedure from scratch by inserting it just under the LoadWordArray procedure. Once created, you will need to modify it as shown below.
'This procedure resets values stored in the game's TextBox controls Sub InitializeGameStats() txtGamesWon.Text = 0 txtGamesLost.Text = 0 End Sub
The StartTheGame Sub procedure is responsible for setting up the game to play a new round. It does so by making three Sub procedure calls, which reset variable defaults, retrieve a secret game word, and format the word for display. The procedure ends by setting focus to the txtInput control in order to ready the game for the player input.
'This procedure preps the game for a new round of play Sub StartTheGame() ResetDefaultSettings() 'Call procedure to reset game settings strGameWord = RetrieveWord() 'Get a word for the player to guess FormatGameWord() 'Format the display of the mystery word txtInput.Focus() 'Prepare the game to accept the player's guess End Sub
Next, create the ResetDefaultSettings Sub procedure, shown below, in order to prepare the game to play a new round.
'This procedure resets variables and TextBox control values back to 'their starting values Sub ResetDefaultSettings() intMisses = 0 intNoRight = 0 txtGuesses.Text = "" txtMisses.Text = intMisses txtInput.Text = "" intHangmanNo = 6 pbcHangmanBmp.Image = imlHangmanBmps.Images.Item (6) End Sub
Note that in addition to resetting variable default values, the last statement in the previous procedure displays an empty hangman's gallows graphic in the PictureBox control.
Now, create the RetrieveWord procedure. The procedure will retrieve a random number, use that number to select a word from the strWordListArray array, and then return the secret word to the statement that called the procedure. Therefore, you will need to define this procedure as a Function and to return the secret word by assigning it to a variable that has the same name as the procedure.
'This procedure randomly retrieves a word for the player to guess Function RetrieveWord() As String 'Declare a variable to hold a randomly generated number Dim intRandomNo As Integer = 0 Dim r As New Random 'Instantiate a Random object 'Use the Random object's Next method to retrieve a number between 'zero and the number of words in the array intRandomNo = r.Next(strWordListArray.Length) 'Use the random number to select a word from the array and pass 'the word back to the calling statement Return strWordListArray(intRandomNo) End Function
Trick | Note the use of the Random object and its Next method in the RetrieveWord procedure. In previous chapters you've seen me use Visual Basic's built-in Randomize and Rnd functions. The functionality provided by these two functions has been duplicated by the .NET Framework's Random object and its Next method. |
Next, create the FormatGameWord Sub procedure as shown below. The procedure begins by declaring an Integer type variable named intCounter. It then assigns an empty string to each of the 10 TextBox controls that are used to display the letters that make up the game's secret word. The procedure then sets up a For…Next loop to display an underscore character for each letter in the secret word.
'This procedure formats the display of underscore characters 'representing the game's word Sub FormatGameWord() 'Define a variable to be used as a counter Dim intCounter As Integer = 1 'Game words are limited to ten or less characters. Each let- ter is 'stored in a separate Textbox control. Start by setting the value 'stored in each Textbox control to a blank character txtLetter1.Text = "" txtLetter2.Text = "" txtLetter3.Text = "" txtLetter4.Text = "" txtLetter5.Text = "" txtLetter6.Text = "" txtLetter7.Text = "" txtLetter8.Text = "" txtLetter9.Text = "" txtLetter10.Text = "" 'Use a For…Next loop to display an underscore character for each 'letter that makes up the game's word For intCounter = 1 To strGameWord.Length If intCounter = 1 Then txtLetter1.Text = "_" If intCounter = 2 Then txtLetter2.Text = "_" If intCounter = 3 Then txtLetter3.Text = "_" If intCounter = 4 Then txtLetter4.Text = "_" If intCounter = 5 Then txtLetter5.Text = "_" If intCounter = 6 Then txtLetter6.Text = "_" If intCounter = 7 Then txtLetter7.Text = "_" If intCounter = 8 Then txtLetter8.Text = "_" If intCounter = 9 Then txtLetter9.Text = "_" If intCounter = 10 Then txtLetter10.Text = "_" Next
You can create the next procedure, shown below, by returning to the form designer and double-clicking on the btnSubmit control and then modifying the procedure as shown below.
'This procedure executes each time the player clicks on the button 'control labeled Submit Private Sub btnSubmit_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnSubmit.Click Dim intCounter As Integer = 0 'Variable to be used as a counter Dim strWordLetter As String = "" 'Store the player's guess Dim strInputOk As String = "" 'Used when validating player input strInputOk = ValidatePlayerInput() 'Call validation procedure If strInputOk = "Error" Then Exit Sub 'Exit procedure if player's guess is invalid End If 'Display an error if the player has already guessed the letter If Strings.InStr(1, txtGuesses.Text, txtInput.Text.ToUpper, 1) _ <> 0 Then MessageBox.Show("Error: Letter has already been guessed.") Else 'See if the player guessed a letter in the word 'Add letter to list of letters that have already been guessed txtGuesses.Text &= " " & txtInput.Text 'Look for the letter in the word If Strings.InStr(1, strGameWord.ToUpper, _ txtInput.Text.ToUpper, 1) = 0 _ Then 'The player's guess was wrong 'Append letter to the list of missed guesses strWrong &= " " & txtInput. Text.ToUpper intMisses += 1 'Add one to the number of missed guesses txtMisses.Text = intMisses 'Display # of missed guesses 'Decrement variable used to keep track of which Hangman 'graphic is to be displayed intHangmanNo -= 1 pbcHangmanBmp.Image = _ imlHangmanBmps.Images.Item(intHangmanNo) 'Display graphic If intMisses = 6 Then 'Game is over after 6 missed guesses txtGamesLost.Text += 1 'Increment counter 'Call procedure to see if player wants to play again PromptForNewGame("Sorry. You have lost.") End If End If 'Use For…Next loop to spin through each letter in the word For intCounter = 1 To strGameWord.Length 'Select one letter at a time strWordLetter = Mid(strGameWord, intCounter, 1) 'See if the letter matches the player's guess If strWordLetter.ToUpper = txtInput.Text.ToUpper Then intNoRight += 1 'Increment counter 'Execute procedure to display a guessed letter FlipGameLetter(intCounter) End If Next End If 'See if the player has guessed every letter in the word If intNoRight = strGameWord.Length Then txtGamesWon.Text += 1 'Call procedure to see if player wants to play again PromptForNewGame("Congratulations. You have Won!") End If txtInput.Text = "" txtInput.Focus() End Sub
The procedure begins by declaring three variables and then calls on the ValidatePlayerInput procedure to determine if the player's input is acceptable. If the input is OK, the procedure continues executing.
Next, the procedure uses an If…Then…Else statement to see if the player has entered a letter guess that has already been tried. If this is a new guess, the letter that is guessed is appended to the end of the test string displayed in the txtGuesses control.
An embedded If…Then statement then checks to see if the player's guess is wrong using the Strings object's Instr() function. If the player guesses incorrectly, the value stored in the txtMisses control is increased by 1 and the graphic displayed in the PictureBox control is changed to reflect the number of misses so far. A check is then made to see if the player has missed a total of six guesses, thus losing the game.
If the game has not been lost, a For…Next loop is used to spin through the secret word and display each letter that matches the player's guess. Finally, a check is made to see if the player has guessed all of the letters that make up the secret word, thus winning the game. If the game has not been won, the txtInput control is cleared and given focus, thus preparing the game for the player's next guess.
The Strings object provides access to methods that can be used to perform string operations. The Strings object's InStr method retrieves an Integer specifying thp start position of one string! within another.
Next, you will need to manually create the ValidatePlayerInput procedure. This procedure will need to return a value, so you will need to define it as a Function, as shown below.
'This procedure validates the player's guess Function ValidatePlayerInput() As String 'See if the player clicked on Submit without entering a guess If txtInput.Text = "" Then MessageBox.Show("Error: You must enter a letter.") txtInput.Text = "" txtInput.Focus() Return "Error" 'Return a value indicating an invalid guess End If 'See if the player entered more than one character If txtInput.Text.Length > 1 Then MessageBox.Show("Error: You may only enter one letter per" & _ "guess.") txtInput.Text = "" txtInput.Focus() Return "Error" 'Return a value indicating an invalid guess End If 'See if the player entered a number If IsNumeric(txtInput.Text) = True Then MessageBox.Show("Error: Numeric entries are not valid.") txtInput.Text = "" txtInput.Focus() Return "Error" 'Return a value indicating an invalid guess End If Return "Passed" 'Return a value indicating a valid guess End Function
The procedure performs three input validation tests in order to make sure that the player has provided an acceptable guess. It accomplishes this using three If…Then statements. The first If…Then statement makes sure that the player enters something. The second If…Then statement makes sure that the player did not enter more than one character, and the third If…Then statement makes sure that the player didn't enter a number.
Each time the player correctly guesses a letter, the FlipGameLetter Sub procedure is executed. You'll need to manually create this procedure, as shown below.
'This procedure is responsible for displaying upper case letters in 'the appropriate Textbox controls Sub FlipGameLetter(ByVal intLetterNumber As Integer) 'Select the letter to be displayed based on the argument passed 'to the procedure Select Case intLetterNumber Case 1 txtLetter1.Text = txtInput.Text.ToUpper Case 2 txtLetter2.Text = txtInput.Text.ToUpper Case 3 txtLetter3.Text = txtInput.Text.ToUpper Case 4 txtLetter4.Text = txtInput.Text.ToUpper Case 5 txtLetter5.Text = txtInput.Text.ToUpper Case 6 txtLetter6.Text = txtInput.Text.ToUpper Case 7 txtLetter7.Text = txtInput.Text.ToUpper Case 8 txtLetter8.Text = txtInput.Text.ToUpper Case 9 txtLetter9.Text = txtInput.Text.ToUpper Case 10 txtLetter10.Text = txtInput.Text.ToUpper End Select End Sub
This procedure consists of a single Select Case statement, which compares the player's guess to each letter in the secret word and flips (that is, displays) any letters that match the guess.
The last procedure that you will need to create is the PromptForNewGame Sub procedure. You will need to manually define this procedure and then set it up as shown below.
'This procedure prompts the player to play again Sub PromptForNewGame(ByVal strMessage As String) 'Ask player to play again intPlayAgain = MessageBox.Show(strMessage &_ " Would you like to play again?", _ cTitleBarMsg, MessageBoxButtons.YesNo, MessageBoxIcon.Question) 'The player clicked on Yes If intPlayAgain = 6 Then StartTheGame() 'Start a new game Else 'The player clicked on No Application.Exit() 'End the game End If End Sub If intPlayAgain = 6 Then
The procedure uses the MessageBox.Show method to ask if the player would like to play another round. If the player clicks on the Yes button, the StartTheGame procedure is executed. Otherwise, the Application.Exit statement executes and terminates the application.
The Hangman game is now complete. Press F5 and test out the game to make sure that everything is working properly. If you run into errors, then you have probably made one or more typing mistakes. Go back and double-check your code and see if you have made any errors. Before you pass the game along to your friends, make sure that you remember to test all aspects of the game, including feeding it good and bad input.