2008 Winter Scripting Games

Solution to Advanced Perl Event 10: Blackjack!

Event 10 Solution


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

Solutions are also available for VBScript and Windows PowerShell.

*

Blackjack!


As we’ve noted before, prior to the start of the 2008 Winter Scripting Games neither of the Scripting Guys had ever written a single line of Perl code.

Note. So then why did we add Perl to the Scripting Games? That’s easy; we figured it would be fun for everyone else, even though it might be a bit … challenging … for us.

Needless to say, we approached Event 10 in the Scripting Games with a little trepidation. Writing a script that could play a hand of Blackjack was a daunting-enough task. But write that script in a language we know nothing about? You’d have to be crazy – or a Scripting Guy – to do that.

But do it we did. We won’t claim that the script we came up with is a thing of beauty; it isn’t. But it does play a single hand of Blackjack:

@arrUserCards = ();
@arrDealerCards = ();
@arrCards=();

@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";

$i = 0;

do
    {
        $b = int(rand(52) + 0);
        if ($arrCards[$b] ne "Dealt")
            {
                $arrUserCards[$i] = $arrCards[$b];
                $arrCards[$b] = "Dealt" ;
                $i++;
            }
    }
until $i == 2;

$i = 0;

do
    {
        $b = int(rand(52) + 0);
        if ($arrCards[$b] ne "Dealt")
            {
                $arrDealerCards[$i] = $arrCards[$b];
                $arrCards[$b] = "Dealt" ;
                $i++;
            }
    }
until $i == 2;

print "Your cards:\n";

for $strCard (@arrUserCards) 
    {
        @arrHand = split(';',$strCard);    
        print $arrHand[0] . "\n";
        $intUserTotal = $intUserTotal + $arrHand[1];
    }

print "\n";
print "Dealer card: \n";
print "\n";


for $strCard (@arrDealerCards) 
    {
        @arrHand = split(';',$strCard);    
        $intDealerTotal = $intDealerTotal + $arrHand[1];
    }
@arrHand = split(';',$arrDealerCards[0]);    
print $arrHand[0] . "\n";

print "\n";

$strDone = 0;

do
    {
        print "Do you want to hit (h) or stay (s): ";
        $strAction = <>;
        chomp $strAction;

        if ($strAction eq "h")
            {
                do
                    {
                        $b = int(rand(52) + 0);
                        if ($arrCards[$b] ne "Dealt")
                            {
                                $strNewCard = $arrCards[$b];
                                push(@arrUserCards, $strNewCard);
                                $arrCards[$b] = "Dealt" ;
                                $i = 100;
                            }
                     }
                until $i == 100;

                print "\n";
                print "Your cards: \n" ;

                $intUserTotal = 0;

                for $strCard (@arrUserCards) 
                     {
                         @arrHand = split(';',$strCard);    
                         print $arrHand[0] . "\n";
                         $intUserTotal = $intUserTotal + $arrHand[1];
                     }

                if ($intUserTotal > 21)
                     {
                         print "\n";
                         print "Over 21. Sorry, you lose.\n";
                         exit;
                     }
            }
    }
until $strAction eq "s";


if ($intDealerTotal >= $intUserTotal)
    {
        for $strCard (@arrDealerCards) 
            {
                @arrHand = split(';',$strCard);    
                print $arrHand[0] . "\n";
                $intUserTotal = $intUserTotal + $arrHand[1];
             }

        print "\n";
        print "The dealer has $intDealerTotal. Sorry, you lose.\n";
        exit
    }

do
    {
        $i = 99;
        do
            {
                $b = int(rand(52) + 0);
                if ($arrCards[$b] ne "Dealt")
                    {
                        $strNewCard = $arrCards[$b];
                        push(@arrDealerCards, $strNewCard);
                        $arrCards[$b] = "Dealt" ;
                        $i = 100;
                    }
            }
        until $i == 100;

        print "\n";

        $intDealerTotal = 0;

        for $strCard (@arrDealerCards)
            {
                @arrHand = split(';',$strCard);
                print $arrHand[0] . "\n";
                $intDealerTotal = $intDealerTotal + $arrHand[1];
            }


        if ($intDealerTotal > 21)
             {
                 print "\n";
                 print "The dealer is over 21. You win.\n";
                 exit;
             }

        if ($intDealerTotal >= $intUserTotal)
            {
                print "\n";
                print "The dealer has $intDealerTotal. Sorry, you lose.\n";
                exit;
            }
    }
until $x == 1000;

So how does our Blackjack script actually work? Well, it starts out by creating three empty arrays: one to hold our deck of cards(@arrCards); one to hold the cards dealt to the user (@arrUserCards); and one to hold the cards dealt to the computer/dealer (@arrDealerCards). That’s what we use these three lines of code for:

@arrUserCards = ();
@arrDealerCards = ();
@arrCards=();

After setting up our arrays, we next run through 52 lines of code that place all the cards into the array $arrCards. And, yes, we could have set up a loop that could have reduced the number of lines of code required to populate the array, and by quite a bit. However, we thought it might be easier for people visualize what was happening if we showed all 52 cards being put into the array.

