2008 Winter Scripting Games

Solution to Advanced VBScript Event 10: Blackjack!

Event 10 Solution


VBScript solution to Event 10 in the 2008 Winter Scripting Games.

Solutions are also available for Windows PowerShell and Perl.

*

Blackjack!


To tell you the truth, we assumed that the solution to Event 10 would be the most difficult to write; we definitely knew that it would be the longest script we’d have to write. As it turned out, however, this was actually one of the easier scripts we had to write. Granted, it took awhile to finish (simply because there were a lot of things going on), but the logic was pretty straightforward and there was a nice progression from step 1 to step 2 to step 3. All in all, a piece of cake.

And yes, we figured that meant we must have made a mistake somewhere along the line, too. But as near as we can tell, the script seems to work as advertised. And that’s great.

Now if we could just write a script that could help us win at Blackjack, well, then we’d be in business.

Until then, however, we’ll have to be content with a script that simply plays Blackjack:

intLow = 0
intHigh = 51

Dim arrUserCards(8)
Dim arrDealerCards(8)
Dim arrCards(51)

CreateDeck

Set objRandom = CreateObject("System.Random")

For x = 0 to 1
    Do While True
        intRandom = objRandom.Next_2(intLow,intHigh)
        If arrCards(intRandom) <> "Dealt" Then
            arrUserCards(x) = arrCards(intRandom)
            arrCards(intRandom) = "Dealt"
            Exit Do
        End If
    Loop
Next

For y = 0 to 1
    Do While True
        intRandom = objRandom.Next_2(intLow,intHigh)
        If arrCards(intRandom) <> "Dealt" Then
            arrDealerCards(y) = arrCards(intRandom)
            arrCards(intRandom) = "Dealt"
            Exit Do
        End If
    Loop
Next
x = 1
y = 1

Wscript.Echo "Your cards: "

For Each strCard in arrUserCards
    If strCard > "" Then
        arrBreaker = Split(strCard, ";")
        Wscript.Echo arrBreaker(0) 
        intUserTotal = intUserTotal + CInt(arrBreaker(1))
    End If
Next

Wscript.Echo 

Wscript.Echo "Dealer's cards: "

For Each strCard in arrDealerCards
    If strCard > "" Then
        arrBreaker = Split(strCard, ";")
        intDealerTotal = intDealerTotal + CInt(arrBreaker(1))
    End If
Next

arrBreaker = Split(arrDealerCards(0), ";") 
Wscript.Echo arrBreaker(0)
Wscript.Echo

Do While True
    Wscript.StdOut.Write "Stay (s) or hit (h) ?"
    strChoice = WScript.StdIn.ReadLine

    If strChoice = "s" Then
        Exit Do
    Else

        Do While True
            intRandom = objRandom.Next_2(intLow,intHigh)
            If arrCards(intRandom) <> "Dealt" Then
                x = x + 1
                arrUserCards(x) = arrCards(intRandom)
                arrCards(intRandom) = "Dealt"
                Exit Do
            End If
         Loop

        intUserTotal = 0

        For Each strCard in arrUserCards
            If strCard > "" Then
                arrBreaker = Split(strCard, ";")
                Wscript.Echo arrBreaker(0) 
                intUserTotal = intUserTotal + CInt(arrBreaker(1))
                If intUserTotal > 21 Then
                    Wscript.Echo "Over 21. Sorry, you lose."
                    Wscript.Quit
                End If
            End If
        Next
    End If 
    Wscript.Echo
Loop

Wscript.Echo
Wscript.Echo "You have " & intUserTotal & "."
Wscript.Echo

For Each strCard in arrDealerCards
    If strCard > "" Then
        arrBreaker = Split(strCard, ";")
        Wscript.Echo arrBreaker(0) 
    End If
Next

Wscript.Echo 

If intDealerTotal > intUserTotal Then
    Wscript.Echo "The dealer has " & intDealerTotal & ". Sorry, you lose."
    Wscript.Quit
