Random Numbers and Tic Tac Toe with Visual Basic .NET
If you are a little like I am you will be doing something with your computer even during the holidays. (More than likely I will be playing Red Alert 2 with the kids, but I may sneak in a little writing.) For you workaholics, I have included a little holiday fare. This way you will be able to do something constructive, yet be able to tear yourself away when company comes knocking on the door.
Random numbers are commonly employed in applications for a wide variety of reasons. In this week's letter let's take a quick look at the Random class in the Common Language Runtime.
The System Namespace
The System namespace is a root namespace in the Common Language Runtime (CLR). One of the classes in the System namespace is the Random class. (That's right! Random numbers are encapsulated in a class in .NET.) Like VB6 random numbers are based on a mathematical algorithm from a finite set of numbers. The result is that you get pseudo-random numbers, but these numbers are random enough for many practical purposes (just not the lottery).
Random numbers in VB.NET can be initialized with or without a seed value and can be constrained to a range of numbers. If the same seed is used then the generated number sequence will be identical. A good strategy is to seed the random numbers using a unique value like the time. Table 1 describes the members of the Random class and is followed by some code examples demonstrating how to use the Random class. Members of the System.Random class.
|Constructs instance of Random Class.|
New(ByVal Seed As Integer)
|Constructs instance of Random class using Seed value.|
Next() As Integer
|Returns next integer in the random number sequence.|
Next(ByVal maxValue As Integer) As Integer
|Returns a positive integer less than maxValue.|
Next(ByVal minValue As Integer, ByVal maxValue As Integer) As Integer
|Returns a number greater than or equal to minValue and less than or maxValue.|
NextBytes(ByVal buffer() As Byte)
|Returns an array of random bytes greater than or equal to 0 and less than Byte.MaxValue.|
NextDouble() As Double
|Returns a random Double between 0.0 and 1.0.|
The basic use of the Random class is to create an instance of the class and use the Random.Next method to acquire subsequent random numbers. You do not have to re-create the Random object each time you want a random number. The following fragments demonstrate several examples of creating Random objects and getting random numbers.
The first example creates a Random object and displays the first random number in a message box.
Dim R As New Random() MsgBox(R.Next().ToString())
The second example creates an instance of the Random class with a seed value of 100.
Dim R As New Random(100) Dim I As Integer = R.Next()
The third example creates a Random object, seeding the object from a time-related value and requests a number between 1 and 100, excluding the value 100.
Dim R As New Random(Now.Millisecond) R.Next(1, 100)
A simple Tic-Tac-Toe game is provided in the listing below, providing some more examples of using the Random class.
TicTacToe: Random Number Example
In keeping with the theme of this article, listing 1 contains a prototype of a Tic-Tac-Toe game (see figure). Just in case someone has forgotten, Tic-Tac-Toe is a simple game where players alternate turns, each player marking an empty square until all squares are marked or someone has three consecutive horizontal, vertical, or diagonal squares marked. If no player has three consecutive squares then the game is a Cats game.
The code is all implemented in a Windows application on a form. The computer player is as random as it can be (so everyone has a really good chance of winning).
Figure 1: Tic-Tac-Toe against a Random computer player.
Listing 1: Computer uses Random numbers to determine where to place "O".
1: Public Class Form1 2: Inherits System.Windows.Forms.Form 3: 4: [ Windows Form Designer generated code ] 5: 6: Private Sub Button2_Click(ByVal sender As System.Object, _ 7: ByVal e As System.EventArgs) Handles Button2.Click 8: Close() 9: End Sub 10: 11: Private Sub Initialize() 12: 13: Dim I As Integer 14: For I = 0 To Me.Controls.Count - 1 15: If (TypeOf Controls(I) Is Label) Then 16: Dim L As Label = Controls(I) 17: If (L.Tag <> Nothing) Then 18: L.Text = "" 19: Squares(L.Tag) = L 20: End If 21: End If 22: Next 23: 24: End Sub 25: 26: Private Random As System.Random 27: Private Turns As Integer = 0 28: Private Squares(8) As Label 29: Private UserTurn As Boolean = False 30: 31: Private Sub NewGame() 32: 33: Static UserFirst As Boolean = False 34: UserFirst = Not UserFirst 35: Initialize() 36: Random = New Random(Now.Millisecond) 37: Turns = 0 38: 39: If (UserFirst) Then 40: UserTurn = True 41: Else 42: UserTurn = False 43: ComputerTurn() 44: End If 45: End Sub 46: 47: Private Sub Button1_Click(ByVal sender As System.Object, _ 48: ByVal e As System.EventArgs) Handles Button1.Click 49: NewGame() 50: End Sub 51: 52: 53: Private Sub Form1_Load(ByVal sender As System.Object, _ 54: ByVal e As System.EventArgs) Handles MyBase.Load 55: 56: NewGame() 57: 58: End Sub 59: 60: Private Sub Label9_Click(ByVal sender As System.Object, _ 61: ByVal e As System.EventArgs) Handles Label1.Click, _ 62: Label2.Click, Label3.Click, Label4.Click, Label5.Click, _ 63: Label6.Click, Label7.Click, Label8.Click, Label9.Click 64: 65: If (Not UserTurn Or IsGameOver()) Then Exit Sub 66: 67: With CType(sender, Label) 68: If (.Text = "") Then 69: .Text = "X" 70: Turns += 1 71: 72: If (Not IsGameOver()) Then 73: UserTurn = False 74: ComputerTurn() 75: End If 76: Else 77: Beep() 78: End If 79: End With 80: 81: 82: End Sub 83: 84: Private Function TakeTurn(ByVal Turn As Integer) As Boolean 85: If (Squares(Turn).Text = "") Then 86: Squares(Turn).Text = "O" 87: Return True 88: Else 89: Return False 90: End If 91: End Function 92: 93: Private Sub ComputerTurn() 94: Dim Turn As Integer = Random.Next(0, 9) 95: 96: While (True) 97: If (TakeTurn(Turn)) Then Exit While 98: Application.DoEvents() 99: Turn = Random.Next(1, 9) 100: End While 101: 102: Turns += 1 103: IsGameOver() 104: UserTurn = True 105: 106: End Sub 107: 108: Private Overloads Function SquaresEqual( _ 109: ByVal First As Integer, _ 110: ByVal Second As Integer, _ 111: ByVal Third As Integer) As Boolean 112: 113: Return Squares(First).Text = Squares(Second).Text _ 114: And Squares(Second).Text = Squares(Third).Text _ 115: And Squares(First).Text <> "" 116: 117: End Function 118: 119: Private Overloads Function SquaresEqual(ByVal First As Integer, _ 120: ByVal Second As Integer, ByVal Third As Integer, _ 121: ByRef Value As String) As Boolean 122: 123: 124: Dim Result As Boolean 125: Result = SquaresEqual(First, Second, Third) 126: 127: If (Result) Then 128: Value = Squares(First).Text 129: End If 130: 131: Return Result 132: End Function 133: 134: 135: Private Function IsWinner( _ 136: Optional ByRef Value As String = "") As Boolean 137: 138: Return SquaresEqual(0, 1, 2, Value) Or _ 139: SquaresEqual(0, 3, 6, Value) Or _ 140: SquaresEqual(0, 4, 8, Value) Or _ 141: SquaresEqual(1, 4, 7, Value) Or _ 142: SquaresEqual(2, 5, 8, Value) Or _ 143: SquaresEqual(2, 4, 6, Value) Or _ 144: SquaresEqual(3, 4, 5, Value) Or _ 145: SquaresEqual(6, 7, 8, Value) 146: 147: End Function 148: 149: Private Sub ScoreGame() 150: Dim Value As String = "" 151: If (IsWinner(Value)) Then 152: MsgBox(Value & " is the winner!") 153: Else 154: MsgBox("Cats Game!") 155: End If 156: End Sub 157: 158: Private Function IsGameOver() As Boolean 159: 160: If (Turns >= 9 Or IsWinner()) Then 161: ScoreGame() 162: Return True 163: Else 164: Return False 165: End If 166: 167: End Function 168: 169: End Class
Line 26 declares an uninitialized Random member named Random. Line 36 constructs a Random object, seeding with the number of milliseconds for the current time value, every time a new game is started. Line 94 requests a random number between 0 and 9, including 0 and excluding 9. This represents the 9 possible spaces on the Tic-Tac-Toe board. Line 94 is included in the ComputerTurn method. The computer tries random numbers between 0 and 9 until it finds an empty square to place it's O. After each player's turn the game checks to see if the game is over. If all squares are marked or a player has won then the game is scored.
About the Author
Paul Kimmel is a freelance writer for Developer.com and CodeGuru.com. Look for cool Visual Basic .Net topics in his upcoming book Visual Basic .Net Unleashed available in January of 2002.
Paul founded Software Conceptions, Inc. in 1990. Contact Paul Kimmel at firstname.lastname@example.org for help building VB.NET applications or migrating VB6 applications to .NET.
# # #