Monday, March 18, 2013

Blackjack Almost a Real Game

AP Computer Science Adding Some User Input/Output

AP Computer Science Adding Some User Input/Output

Introduction

When we last left our Blackjack program we had put in place a model for dealing with a deck of cards, Shuffling the deck and a way to distinguish 'hands' for dealer and player. Now let's scaffold in some user I/O (input/output). Sounds impressive but it's just a series of prints, printlns and getLines. To be able to repeat the process for multiple games (or hands) there will need to be a loop of some sort (keyword repeat at the beginning of the sentence is the give away for a loop).

I am trying to plow through this program for you so you have a fairly large project to look at. Since the target audience for these AP Computer Science Blogs are beginning programmers I am trying to get you to see the utility of what you have learned so far. I hope that your experience with computer science has been positive. It's really not that difficult a subject and for the thoughtful student provides a virtual world where almost anything is possible. The field is really limitless and is bound only by your imagination and time.

For those of you who have been turned off by the experience of taking AP Computer Science and feel you were crazy to think this was interesting, don't let this experience jade you. AP Computer Science is a one size fits all type of course with restrictions on what should do. It may be that your imagination finds the restrictions too limiting and burdensome. At the university level you will find Professors that love Computer Science so much they have chosen to specialize in the subject. I would give it another chance at the university level. You may find that the right instructor with a broad base of experience was all you needed to get your mind around the subject.

When you do take college courses remember you have at your disposal the Professor of the course and his or her teaching assistants. That means you have multiple people with exellent experience to help you master the subject. In High School AP there is the teacher alone, if you don't connect with their perspective you are out of luck. In college you will have professors, teaching assistants, and fellow students each with a different perspective. You should be able to find someone that can connect with you to help you master this subject.

The game loop pseudocode

The design process here is:

  • guess at the calls
  • get the structure of the code in place
  • then cut and paste into the IDE and see what the real calls are supposed to be

The Pseudo Code

Pay attention to the comments below they help in understanding what is going on

CardDeck card = new CardDeck();

String user_inp = new String("");
int bankroll = 1000;
int bet = 0;           // if the user bets 0 quit the game loop

// game loop just loop forever
while(true) {
  // Tell the user the amount of the bank roll 
  System.out.println("Bankroll = " + bankroll);
  // get bet
  System.out.print("Enter the amount you want to bet: ");
  user_inp = System.in.getLine();
  bet = Integer.toInt(user_inp);

  // bet == 0 is the signal to quit loop and end program
  if (bet == 0) { 
    break;
  }

  card.shuffle();

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

  card.displayHands();

  // Need a loop to handle HIT or STAND commands
  while(true) { 
    System.out.print("Player Hand hit or stand? ");
    user_inp = System.in.getLine();
    if (user_inp.equals("HIT")) {
      card.dealPlayer();
      card.displayHands();
    }
    else if (user_inp.equals("STAND")) {
      break;
    }
    else {
      // If we end up here it's because of a typo or wiseguy
      // print error response and go back to looping
      System.out.println("I don't understand: " + user_inp);
    }
  }

  // Manually control dealer hand the same way
  // We will swap this out for code that will run the dealer rules 
  // automatically
  while(true) { 
    System.out.print("Dealer Hand hit or stand? ");
    user_inp = System.in.getLine();
    if (user_inp.equals("HIT")) {
      card.dealDealer();
      card.displayHands();
    }
    else if (user_inp.equals("STAND")) {
      break;
    }
    else {
      // If we end up here it's because of a typo or wiseguy
      // print error response and go back to looping
      System.out.println("I don't understand: " + user_inp);
    }
  }

  // Code to score the game and settle the bet will go here

}

Pseudo code to real code

  • I need to use the Scanner class to get user input there is no System.in.getLine
    • I defined uinp to be a scanner for System.in
    • I replaced the System.in.getLine's with uinp.nextLine()
  • The String userinp doesn't need a constructor just " "
  • There is no toInt in the Integer class use getInteger instead
