Friday, March 8, 2013

Blackjack (adding more features)

AP Computer Science Blackjack Continued

AP Computer Science Blackjack Continued

Representing Dealer and Player Hands

Last time we created a CardDeck class to simulate a deck of cards. Now we need a way to deal those cards and store them in 'hands' for a player and a dealer.

  • Use arrays for the dealer and player hands
  • put the arrays in CardDeck class.
  • create 'dealPlayer' and 'dealDealer' methods to store cards in the arrays associated with each hand
  • Use int variables to track the indexs of the hands and the deck
    • increment the index after each deal so we get the next card from the deck
    • increment the index for the appropriate hand
  • create a method to display the hands with only one card turned over on the dealer
  • create a method to display all the hands so that a winner can be determined by the user for now
  • Hold off on creating a hand scoring mechanism
int playerHand[20];
int dealerHand[20];
int dcards = 0; // the number of cards in the dealer hand so far
int pcards = 0; // the number of cards in the player hand so far
int deckidx = 0; // the location of the next card to be dealt

public int deal()
{
  if (deckidx < 52) {
    return deck[deckidx++]; // return the top card and increment to the next card
  }
  else {
    return deck[deckidx-1];
  }
}

public void dealPlayer()
{
  playerHand[pcards] = deal();
  pcards++;
}

public void dealDealer()
{
  dealerHand[dcards] = deal();
  dcards++;
}

public void displayHands()
{
  System.out.println("Player Hand  ");
  for (int i = 0; i < pcards; i++) {
    System.out.print(playerHand[i]+" ");
  }
  System.out.println();
  System.out.println("Dealer Hand:  ");
  for (int j = 0; j < dcards; j++) {
    System.out.print(dealerHand[j],+" ");
  }
  System.out.println();
  System.out.println();
}

Bugs

I have a ',' in the print statement in the 'displayHands' routine. I have forgotten my style requirements that I use 'this' prepended to all field usage. I need to initialize the arrays for hand.

Fixed code with usage in 'main' method