End If   

Do While True
    Do While True
        intRandom = objRandom.Next_2(intLow,intHigh)
        If arrCards(intRandom) <> "Dealt" Then
            y = y + 1
            arrDealerCards(y) = arrCards(intRandom)
            arrCards(intRandom) = "Dealt"
            Exit Do
        End If
     Loop

    intDealerTotal = 0

    Wscript.Echo "Dealers' cards: "
Wscript.Echo

    For Each strCard in arrDealerCards
        If strCard > "" Then
            arrBreaker = Split(strCard, ";")
            Wscript.Echo arrBreaker(0) 
            intDealerTotal = intDealerTotal + CInt(arrBreaker(1))
            If intDealerTotal > 21 Then
                Wscript.Echo "The dealer is over 21. You win!"
                Wscript.Quit
            End If
            If intDealerTotal >= intUserTotal Then
                Wscript.Echo "The dealer has " & intDealerTotal & ". Sorry, you lose."
                Wscript.Quit
            End If
        End If
    Next
    Wscript.Echo
Loop

Sub CreateDeck
    arrCards(0) = "Ace of Hearts;11"
    arrCards(1) = "King of Hearts;10"
    arrCards(2) = "Queen of Hearts;10"
    arrCards(3) = "Jack of Hearts;10"
    arrCards(4) = "Ten of Hearts;10"
    arrCards(5) = "Nine of Hearts;9"
    arrCards(6) = "Eight of Hearts;8"
    arrCards(7) = "Seven of Hearts;7"
    arrCards(8) = "Six of Hearts;6"
    arrCards(9) = "Five of Hearts;5"
    arrCards(10) = "Four of Hearts;4"
    arrCards(11) = "Three of Hearts;3"
    arrCards(12) = "Two of Hearts;2"

    arrCards(13) = "Ace of Clubs;11"
    arrCards(14) = "King of Clubs;10"
    arrCards(15) = "Queen of Clubs;10"
    arrCards(16) = "Jack of Clubs;10"
    arrCards(17) = "Ten of Clubs;10"
    arrCards(18) = "Nine of Clubs;9"
    arrCards(19) = "Eight of Clubs;8"
    arrCards(20) = "Seven of Clubs;7"
    arrCards(21) = "Six of Clubs;6"
    arrCards(22) = "Five of Clubs;5"
    arrCards(23) = "Four of Clubs;4"
    arrCards(24) = "Three of Clubs;3"
    arrCards(25) = "Two of Clubs;2"

    arrCards(26) = "Ace of Diamonds;11"
    arrCards(27) = "King of Diamonds;10"
    arrCards(28) = "Queen of Diamonds;10"
    arrCards(29) = "Jack of Diamonds;10"
    arrCards(30) = "Ten of Diamonds;10"
    arrCards(31) = "Nine of Diamonds;9"
    arrCards(32) = "Eight of Diamonds;8"
    arrCards(33) = "Seven of Diamonds;7"
    arrCards(34) = "Six of Diamonds;6"
    arrCards(35) = "Five of Diamonds;5"
    arrCards(36) = "Four of Diamonds;4"
    arrCards(37) = "Three of Diamonds;3"
    arrCards(38) = "Two of Diamonds;2"

    arrCards(39) = "Ace of Spades;11"
    arrCards(40) = "King of Spades;10"
    arrCards(41) = "Queen of Spades;10"
    arrCards(42) = "Jack of Spades;10"
    arrCards(43) = "Ten of Spades;10"
    arrCards(44) = "Nine of Spades;9"
    arrCards(45) = "Eight of Spades;8"
    arrCards(46) = "Seven of Spades;7"
    arrCards(47) = "Six of Spades;6"
    arrCards(48) = "Five of Spades;5"
    arrCards(49) = "Four of Spades;4"
    arrCards(50) = "Three of Spades;3"
    arrCards(51) = "Two of Spades;2"