import java.util.Scanner;

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);
        //return this.faceVal.substring(card_val, card_val+1).concat(
        //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();
        // Start game intialize bankroll to 500
        // Start game loop <CR> on bet means quit
        // CardDeck card = new CardDeck();

        String user_inp = " ";
        int bankroll = 1000;
        int bet = 0;           // if the user bets 0 quit the game loop

        // Need a Scanner for user input
        Scanner uinp = new Scanner(System.in);

        // game loop just loop forever
        while (true) {
            // Tell the user the amount of the bank roll 
            System.out.println("Bankroll = " + bankroll);
            // get bet
            System.out.print("Enter the amount you want to bet: ");
            user_inp = uinp.nextLine();
            bet = Integer.getInteger(user_inp);

            // bet == 0 is the signal to quit loop and end program
            if (bet == 0) {
                break;
            }

            card.shuffle();

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

            card.displayHands();

            // Need a loop to handle HIT or STAND commands
            while (true) {
                System.out.print("Player Hand hit or stand? ");
                user_inp = uinp.nextLine();
                if (user_inp.equals("HIT")) {
                    card.dealPlayer();
                    card.displayHands();
                } else if (user_inp.equals("STAND")) {
                    break;
                } else {
                    // If we end up here it's because of a typo or wiseguy
                    // print error response and go back to looping
                    System.out.println("I don't understand: " + user_inp);
                }
            }

            // Manually control dealer hand the same way
            // We will swap this out for code that will run the dealer rules 
            // automatically
            while (true) {
                System.out.print("Dealer Hand hit or stand? ");
                user_inp = uinp.nextLine();
                if (user_inp.equals("HIT")) {
                    card.dealDealer();
                    card.displayHands();
                } else if (user_inp.equals("STAND")) {
                    break;
                } else {
                    // If we end up here it's because of a typo or wiseguy
                    // print error response and go back to looping
                    System.out.println("I don't understand: " + user_inp);
                }
            }

            // Code to score the game and settle the bet will go here

        }
    }
}

Output

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

Bankroll = 1000
Enter the amount you want to bet: 10
Exception in thread "main" java.lang.NullPointerException
        at apcompsci.CardDeck.main(CardDeck.java:129)
Java Result: 1
BUILD SUCCESSFUL (total time: 19 seconds)

Debugging

If you use the program above 129 is not the right line number I have extra comments produced in the IDE that I don't cut and paste. So the problem is at the bet = Integer.getInteger line. It seems that our user input is not working so well. I'm not sure what is wrong here. Stepping through the code in the debugger shows the 'userinp' variable gets assigned "10", I am probably using the Integer class incorrectly. Let's take a different tact.

For AP purposes you can use the Scanner class routines (nextInt). That seems to work so I changed the following:

//            user_inp = uinp.nextLine();
//            bet = Integer.getInteger(user_inp).intValue();
            bet = uinp.nextInt();

Output 2

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

Bankroll = 1000
Enter the amount you want to bet: 10
Player Hand  
7S 2C 6D AD 
Dealer Hand:  
6H 4S JD 9D 

Player Hand hit or stand? I don't understand: 
Player Hand hit or stand? HIT
Player Hand  
7S 2C 6D AD JH 
Dealer Hand:  
6H 4S JD 9D 

Player Hand hit or stand? STAND
Dealer Hand hit or stand? HIT
Player Hand  
7S 2C 6D AD JH 
Dealer Hand:  
6H 4S JD 9D 5S 

Dealer Hand hit or stand? STAND
Bankroll = 1000
Enter the amount you want to bet: 0
BUILD SUCCESSFUL (total time: 34 seconds)

There is one major problem I left the old code from the previous blog in as a debugging aid. That's why there are four (4) cards instead of two (2) when we play the game. I need to reset the hands to no cards before we start the game.

It also looks like the CR after I input the bet gets registered as input. I will leave this error alone since it is a minor annoyance and tests the input error portion of the code (I will try to fix it in a later post).

Let's create a resetHands method. It will set all the cards in the hands to 0 and reset the card indexes for each hand. Let's place it in the code so it fixes the above problem. Let's also assume we win every hand and modify the bankroll accordingly.

