Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

C# ПІДРУЧНИКИ / c# / Hungry Minds - ASP.NET Bible VB.NET & C#

.pdf
Скачиваний:
128
Добавлен:
12.02.2016
Размер:
7.64 Mб
Скачать

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

Imports Microsoft.VisualBasic

Namespace RandomizableStack

Public Class RandStack

Private StackList As ArrayList = New ArrayList

Public ReadOnly Property Count As Integer

Get

Return StackList.Count

End Get

End Property

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

Public Function Peek As Object

Dim Entry As Object

Entry = StackList(0)

Return(Entry)

End Function

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

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.

Соседние файлы в папке c#