End Sub

So how does this script work? Well, sit down and make yourself comfortable; this could take awhile.

We start out by assigning values to a pair of variables, intHigh and intLow:

intLow = 0
intHigh = 51

In a minute, we’re going to place all 52 cards from a standard deck of cards into an array. During the course of the game, we’ll need to “deal” cards from the deck. How do we know which card has been dealt? We do that by randomly selecting a number corresponding to an index number from that array. With 52 cards in the deck, our index numbers will start at 0 and end at 51. Therefore, we use 0 and 51 as the high and low ends of our random number range.

Next we initialize three arrays:

Dim arrUserCards(8)
Dim arrDealerCards(8)
Dim arrCards(51)

As the names imply, arrUserCards will help us keep track of the cards dealt to the user; arrDealerCards will help us keep track of the cards dealt to the dealer; and arrCards represents the entire deck of cards. After setting up our arrays, we then call a subroutine named CreateDeck which loads all the cards into the array arrCards. The subroutine simply consists of 52 lines of code (corresponding to the 52 cards in a deck), with each line of code looking similar to this:

arrCards(0) = "Ace of Hearts;11"

In that line, we’re setting item 0 in the array to the Ace of Hearts. In addition, we’ve included the value of the card (11), separating it from the card name by using a semicolon.

Exciting, huh?

That brings us to this line of code:

Set objRandom = CreateObject("System.Random")

This line of creates an instance of the System.Random object. System.Random (which is used to generate random numbers) is actually a .NET Framework class that’s accessible to VBScript. We don’t need to use System.Random in this script; VBScript’s built-in random number generator would work just fine. However, because several of the events in the Advanced Division use random numbers, we decided to try our hand with System.Random, just to change things up a little.

Now it’s time to deal the first two cards to the user:

For x = 0 to 1
    Do While True
        intRandom = objRandom.Next_2(intLow,intHigh)
        If arrCards(intRandom) <> "Dealt" Then
            arrUserCards(x) = arrCards(intRandom)
            arrCards(intRandom) = "Dealt"
            Exit Do
        End If
    Loop
Next

Here we’ve set up a simple little For Next loop, one that runs from 0 to 1 (corresponding to items 0 and 1 in the array arrUserCards). Inside the loop, we set up another loop, a Do loop that runs until we’ve dealt a card to the user. And then inside that loop we use the oddly-named Next_2 method to generate a random number between o and 51. Once we have that number we then check the array to see if that card has already been dealt; we’ll be able to tell that because the value of that array item would be the string Dealt. If the card has already been dealt, we simply go back to the top of the loop and try again. If the card hasn’t been dealt, we execute these three lines of code:

arrUserCards(x) = arrCards(intRandom)
arrCards(intRandom) = "Dealt"
Exit Do

In line 1 we assign the card (that is, the value of the item in the array arrCards) to the array holding the user’s cards; in line 2, we set the value of the item in arrCards to the string Dealt. (That way, we’ll know that this card is no longer available.) We then call the Exit Do statement to exit the Do loop.

After we’ve dealt two cards to the user, we repeat the process and deal two cards to the dealer. Once the cards have been dealt, we use this block of code to reveal the user’s two cards:

For Each strCard in arrUserCards
    If strCard > "" Then
        arrBreaker = Split(strCard, ";")
        Wscript.Echo arrBreaker(0) 
        intUserTotal = intUserTotal + CInt(arrBreaker(1))
    End If
Next

All we’re doing here is looping through the items in the array arrUserCards. If an item actually has a value (that is, If strCard > "") we then use the Split function to split the card information into an array. (Remember, each array item contains both the card name and the card value.) We display the card name using this line of code:

Wscript.Echo arrBreaker(0)

Then we use this line of code to add the card value to the user’s point total:

intUserTotal = intUserTotal + CInt(arrBreaker(1))