int playerHand[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int dealerHand[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int dcards = 0; // the number of cards in the dealer hand so far
int pcards = 0; // the number of cards in the player hand so far
int deckidx = 0; // the location of the next card to be dealt

public int deal()
{
  if (this.deckidx < 52) {
    return this.deck[this.deckidx++]; // return the top card and increment to the next card
  }
  else {
    return this.deck[this.deckidx-1];
  }
}

public void dealPlayer()
{
  this.playerHand[pcards] = deal();
  this.pcards++;
}

public void dealDealer()
{
  this.dealerHand[dcards] = deal();
  this.dcards++;
}

public void displayHands()
{
  System.out.println("Player Hand  ");
  for (int i = 0; i < this.pcards; i++) {
    System.out.print(this.playerHand[i]+" ");
  }
  System.out.println();
  System.out.println("Dealer Hand:  ");
  for (int j = 0; j < this.dcards; j++) {
    System.out.print(this.dealerHand[j] +" ");
  }
  System.out.println();
  System.out.println();
}

    public static void main(String[] args) {
        CardDeck card = new CardDeck();

        card.display();
        card.shuffle();
        card.display();

        card.dealPlayer();
        card.dealDealer();
        card.dealPlayer();
        card.dealDealer();

        card.displayHands();
    }

Make a card convert routine

The above example does not display the text based card. It would be nice to have a method that converts the integer representation into the text based representation. This can then be used in both the hand display method and the deck display method. Good design too use the same underlying method in both places means less chance for error. If there is an error found localizing it to one method means it's fixed everywhere when the bug is fixed in the conversion method. Call the method 'getCardText' and the method can just return a String (right? makes sense, it's just going to be a couple of characters). Let's modify the deck display routine (i.e. the 'display' method) to make use of this conversion routine as well (don't forget I use method, routine, and function interchangeably I hope it's not too confusing).

public String getCardText (int crd) 
{
  int card_val = crd%13;
  int card_suit = crd%4;
  return this.faceVal.substring(card_val,card_val+1) + 
         this.suit.substring(card_suit,card_suit+1);    // Why does this work??
}

Digression on the String Class

I flagged the return statement with a comment. Do you the reader understand why that code actually works? There is really a great deal of Java compiler magic happening in that one statement. It's the same magic that happens implicitly in a System.out.print method call. 'faceVal' and 'suit' are 'String' fields in the CardDeck class. 'substring' is a method in the 'String' class that returns a 'String' class (or object if you prefer). The 2 calls to substring (one for 'faceVal' and the other for 'suit') will get done first. Then the compiler will be faced with what to do with 'String' + 'String'. It turns out that the '+' operator has built in meaning when dealing with Strings. It will concatenate the 2 strings together (which essential glues them together) to form a new String. It would be the same as if we used the 'concat' method of the 'String' class:

return this.faceVal.substring(card_val,card_val+1).concat(this.suit.substring(card_suit,card_suit+1));

Either return statement works. I prefer the '+' operator because it gives me a natural split to place the long method calls on separate lines.

Back to the final code

So now fixing up all the routines the code is now:

public class CardDeck {

    int deck[] = new int[52];
String faceVal = "A23456789TJQK";
String suit = "HDSC";

    public CardDeck() {
        for (int i = 0; i < 52; i++) {
            deck[i] = i;
        }
    }

    void shuffle() {
        int rindex;
        int swap;

        for (int i = 0; i < 52; i++) {
            // each time through the loop there are less numbers to randomize
            // 52 - i to be exact. But then everything from 0 to i-1 has already
            // been selected at random so add i to the random number to get the
            // appropriate index
            rindex = (int) ((Math.random() * ((double) (52 - i))) + i);
            swap = deck[i];
            deck[i] = deck[rindex];
            deck[rindex] = swap;
        }
    }

    public String getCardText (int crd) 
{
  int card_val = crd%13;
  int card_suit = crd%4;
  return this.faceVal.substring(card_val,card_val+1) + 
         this.suit.substring(card_suit,card_suit+1);
}

    void display() {
        int card_val;
        int card_suit;
        for (int i = 0; i < 52; i++) {
            //card_val = deck[i]%13;
            //card_suit = deck[i]%4;
            System.out.print(this.getCardText(deck[i]) + " ");
        }
        System.out.println();
    }

int playerHand[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int dealerHand[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int dcards = 0; // the number of cards in the dealer hand so far
int pcards = 0; // the number of cards in the player hand so far
int deckidx = 0; // the location of the next card to be dealt

public int deal()
{
  if (this.deckidx < 52) {
    return this.deck[this.deckidx++]; // return the top card and increment to the next card
  }
  else {
    return this.deck[this.deckidx-1];
  }
}

public void dealPlayer()
{
  this.playerHand[pcards] = deal();
  this.pcards++;
}

public void dealDealer()
{
  this.dealerHand[dcards] = deal();
  this.dcards++;
}

public void displayHands()
{
  System.out.println("Player Hand  ");
  for (int i = 0; i < this.pcards; i++) {
    System.out.print(this.getCardText(this.playerHand[i])+" ");
  }
  System.out.println();
  System.out.println("Dealer Hand:  ");
  for (int j = 0; j < this.dcards; j++) {
    System.out.print(this.getCardText(this.dealerHand[j]) +" ");
  }
  System.out.println();
  System.out.println();
}

    public static void main(String[] args) {
        CardDeck card = new CardDeck();

        card.display();
        card.shuffle();
        card.display();

        card.dealPlayer();
        card.dealDealer();
        card.dealPlayer();
        card.dealDealer();

        card.displayHands();
    }
}

Output

AH 2D 3S 4C 5H 6D 7S 8C 9H TD JS QC KH AD 2S 3C 4H 5D 6S 7C 8H 9D TS JC QH KD AS 2C 3H 4D 5S 6C 7H 8D 9S TC JH QD KS AC 2H 3D 4S 5C 6H 7D 8S 9C TH JD QS KC 
8C 6H 4D 4S 5S 5C 2C KD 2S 3D 6C 9D 7S 8S QS 5H QD 3S JS 3H 7D TD KH TC 7C 9H 8H 8D TS JH QH 4C AC 9S 5D 9C AS 4H JD QC TH 2H 2D JC KS KC 6S 6D 3C AH AD 7H 
Player Hand  
8C 4D 
Dealer Hand:  
6H 4S 

Conclusion

Not bad. The class can shuffle, deal hands, and display hands. In the next installment let's work on the 'main' method to accept a bankroll and bets. Have it accept input to HIT or STAND on the player hand. The dealer can just take 1 card for now as it's hand playing rule. Hopefully we can simulate a game

Author: Nasty Old Dog

No comments:

Post a Comment