Thursday, March 7, 2013

Modulo Part II

AP Computer Science Modulo Part II

AP Computer Science Modulo Part II

Fun with Modulo

Back to uses of modulo. Rather than expound on the virtues of modulo.

Let's make a simple blackjack game.

This will be a multi-part blog so first let's define what the game will be. Then start building the pieces.

Simple Blackjack Rules

  • Dealer and player try to make a hand as close to 21 without going over
  • Player goes first
    • may HIT (take a card) or STAND (done dealer's turn)
  • Dealer goes next must HIT or STAND according to rules
    • Hand <= 16 dealer must HIT
    • Hand > 16 dealer must STAND
  • Any hand that goes over 21 is BUST and loses immediately regardless if the dealer hasn't dealt his hand
  • Player starts with a Bankroll (total amount of money he has to bet with)
  • Player places a bet before the cards are dealt.
  • Player may bet any amount up to the value of his bankroll

Task

Design a text based input and output program to play the game of blackjack according to the rules above. Use single letter designations for the type of card (A,2,3,..,8,9,T,J,Q,K) and single letter for the suit (H,D,S,C).

You may only use a one dimensional array for the storage of a deck of cards

You must be able to shuffle the deck of cards randomly using the random number generator supplied by Java with no repeats.

Design Analysis

  • loop until player hits return rather than a value for a bet (ie. not betting ends the game)
  • print a prompt with the current bankroll when asking for a bet
  • deal hands displaying both cards of the player and XX for the hole card of dealer
  • accept user input H or S for HIT or STAND check for bust after each HIT
  • Display the hand with all the cards after each HIT
  • When S start the dealer's play hitting or standing according to the rules
  • At each HIT of the dealer redisplay the hands
  • when dealer stands determine the winner
  • update bankroll according to whether player won or lost
  • ** use only a one dimensional array to store the deck of cards **
  • Need a shuffle routine (it can't just be random()*52 I may get duplicates)

Where to Start: A Deck of Cards

  • The deck of cards there are 52 cards lets not worry about suits represent them by a number from 0 - 51
  • Let's create a CardDeck object to house them in.
  • The constructor will just initialize the one dimensional array in cardinal order
  • Let's put a shuffle method to shuffle the deck
class CardDeck {
int deck[] = new int[52];

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

void display()
{
  for(int i=0; i < 52; i++) {
    System.out.print(i + " ");
  }
  System.out.println();
}

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

  card.display();
}

And the output from the above program:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 

SHUFFLE

Obviously I want to use the random number generator but how? I could randomly select a number from 0 to 51 and move it to a new array that would become the shuffled deck. I could then shift all the values down and then pick a random number from 0 to 50, and so on until I've randomly moved all the values.

But think for a second what if I just swap the random value for the one at the end. Then pick again from 0 - 50 (ie. n-1) and so on. But then I have to do descending loops which I don't like to do. But think for a minute. If I move throught the array one at a time starting at 0 (like a for loop would) I could pick a random index and then swap the ith index with the random index. This way I just need to pick a random number between i and 51 swap and repeat the loop.

void shuffle()
{
  int rindex;
  int swap;

  for (i = 0; i <= 52; i++) {
    // this will probably need some debugging 
    rindex = Math.rand()*(52-i)+i;
    swap = card[i];
    card[i] = card[rindex];
    card[rindex] = swap;
  }
}

The random number generator needed some debugging and some casting 1. I will just display the code after debug. I did it again on the 'display' routine too, I forgot the deck[] reference and just displayed i instead. So here is the class definition so far:

public class CardDeck {

    int deck[] = new int[52];

    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;
        }
    }

    void display() {
        for (int i = 0; i < 52; i++) {
            System.out.print(deck[i] + " ");
        }
        System.out.println();
    }

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

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

Output

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 
43 15 34 41 27 14 17 2 3 36 48 12 45 7 37 4 39 13 44 8 16 29 23 46 19 10 18 42 51 1 25 0 38 20 28 31 35 9 50 21 40 11 30 32 47 24 22 6 49 26 5 33 

So how are we going to get the suits and face values of the cards from a single number? If you guessed modulo because I'm writing about modulo you're correct. There are 13 different cards for each suit and 4 different suits. It will be as easy as taking the card number and modulo 13 for value and modulo 4 for suit. To represent them in text in a more card appropriate way let's create 2 strings to hold the text values:

String faceVal = "A23456789TJQK";
String suit = "HDSC";

Then change 'display' to print the 2 character representation of each card.

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.faceVal.substring(card_val,card_val+1) + 
                          this.suit.substring(card_suit,card_suit+1) + " ");
    }
    System.out.println();
}

Output of cards in text

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

The output of your program should be the same for the top line when you initialize the deck. But you should get a different second line the results after a shuffle has taken place. That's because different computers will have different random number sequences depending on what you random seed is and how many times you run the program.

The Program So Far

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;
        }
    }

    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.faceVal.substring(card_val,card_val+1) + 
                              this.suit.substring(card_suit,card_suit+1) + " ");
        }
        System.out.println();
    }

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

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

Next Steps

We've only just begun. In the next part we will work on how we deal hands to the player and dealer. I'm going to try to do this in one class definition only. Yes, that's poor object oriented design but let's see what that would look like and then for the final part let's refactor the program into a more appropriate object oriented example.

(To Be Continued)

Footnotes:

1 Casting
The compiler is able to make some decisions of how to represent various base types as other base types. For instance double to int. A double is what we think of as a decimal number. The compiler with a 'casting' directive can return just the whole number portion of the decimal number. Placing the base type in parenthesis instructs the computer to make that modification.
For example: (int) 32.25 → 32
Likewise: (double) 32 → 32.0
This is a nice way of telling the compiler you are purposefully looking for it to make those modifications.

Author: Nasty Old Dog

No comments:

Post a Comment