After that we employ a similar strategy to handle the dealer’s cards; the only difference here is that, per standard blackjack rules, we reveal only one of the dealer’s cards.

Thus far, our game has played out something like this:

Your cards:
Seven of Spades
Three of Spades

Dealer's cards:
King of Spades

Now it’s time to do some serious gambling. (Note to Microsoft’s legal department: No, not really.) To begin with, we set up a Do loop designed to run until the end of time (when True is no longer equal to True) or until the end of the hand, whichever comes first:

Do While True

Inside the loop, we use the Windows Script Host StdOut and StdIn objects to prompt the user to stay (s) or hit (s):

Wscript.StdOut.Write "Stay (s) or hit (h) ?"
strChoice = WScript.StdIn.ReadLine

If the user chooses to stay, we exit the loop; that’s because the user doesn’t want any more cards. If the user chooses anything other than stay (s) we deal that user another card. That leads us into this block of code:

intUserTotal = 0

For Each strCard in arrUserCards
    If strCard > "" Then
            arrBreaker = Split(strCard, ";")
            Wscript.Echo arrBreaker(0) 
            intUserTotal = intUserTotal + CInt(arrBreaker(1))
        If intUserTotal > 21 Then
            Wscript.Echo "Over 21. Sorry, you lose."
            Wscript.Quit
        End If
    End If
Next

What we’re doing here is recalculating the user’s point total, adding up the values of all the cards in his or her hand. There’s one catch, however: if that point total exceeds 21 then the user has gone “bust” and automatically loses; in that case, we execute the following If Then block, which reports the fact that the user has gone over 21, then quits the script:

If intUserTotal > 21 Then
    Wscript.Echo "Over 21. Sorry, you lose."
    Wscript.Quit
End If

And then it’s back to the top of the loop, where we repeat the entire process, once again asking the user if he or she would like another card.

Onscreen, the action looks something like this:

Your cards:
Seven of Spades
Three of Spades

Dealer's cards:
King of Spades

Stay (s) or hit (h)?h
Seven of Spades
Three of Spades
King of Diamonds

Stay (s) or hit (h)?

Once the user decides to stay then it’s the dealer’s turn to play. After we echo the user’s total score, we use this block of code to check the point total for the dealer:

If intDealerTotal > intUserTotal Then
    Wscript.Echo "The dealer has " & intDealerTotal & ". Sorry, you lose."
    Wscript.Quit
End If

Suppose the user has 17 and the dealer already has 18 (an 8 of Clubs to go along with the King of Spades). If the dealer’s point total exceeds the user’s point total then the dealer has already won; therefore, we echo back this lamentable fact, then quit the script.

But what happens if the dealer doesn’t have more points than the user? In that case, the dealer needs to be dealt another card. With that in mind, we set up another Do loop, and deal a card. We update the dealer’s point total, then check to see if the dealer has gone over 21:

If intDealerTotal > 21 Then
    Wscript.Echo "The dealer is over 21. You win!"
    Wscript.Quit
End If

If that’s the case, then the dealer automatically loses and the script ends. If the dealer did not go over 21, we then check to see if the dealer’s point total is greater than the user’s point total:

If intDealerTotal >= intUserTotal Then
    Wscript.Echo "The dealer has " & intDealerTotal & ". Sorry, you lose."
    Wscript.Quit
End If

If so, the dealer is declared the winner and the game is over. If not, we go back to the top of loop, deal another card, and then check the point totals again. This continues until we have a winner.

The entire game might look something like this:

Your cards:
Seven of Spades
Three of Spades

Dealer's cards:
King of Spades

Stay (s) or hit (h)?h
Seven of Spades
Three of Spades
King of Diamonds

Stay (s) or hit (h)?s

You have 20.

King of Spades
Five of Diamonds

Dealers' cards:

King of Spades
Five of Diamonds
Ace of Spades
The dealer is over 21. You win!

The Scripting Guys win again!


Top of pageTop of page