
C# ПІДРУЧНИКИ / c# / Hungry Minds - ASP.NET Bible VB.NET & C#
.pdf
This continues until one of three things happens:
§You go over 21 — you lose. (See Figure 29-3. )
Figure 29-3: You busted.
§You're total adds up to 21 on the nose — you win! (See Figure 29-4.)
Figure 29-4: You got 21! You win! §You decide to stand.
When you choose to stand, it becomes the dealer's turn. By Las Vegas rules, the dealer always draws until his total is 17 or over or he busts.
The results are tallied and the user is informed of the result, be it good (see Figure 29-5) or bad (see Figure 29-6).

Figure 29-5: You win!
Figure 29-6: You lose.
Finally, you're asked if you'd like to play again. If you click Yes, a new game begins.
Design: The Blackjack Breakdown
The first steps in creating any software application are analysis and design. Fortunately with a game like this, if you know the rules, the analysis step is done. So on to design!
When you're developing in an object-oriented environment, design is all about compositional breakdown. That process, like most, involves asking the right questions:
§What is the functionality that I need to perform and the data that I need to track?
§How should I carve that functionality into classes, methods, and properties?
Stepping through the process
A blackjack game consists of at least one player and one dealer. So there are at least two participants. They are each going to have their own hand of cards dealt to them from a common deck. The cards themselves should be a standard deck of 52 with four suites,
and so forth. You'll need to be able to shuffle those cards and draw from the top of the deck.
In addition, the user has to be able to see the cards they receive and be able to choose whether they want to hit or stand. Each time they hit, they should receive a new card and be allowed to make the decision again. Once they choose to stand, the computer will play the role of the dealer and draw cards appropriately.
Finally, when the player and dealer are done, the results should be compared and the winner declared. The user should have the option to play again, and if he or she chooses to, the process should be restarted.
Picking out the objects and dividing responsibilities
The most obvious class you need for a game like this is a Card class. It holds an individual card's identity and its value.
Further, you need a class that holds the entire deck of cards to draw from. Casinos use multiple decks and, after they are shuffled, the cards are organized into a plastic box called a shoe. The shoe provides an easy way to draw one card at a time from the end of the box. I call the class that holds the collection of cards Shoe. Shoe has two key capabilities in addition to holding the cards: Shuffle and Draw.
Because the player and the dealer play very similar roles, it makes sense to create one class called Participant, and then make two instances of that class — Player and Dealer. The Participant class has one important property: Hand. This is an ArrayList holding the cards that have been dealt to that person. The Participant class also has a function, TotalValue, which calculates the total value of the cards in the participant's hand.
The user interface
As with any game, the user interface is an important element of this application. In this application, I opted for a design that is simple rather than flashy. I did everything with little more than buttons and labels. That leaves you plenty of opportunity to dress it up as you see fit.
The player's cards appear on the left and the dealer's cards appear on the right. Each card is displayed using a label holding the name of the card, such as "Ace of Spades" or "3 of Hearts." Two buttons below the cards offer the options to Hit or Stand. After the user is done, they are asked if they'd like to play again. If they click Yes, the page is reset and a new game begins. Simple enough.
A Place to Work
The first step in the development phase is to find a place to work. You will need both access to a Web server that supports ASP.NET and the ability to create virtual folders. If you are working on the server itself or accessing its files over a network, create a new folder under wwwroot called "blackjack." Under that folder, create a new folder called
"bin." Now make the blackjack folder a virtual folder. (I describe how to do this in the next paragraph.) This is important, because it lets the server know that this is the root for a Web application, and it will allow you to use the bin subfolder to hold components that this Web application will use.
If you are on the server yourself, you can make blackjack a virtual folder by choosing Internet Services Manager under Administrative Tools. Then, open the server and find the Default Web Site. Under that, you should see a list of folders that includes the blackjack folder you just created. Right-click it and choose Properties. In the dialog box, on the Virtual Directory tab, under Application Settings, click the Create button. This turns the blackjack folder into a virtual folder and tells IIS that this folder contains a separate Web application. Click OK to complete the process.
If your Web site is provided by a hosting company, it may provide an administrative page or utility that enables you to identify folders and virtual folders on its Web server. If not, you may have to simply request the hosting company to do it for you.
The Card Object
With the basic organization of the game in mind, I began to search through the various data structures the .NET Framework makes available and began to think about the best ways to create my classes and their contents.
Because the Card object is really going to be very simple, I used a structure for it instead of a class. Here's the code to create it. (Don't worry about typing this code in anywhere right now. I'll show you where it goes in a future section.)
Public Structure Card
Public Suit As String
Public Name As String
Public Value As Integer
End Structure
Suit holds the suit name: Hearts, Diamonds, Clubs, or Spades. Name holds the name of the card, as a string. This might be Jack, King, Ace, or 3. Value holds the value of this card in blackjack, as an integer. For numbered cards, it is the numeric equivalent of the Name. For face cards, it is 10, and aces are 1. (The potential for aces to count as 11 sometimes will be handled in the code.)
A Data Structure for the Shoe Object
Next on the agenda is the Shoe object. But before I could figure out the Shoe object itself, I had another question buzzing around in my head that I had to answer: What should I use inside the Shoe object to store the cards? Lots of options are available. I could use an array, an ArrayList, a stack, a queue, and so on.
I don't need to be able to directly address the individual Card objects inside the Shoe object, but I do need the ability to add new cards to it and pull cards off the top. The Stack class in the System.Collections namespace provides these capabilities, but it is missing one critical requirement: the ability to randomly rearrange the elements inside the data structure — in other words, shuffle the cards.
The randomizable stack
I decided to create my own randomizable stack. I did this by creating a namespace and a class in a text file using the Visual Basic syntax and compiling it with the command-line compiler. The beauty of this approach is that you don't have to go to the expense and complexity of using Visual Studio to do it. It's as easy as creating an ASPX page. The only difference is that you have to use the vbc command-line compiler to turn it into a DLL.
Here's what the new randomizable stack looks like. Put this code in a file named rndstk.vb.
Option Explicit
Option Strict
Imports System
Imports System.Collections
EntryIndex1 = CInt(Int(Rnd * NumEntries))
EntryIndex2 = CInt(Int(Rnd * NumEntries))
TempEntry = StackList(EntryIndex1)
StackList(EntryIndex1) = StackList(EntryIndex2)
StackList(EntryIndex2) = TempEntry
Next
End Sub
End Class
End Namespace
A new class and a place to stack your stuff
After specifying Option Strict and Explicit, I imported three namespaces to use here. Then I declared a single class inside a single namespace: RandStack. The most important part of this class is the private StackList ArrayList. This is where the stack information is actually stored.
Because an ArrayList can store anything inherited from Object (and that means anything), I decided to make this stack equally flexible. That means that it can be reused in any circumstance where stack-like functionality is needed along with the ability to randomly reorganize the elements. Other kinds of cards (those used in collectible card games such as Magic the Gathering, for example) can be represented with their own structures or classes and then stored in this data structure as well. Wherever possible, design for reuse!
Count is created as a read-only property that returns the number of elements in the ArrayList.
Push me, pop me
The old standby methods that are a part of every stack are Push and Pop. Push adds a value to the top of the stack and pop retrieves the value from the top of the stack. Here is the code (from the listing above) that implements these two methods.
Public Sub Push(Entry As Object)
StackList.Insert(0,Entry)
End Sub
Public Function Pop As Object
Dim Entry As Object
Entry = StackList(0)
StackList.RemoveAt(0)
Return(Entry)
End Function
In this implementation, the Insert method of the ArrayList is used to place new elements in the very first position in the ArrayList (causing all the other elements to be pushed down a notch). Then, to retrieve an element, the object is placed in a local variable to be returned, and then the ArrayList's RemoveAt method is used to remove the very first ArrayList element (causing all the other elements to hop up a notch).
No peeking
Although I didn't need this functionality for my blackjack page, I decided to go ahead and implement another method, called Peek, from the stack in the System.Collections namespace:
Public Function Peek As Object
Dim Entry As Object
Entry = StackList(0)
Return(Entry)
End Function
Peek does essentially the same thing as Pop except that the item isn't removed from the stack. Again, if this class is reused for other purposes in the future, there may be need for such a method.
Bringing order to randomness (or vice versa)
Finally, the RandomizeOrder subroutine is the reason I went to all this trouble in the first place:
Public Sub RandomizeOrder
Dim Swaps As Integer
Dim EntryIndex1, EntryIndex2 As Integer
Dim NumEntries As Integer = StackList.Count
Dim NumSwaps As Integer = NumEntries * 3
Dim TempEntry As Object
Randomize
For Swaps = 1 To NumSwaps
EntryIndex1 = CInt(Int(Rnd * NumEntries))
EntryIndex2 = CInt(Int(Rnd * NumEntries))
TempEntry = StackList(EntryIndex1)
StackList(EntryIndex1) = StackList(EntryIndex2)
StackList(EntryIndex2) = TempEntry
Next
End Sub
How do you shuffle the entries in an ArrayList? Probably the easiest way, without creating a whole new ArrayList, is to pick two elements at random and swap them. And then do it again. And so on. How many times? Well, after trying different numbers, it seems that to really get 50 cards shuffled well, you need to do about 150 random swaps. With that ratio in mind, this routine loops for three times the number of elements in the ArrayList, picks two indexes at random, and then swaps the elements at those indexes.
Saving and compiling the new class
After I wrote this code, I saved it to a file named Rndstk.vb and put it in the blackjack/bin folder. Then, in a command window, I went to the folder and executed this command:
vbc /t:library rndstk.vb
This compiles the file using the Visual Basic command-line compiler into a DLL, the result of which is the creation of the Rndstk.dll file.
The Shoe Object
Now that I have a solid data structure to use, I'm ready to create the class for the Shoe object. This class, when it's instantiated, will actually create a deck with 52 Card objects representing the standard 52 cards (minus jokers) found in a deck of playing cards. This takes a big load off the ASP.NET page itself and allows the focus there to be on the game, not on handling the cards.
I decided to make the class for the Shoe object a separate Visual Basic component, just like the Randomized Stack. And I do it for the same reason: reusability. This is surely not the only page that could benefit from the use of a deck of standard playing cards. You might later want to create a Video Poker page, and this class would be just as useful there.
Try this on for size
The following code is what the class looks like. This code will go in its own file named playingcards.vb. I'll show you how to compile it after I describe the code.
Option Explicit
Option Strict
Imports System
Imports RandomizableStack
Namespace PlayingCards
Public Structure Card
Public Suit As String
Public Name As String
Public Value As Integer
End Structure
Public Class Shoe
Private CardIndex As Integer
Private CardDeck As RandStack = New RandStack
Sub New(NumDecks As Integer)
For CardIndex = 0 To (52 * NumDecks -1)
Dim NewCard As Card = New Card
Select Case (CardIndex Mod 4)
Case 0
NewCard.Suit = "Hearts"
Case 1
NewCard.Suit = "Diamonds"
Case 2
NewCard.Suit = "Clubs"
Case 3
NewCard.Suit = "Spades"
End Select
Select Case (CardIndex Mod 13)
Case 0
NewCard.Name = "Ace"
NewCard.Value = 1
Case 1
NewCard.Name = "2"
NewCard.Value = 2
Case 2
NewCard.Name = "3"
NewCard.Value = 3
Case 3
NewCard.Name = "4"
NewCard.Value = 4
Case 4
NewCard.Name = "5"
NewCard.Value = 5
Case 5
NewCard.Name = "6"
NewCard.Value = 6
Case 6
NewCard.Name = "7"
NewCard.Value = 7
Case 7
NewCard.Name = "8"
NewCard.Value = 8
Case 8
NewCard.Name = "9"
NewCard.Value = 9
Case 9
NewCard.Name = "10"
NewCard.Value = 10
Case 10
NewCard.Name = "Jack"
NewCard.Value = 10
Case 11
NewCard.Name = "Queen"
NewCard.Value = 10
Case 12
NewCard.Name = "King"
NewCard.Value = 10
End Select
CardDeck.Push(NewCard)
Next
End Sub
Public ReadOnly Property Count As Integer
Get
Return CardDeck.Count
End Get
End Property
Public Sub Shuffle
CardDeck.RandomizeOrder
End Sub
Public Function Draw As Card
Return(CType(CardDeck.Pop,Card))
End function
End Class
End Namespace
Notice that this code includes an Imports statement for RandomizableStack. That's not a standard .NET namespace. That's the namespace I created with the Rndstk.vb file created in the last section. I import it here so that I can use that data structure to store the cards inside the Shoe class.
The namespace created in this file is called PlayingCards and includes two items: the Card structure, which you saw earlier in this chapter, and the Shoe class. The nice thing about doing it this way is that when this namespace is imported into an ASP.NET page, both the structure and the class that uses that structure are imported. The page can immediately begin creating and using Card objects and can receive Card objects back from methods in the Shoe class.