Besides, the Scripting Guys just love to type.

Incidentally, each line of code looks something like this:

@arrCards[0] = "Ace of Hearts;11";

In this line of code, we’re simply assigning the Ace of Hearts to the array. In addition, we’re including the value of the card (11), separating it from the card name by using a semicolon. That makes it easy to associate a value with each card. And, as you’ll see in a minute or two, it also makes it easy for us to separate a card and its value.

At this point it’s time to deal the first two cards to the user:

do
    {
        $b = int(rand(52) + 0);
        if ($arrCards[$b] ne "Dealt")
            {
                $arrUserCards[$i] = $arrCards[$b];
                $arrCards[$b] = "Dealt" ;
                $i++;
            }
    }
until $i == 2;

Here we’ve set up a simple little do loop, one that runs until the variable $i (currently equal to 1) is equal to 2. Inside the loop we use this line of code to generate a random number between 0 and 51:

$b = int(rand(52) + 0);

Once we have that number we then check the array to see if that card has already been dealt; we can do that because, if a card has been dealt, the value of that array item will 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[$i] = $arrCards[$b];
$arrCards[$b] = "Dealt" ;
$i++;

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.) Finally, in line 3 we use the ++ operator to increment the value of $i by 1.

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:

print "Your cards:\n";

for $strCard (@arrUserCards) 
    {
        @arrHand = split(';',$strCard);    
        print $arrHand[0] . "\n";
        $intUserTotal = $intUserTotal + $arrHand[1];
    }

All we’re doing here is looping through the items in the array @arrUserCards. For each item in the array we 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 then display the card name using this line of code:

print $arrHand[0] . "\n";

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

$intUserTotal = $intUserTotal + $arrHand[1];

After that we employ a similar strategy to handle the dealer’s cards. The only difference? Following standard blackjack rules, we reveal only one of the dealer’s cards.

And yes, that does give the dealer a big advantage. And that’s the whole idea.

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

C:\scripts\games>perl test.pl
Your cards:
Four of Hearts
Three of Spades

Dealer card:

Eight of Clubs

Now the game starts to get interesting. To begin with, we set up another do loop. Inside this loop, we use the following block of code to prompt the user to hit (be dealt another card) or stay (play the hand as-is):

print "Do you want to hit (h) or stay (s): ";
$strAction = <>;
chomp $strAction;

If the user chooses to stay, we exit the loop; that’s because the user doesn’t want any more cards. In fact, our do loop has been configured to terminate as soon as the variable $strAction is equal to s:

until $strAction eq "s";

If the user chooses h (hit) we deal that user another card. That leads us into this block of code:

$intUserTotal = 0;

for $strCard (@arrUserCards) 
    {
        @arrHand = split(';',$strCard);    
        print $arrHand[0] . "\n";
        $intUserTotal = $intUserTotal + $arrHand[1];
    }

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 block, which reports the fact that the user has gone over 21, then quits the script:

if ($intUserTotal > 21)
    {
        print "\n";
        print "Over 21. Sorry, you lose.\n";
        exit;
    }

If the user’s point total isn’t over 21 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.

The play-by-play for our game might look something like this:

C:\scripts\games>perl test.pl
Your cards:
Four of Hearts
Three of Spades

Dealer card:

Eight of Clubs

Do you want to hit (h) or stay (s): h

Your cards:
Four of Hearts
Three of Spades
Three of Clubs
Do you want to hit (h) or stay (s): 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)
    {
        for $strCard (@arrDealerCards) 
            {
                @arrHand = split(';',$strCard);    
                print $arrHand[0] . "\n";
                $intUserTotal = $intUserTotal + $arrHand[1];
             }

        print "\n";
        print "The dealer has $intDealerTotal. Sorry, you lose.\n";
        exit
    }

Suppose the user has 17 and the dealer already has 18. If the dealer’s point total exceeds (or equals) the user’s point total then the dealer has already won; therefore, we echo back this sad 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 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)
    {
        print "\n";
        print "The dealer is over 21. You win.\n";
        exit;

    }

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

if ($intDealerTotal >= $intUserTotal)
    {
        print "\n";
        print "The dealer has $intDealerTotal. Sorry, you lose.\n";
        exit;
    }

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.

An entire game might look something like this:

C:\scripts\games>perl test.pl
Your cards:
Four of Hearts
Three of Spades

Dealer card:

Eight of Clubs

Do you want to hit (h) or stay (s): h

Your cards:
Four of Hearts
Three of Spades
Three of Clubs
Do you want to hit (h) or stay (s): h

Your cards:
Four of Hearts
Three of Spades
Three of Clubs
Seven of Diamonds
Do you want to hit (h) or stay (s): s

Eight of Clubs
Four of Clubs
Queen of Hearts

The dealer is over 21. You win.

Needless to say, the Scripting Guys always win. Well, as long as there isn’t any money on the line, that is.


Top of pageTop of page