public void resetHands()
{
  for (int i = 0; i < playerHand.length; i++) {
    playerHand[i] = 0;
    dealerHand[i] = 0;
  }
  dcards = 0;
  pcards = 0;
}

// Now after the first displayHands add this method call before we start:
        // Reset hands
        card.resetHands();

        // CardDeck card = new CardDeck();

        String user_inp = " ";

// Then at the end of the main loop lets add the following code:
            // Code to score the game and settle the bet will go here
            // For now assume we win every time
            bankroll = bankroll + bet;

            // reset hands to play a new game
            card.resetHands();

Output 3

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

Bankroll = 1000
Enter the amount you want to bet: 10
Player Hand  
5C 8S 
Dealer Hand:  
8D 4C 

Player Hand hit or stand? I don't understand: 
Player Hand hit or stand? HIT
Player Hand  
5C 8S 6C 
Dealer Hand:  
8D 4C 

Player Hand hit or stand? STAND
Dealer Hand hit or stand? HIT
Player Hand  
5C 8S 6C 
Dealer Hand:  
8D 4C TC 

Dealer Hand hit or stand? STAND
Bankroll = 1010
Enter the amount you want to bet: 0
BUILD SUCCESSFUL (total time: 40 seconds)

Not bad we actually won the hand for real (your results may vary). Under the rules of Blackjack the dealer had to HIT the 8,4 (12 points) since it's 16 or less. But even if we had lost we increase the bankroll anyway for now.

Conclusion

So this isn't quite polished. The program needs to score each hand after a deal and it must step through the dealer rules. It should also hide one card of the dealer until the player 'STANDS'. But the user can now step through some games and do the calculations themselves to see if it would be a winning or losing game. Don't worry we will massage this into a useable program.

To be continued.

Final Code

import java.util.Scanner;

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);
        //return this.faceVal.substring(card_val, card_val+1).concat(
        //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 resetHands() {
        for (int i = 0; i < playerHand.length; i++) {
            playerHand[i] = 0;
            dealerHand[i] = 0;
        }
        dcards = 0;
        pcards = 0;
    }

    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();
        // Reset hands
        card.resetHands();

        // CardDeck card = new CardDeck();

        String user_inp = " ";
        int bankroll = 1000;
        int bet = 0;           // if the user bets 0 quit the game loop

        // Need a Scanner for user input
        Scanner uinp = new Scanner(System.in);

        // game loop just loop forever
        while (true) {
            // Tell the user the amount of the bank roll 
            System.out.println("Bankroll = " + bankroll);
            // get bet
            System.out.print("Enter the amount you want to bet: ");
//            user_inp = uinp.nextLine();
//            bet = Integer.getInteger(user_inp).intValue();
            bet = uinp.nextInt();
            // bet == 0 is the signal to quit loop and end program
            if (bet == 0) {
                break;
            }

            card.shuffle();

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

            card.displayHands();

            // Need a loop to handle HIT or STAND commands
            while (true) {
                System.out.print("Player Hand hit or stand? ");
                user_inp = uinp.nextLine();
                if (user_inp.equals("HIT")) {
                    card.dealPlayer();
                    card.displayHands();
                } else if (user_inp.equals("STAND")) {
                    break;
                } else {
                    // If we end up here it's because of a typo or wiseguy
                    // print error response and go back to looping
                    System.out.println("I don't understand: " + user_inp);
                }
            }

            // Manually control dealer hand the same way
            // We will swap this out for code that will run the dealer rules 
            // automatically
            while (true) {
                System.out.print("Dealer Hand hit or stand? ");
                user_inp = uinp.nextLine();
                if (user_inp.equals("HIT")) {
                    card.dealDealer();
                    card.displayHands();
                } else if (user_inp.equals("STAND")) {
                    break;
                } else {
                    // If we end up here it's because of a typo or wiseguy
                    // print error response and go back to looping
                    System.out.println("I don't understand: " + user_inp);
                }
            }

            // Code to score the game and settle the bet will go here
            // For now assume we win every time
            bankroll = bankroll + bet;

            // reset hands to play a new game
            card.resetHands();
        }
    }
}

Author: Nasty Old Dog

No comments:

Post a Comment