Saturday, May 24, 2014

Android BlackJack: Getting Individual Cards

Android BlackJack: Getting Individual Cards

Android BlackJack: Getting Individual Cards

Introduction

Previously code was added in to the MainActivity class to diplay the full Bitmap for the card pictures. Now we want to get at one of the individual cards in that Bitmap and display it. This will ultimately become the code needed to display an entire hand as the game progresses. The Bitmap class has some methods that are similar to what was used in regular Java. Let's adapt the classes used in the regular Java program to take advantage of the new Android classes and methods

CardImageFactory

CardImageFactory was responsible for getting individual card images out of the larger bitmap picture of an entire deck of cards. Under Android the Bitmap class is used to manipulate these pictures. Looking at the code for Bitmaps from the Android documentation site, the Bitmap class has methods similar to the ImageBuffer class in standard Java. If we rewrite CardImageFactory class to use the appropriate Android classes then the PlayingCard class can have a Bitmap based method to return the bitmap of the particular card we want displayed. The code is fairly straight forward and changes were made to the initCardImageFactory method and the makeCardIcon method.

CardImageFactory

public class CardImageFactory {
           private static final int SUIT_COUNT = 4;
           private static final int RANK_COUNT = 13;
           private  static Bitmap fullDeckImg = null;
           public static   int width = 0;
           public static   int height = 0;

            /**
            * Initialized the .png image in memory and sets up width and height 
            * readying size parameters to cut the image up into icons. This also
            * isolates IO exceptions to this method so the individual calls 
            * do not have to have exception handling.
            * 
            * @param pathToDeck the http address of the full card image file
            * @throws MalformedURLException
            * @throws IOException 
            */
           public static void initCardImageFactory(Resources res, int resId) {
              if (fullDeckImg == null) {
                  final BitmapFactory.Options options = new BitmapFactory.Options();
                  // options.inJustDecodeBounds = true;
                  fullDeckImg = BitmapFactory.decodeResource(res, resId, options);
                  height = options.outHeight;
                  width = options.outWidth;

                  // width = fullDeckImg.getWidth();
                  // height = fullDeckImg.getHeight();
              }
           }

           /**
            * For the structure of the current Black Jack program this returns 
            * individual card images instead of a full list. This allows you to 
            * develop a class hierarchy where the image is stored at the individual
            * PlayingCard object level.
            * 
            * @param rank card face value number 0 - 12
            * @param suit suits 0 - 4
            * @return 
            */
           public static Bitmap makeCardIcon(int rank, int suit) {
                    int x = (rank * width) / PlayingCard.RANK;
                    int y = (suit * height) / PlayingCard.SUIT;
                    int w = width / PlayingCard.RANK;
                    int h = height / PlayingCard.SUIT;
                    //return fullDeckImg.getSubimage(x, y, w, h);
//                  BufferedImage cardImg = fullDeckImg.getSubimage(x, y, w, h)
                    return Bitmap.createBitmap(fullDeckImg, x, y, w, h);
           } 

}

Similar to our previous use of the Android Bitmap routines (used to display a full deck of cards) there needs to be a 'Resources' instance passed in. There also needs to be the resource ID of the main bitmap that contains the pictures of each of the cards. Since there isn't any resizing of the Bitmap going on (just a piece of the bitmap is needed) we don't set the 'inJustDecodeBounds' option. BitmapFactory.decodeResource(res, resId) pulls in the full Bitmap. This gets stored under the static field fullDeckImg. The options field get populated with the width and the height of the full bitmap. These are stored in the CardImageFactory class for later use.

Now most of the code for the makeCardIcon method can be used the same way as in standard Java. Just calculate the starting pixel address and the length and width of the individual card and use the Bitmap.createBitmap method to generate a Bitmap of the individual card.

PlayingCard

The PlayingCard class is where the actual display method is located. It is at the level of this class that we have access to the data need to calculate the card suit and face value. The getCardBitmap method is all that was added. The display card back methods have been commented out as they still have the standard Java way of doing things. This will need to be fixed when we are ready to display a real game.

public class PlayingCard {
    public static final int RANK=13;
    public static final int SUIT=4;
    private int cardno;
    private int score = 0;
    private boolean diplayBack = false;

    private String faceVal = "A23456789TJQK";
    private String suit = "CSHD";

    public PlayingCard(int cardno, int score)
    {
        this.cardno = cardno;
        this.score = score;
        this.diplayBack = false;
       // this.setIcon(CardImageFactory.makeCardIcon(cardno%RANK, cardno%SUIT));
    }

    /**
     * Get the card scoring value for blackjack face cards = 10 ace = 11 
     * all other cards equal their face value. Aces can equal 1 at times and 
     * is handled elsewhere
     * @return 
     */
    public int card_value()
    {
        return this.score;
    }

    /**
     * convert cardno into text string of face value and suit
     * @return 
     */
    public String getCardText() {
        int card_val = this.cardno % 13;
        int card_suit = this.cardno % 4;
        return this.faceVal.substring(card_val, card_val + 1)
                + this.suit.substring(card_suit, card_suit + 1);
    }

    public boolean getDisplayBack() {
        return this.diplayBack;
    }

    public void setDisplayBack(boolean disp) {
        this.diplayBack = disp;
/*        if (this.diplayBack) {
            this.setIcon(new ImageIcon(System.getProperty("user.home")+"/Downloads/classic-cards/b1fv.png"));
        }
        else {
            this.setIcon(CardImageFactory.makeCardIcon(cardno%RANK, cardno%SUIT));
        }
        */
    }

    public Bitmap getCardBitmap() {
        return CardImageFactory.makeCardIcon(cardno%RANK, cardno%SUIT);
    }
}

Lifecycle issues

There is more to the Activity life cycle than just the onCreate method (more details here1,2). The onCreate happens before all the resources are created for the main resource object R. Luckily there is an onStart() method. Overriding this method in MainActivity allows the new graphic elements (the card pictures) to be displayed properly on the very first screen the user will see.

I moved the setup of the dealer and player hands into the onStart routine and added code to initialize getting the full card bitmap. I also added the display of the full deck into this method. Now the deck will be displayed when the program starts instead of having to see the little green robot.

onStart method and onCreate changes

@Override
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (savedInstanceState == null) {
                getFragmentManager().beginTransaction()
                                .add(R.id.container, new PlaceholderFragment()).commit();
        }
}

@Override
protected void onStart() {
        super.onStart();
this.strategy = new BlackJackStrategy();
this.card = new CardDeck(this.strategy);
// Before adding graphic elements shuffle the deck of cards
this.card.shuffle();

ImageView mImageView1 = (ImageView)findViewById(R.id.imageView1);

mImageView1.setImageBitmap(
            decodeSampledBitmapFromResource(getResources(), R.raw.classic_playing_cards, 100, 100));
CardImageFactory.initCardImageFactory(getResources(), R.raw.classic_playing_cards);

}

Display a Card in the onGo Method

ImageView is the easiest way to get a picture placed on the tablet screen. As with previous additions to the display just use the graphical placement tool under Eclipse under the Fragment.xml file. The ImageView instances placed under the dealer and player hands display their text. These ImageView object were set initially to display the little green robot.

Screen additions

onGo Method changes

Now the onGo method needs to add in code to access the 2 new ImageView objects placed on the screen. Then once the objects have been initialized they can be made to receive the individual card Bitmap. The following are the changes made to the onGo method to display a card from each hand. In this case the second card of the hand.

    public void onGo(View view) {
        TextView mPlayer = (TextView)findViewById(R.id.textView6);
        TextView mDealer = (TextView)findViewById(R.id.textView5);
        EditText mBet = (EditText)findViewById(R.id.editText1);
        EditText mBkroll = (EditText)findViewById(R.id.editText2);
//      ImageView mImageView = new ImageView(this.getBaseContext());
//        ImageView mImageView1 = (ImageView)findViewById(R.id.imageView1);
        ImageView mImageView2 = (ImageView)findViewById(R.id.imageView2);
        ImageView mImageView3 = (ImageView)findViewById(R.id.imageView3);

//        mImageView1.setImageBitmap(
//                  decodeSampledBitmapFromResource(getResources(), R.raw.classic_playing_cards, 100, 100));
//        CardImageFactory.initCardImageFactory(getResources(), R.raw.classic_playing_cards);

        // Get bet and bankroll from the EditText widgets
        this.bet = Integer.parseInt(mBet.getText().toString());
        this.bankroll = Integer.parseInt(mBkroll.getText().toString());

        player = new Hand("Player");
        dealer = new Hand("Dealer");

        // Deal cards
        player.deal(this.card);
        dealer.dealHoleCard(this.card);
        player.deal(this.card);
        dealer.deal(this.card);

        // Need a clear display because display appends
        player.display(mPlayer);
        dealer.display(mDealer);
        mImageView2.setImageBitmap(dealer.cards.get(1).getCardBitmap());
        mImageView3.setImageBitmap(player.cards.get(1).getCardBitmap());
    }

Screen Before onGo Executed

Screen After onGo Executed

Clicking the 'New' button causes the onGo method to be executed. If the changes were made correctly this is what you should see (the individual cards may differ secondary to the differences in the underlying random number generator).

Conclusion

There it is individual cards can now be displayed. In the next incantation of this program the following enhancements will be made:

  • Change the display so that the dealer and player hands can take up the whole width of the screen if necessary
  • Get rid of the display of the whole deck. It was just there as an experiment to learn how to setup a Bitmap for display
  • Add in display of the back of the card for the hole card of the dealer
  • Figure out how to erase the cards from the screen in preparation for a new game.
  • Package up for deployment on an actual android device

Author: Nasty Old Dog

Validate XHTML 1.0

Wednesday, May 21, 2014

Android BlackJack: Getting Some Cards on the Table

Android BlackJack: Getting Cards on the Screen

Android BlackJack: Getting Cards on the Table

Introduction

The next step is to get rid of the ugly texted based card representation and replace it with graphics more fitting to an Android application. This short article is all about brute force. The 2 references below were all that was needed to get some graphics up on the screen. It's certainly not the card representation we are ultimately looking to use, but it provides some direction of the classes we need to use.

Android does not implement the AWT or the Swing graphics classes that a laptop can use. So the BufferedImage class is not there to help slice and dice the card images. It turns out that the Bitmap class is at the low level of image handling under android1. Then lifting code directly from an ImageView tutorial at the Android site2, The card deck we used previously can be displayed3.

Step One Create an image view object

  • Using the fragment.xml visual tool. Select an ImageView object and place it

on the screen.

  • A dialog box appears. Select program resource and select the standard Android Image for the icon to be displayed
  • Save the fragment.xml file.
  • Then go to Projects and do a clean and build.
  • Run the program in the emulator you should see a little robot on the screen.
  • I placed mine at the bottom.

Step Two

Lift the code from the android site. See the address at footnote 2. I added the 2 static methods found at the site directly to the MainActivity (calculateInSampleSize and decodeSampledBitmapFromResource).

    // the following 2 methods were taken from the Android documentation site
    // It deserves an in-line http address reference
    // http://developer.android.com/training/displaying-bitmaps/load-bitmap.html
    public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

    public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
            int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeResource(res, resId, options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeResource(res, resId, options);
    }

Step Three

The line where the methods are used (it's a little further down the page) needs to be modified from:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

to:

// Notice that R.id.imageView1 matches the element placed on the fragment
ImageView mImageView = (ImageView)findViewById(R.id.imageView1);
   mImageView.setImageBitmap(
                    decodeSampledBitmapFromResource(getResources(), R.raw.classic_playing_cards, 100, 100));

If you followed the steps properly you should see the andriod robot change to a miniature display of all the cards when you press the 'New' button and the onGo method gets executed. The next 2 figures show the difference.

Opening Blackjack Screen

BlackJack with Card Display

Conclusion

The next iteration we will need to figure out how to access just an individual card out of the whole sheet. Hopefully the Bitmap class will have similar functionality to what was available under the Java SE platform.

Footnotes:

3

Playcard images: www.jfitz.com/cards/classic-playing-cards.png

Author: Nasty Old Dog

Validate XHTML 1.0

Thursday, May 15, 2014

Android Blackjack: Adapting Old Java Code

Android Piecing Together BlackJack

Android Piecing Together BlackJack

Introduction

Now that the Eclipse Android IDE is up and running it's time to start adapting the old BlackJack code for use under Android. I am only a beginning Android programmer and I myself am using this opportunity to become better acquainted with the Android IDE and the Graphics features of the Android library. In the "Hello World" version of BlackJack the Android tools built everything for us. Now will be the time to add explicitly the Android widgets we need to get the job done

Programs are Activities

Android creates a MainActivity class. This is the main program for the application. It is the Android equivalent to the public static void main() routine in Java. Activities have a "lifecycle" and one of the methods that gets created is 'onCreate'. This is where you put some initialization code for your application. Anything else you want the application you must create your own handler methods in the ActivityMain.

Android did that

Looking over the Android generated code you see that Android used a TextView object placed on the screen to show the words "Hello World". We are going to use that element to do our own bidding. We are also going to add some more TextView elements to display other necessary output.

TextView + EditView + Buttons = input and output

The first step in recreating BlackJack on Android is to simulate the text based game. When we previously created a text based game for regular Java it helped to establish the classes for the underlying BlackJack logic and the classes that would become the playing cards on the screen. There is only one problem Android doesn't have stdin and stdout like Java on a traditional computer. So the first step is to use some simple elements in Android to simulate a textbased display.

TextView

This object displays text on the android screen. The Hello World program has one TextView. You can find it under the fragmentmain.xml file and you can manipulate in a graphical mode or by directly editing the xml of the file itself. Take a look at the properties on the right of the graphical editor display. There is a field called Text and it has a strange string representation: '@string/helloword'. Android places all strings in a strings.xml file. This is to centralize the strings so that other people can grab the file and put translations to languages other than English. In this manner your program can be easily translated into other languages. This file can be directly accessed in the res/values directory under the main project.

If you don't want to deal with the Android formalism of strings. You can just type a straight string in it will work. The IDE will spit out some complaints but it will still all run under the simulator.

The TextView widget is found under the Form Widgets heading in the Graphical layout tool. Just drag and drop to place another widget on the screen.

EditView

EditView widgets are used for user input. There are different types depending on whether you are inputting numbers or text. You can find these in the Text Fields section of the graphical layout.

Buttons

Most of these elements are quite intuitive we use them all the time. The Button widget is no exception. There are 2 properties we need to set or use to make them completely functional. First like a TextView there is a Text property that gets displayed inside the button. Second Buttons have an onClick attribute. You create a method in your Activity that is public and has a single parameter of class View. Then in the attributes for OnClick you place the name of your method. When the button is pressed the method will be called.

A first rough cut at a UI

Need an EditText for bet, bankroll. Need a button for deal, hit, stand, new game. Need some TextViews to label the EditText widgets. Using the graphical tools this is what the UI will look like:

Relative Layout

The key to placement using the graphical interface is that all the elements must be specified either relative to eachother or to the parent screen. This allows you to place elements where you want and have them display similarly on different sized devices. I threw those elements up used the text property to create a string for each of the display names you see. Just use the '…' button to the right of property:

  • click on this it opens a dialog box

  • Select new String

  • provide at least 2 values the 'R' field name and the value of the

string itself. 'R' is a generated class that interfaces all of the externalized elements (from the .xml files) and gives us access inside of Java to the info. R contains an ID for every widget placed and every property so that we get programmatic access to those values. The function of R for our purposes will become more apparent when we start some actual coding.

Combine the BlackJack code

The previous BlackJack program was strung together from scratch and functionality added as we thought of it. get closer to the final classes that will be used in the Android application.

  • CardDeck
  • BlackJackStrategy
  • Hand
  • PlayingCard

I cut and paste code from the last BlackJack game designed for regular Java. I deleted or commented the calls to the original Java graphics. Then I took the BlackJack class and threaded that code into the MainActivity. The method calls add are the following

  • onGo
  • onHit
  • onStand
  • handleDealer
  • onCreate

Hand changes

I added a new display method that takes a TextView parameter and will display the text representation of a Blackjack hand.

Button Handling

The buttons New, Hit, and Stand need to invoke code when they are pressed. The routines onGo, onHit, and onStand handle each of these button presses respectively. I took code out of the original BlackJack class and moved it to the appropriate routines. These routines all take a View parameter and the name of each routine gets placed in the Button's onClick property.

Conclusion

This may seem like a quick bit of work. What you didn't see was the week or two of part-time work getting Eclipse on my Mac, getting the right Android SDK, Getting the right Android plug-in. I also did another program before I started in on the BlackJack program. So if you're trying Android development for the first time, don't be discouraged if it seems to be taking up a ton of your time. It get's better as you gain experience (like everything in life).

If you've done everything correctly you should have the following screens when you run the App in the emulator:

Before Starting a Hand

This is a picture after I entered a bet and bankroll but before pressing New.

After Starting a Hand

This is after pressing the New Button. See that the Text representation of the Hands appear.

The Code

The MainActivity class gets generated with the project. You should cut and paste code from the MainActivity class below as opposed to replacing entirely. The other classes you can add right in to the source tree.

MainActivity.java

package com.nasty.blackjack;

import android.app.Activity;
import android.app.ActionBar;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;
import android.os.Build;

public class MainActivity extends Activity {
    private Hand player;
    private Hand dealer;
    private CardDeck card;
    private BlackJackStrategy strategy;
    private int bet;
    private int bankroll;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
                super.onCreate(savedInstanceState);
                setContentView(R.layout.activity_main);

                if (savedInstanceState == null) {
                        getFragmentManager().beginTransaction()
                                        .add(R.id.container, new PlaceholderFragment()).commit();
                }
        this.strategy = new BlackJackStrategy();
        this.card = new CardDeck(this.strategy);
        // Before adding graphic elements shuffle the deck of cards
        this.card.shuffle();


        }

        @Override
        public boolean onCreateOptionsMenu(Menu menu) {

                // Inflate the menu; this adds items to the action bar if it is present.
                getMenuInflater().inflate(R.menu.main, menu);
                return true;
        }

        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
                // Handle action bar item clicks here. The action bar will
                // automatically handle clicks on the Home/Up button, so long
                // as you specify a parent activity in AndroidManifest.xml.
                int id = item.getItemId();
                if (id == R.id.action_settings) {
                        return true;
                }
                return super.onOptionsItemSelected(item);
        }

        /**
         * onGo - handles the start of the game after the fo button is pressed
         */
    public void onGo(View view) {
        TextView mPlayer = (TextView)findViewById(R.id.textView6);
        TextView mDealer = (TextView)findViewById(R.id.textView5);
        EditText mBet = (EditText)findViewById(R.id.editText1);
        EditText mBkroll = (EditText)findViewById(R.id.editText2);

        // Get bet and bankroll from the EditText widgets
        this.bet = Integer.parseInt(mBet.getText().toString());
        this.bankroll = Integer.parseInt(mBkroll.getText().toString());

        player = new Hand("Player");
        dealer = new Hand("Dealer");

        // Deal cards
        player.deal(this.card);
        dealer.dealHoleCard(this.card);
        player.deal(this.card);
        dealer.deal(this.card);

        // Need a clear display because display appends
        player.display(mPlayer);
        dealer.display(mDealer);
    }
        /**
         * onHit - handles the hit button being pressed
         */
        public void onHit(View view) {
                TextView mPlayer = (TextView)findViewById(R.id.textView6);

        this.player.deal(this.card);
        player.display(mPlayer);
        int scr = this.player.scoreHand();
        if (scr >= 21) {
            // Player has gone bust display a message and activate continue button
            // or Player has 21 and will win if dealer has <21 or bust
            this.handleDealer(scr);
        }
        }

        /**
         * onStand - handles the rest of the game after player stands
         */
        public void onStand(View view) {
        // Player is done getting cards time to do the dealer stuff
        this.handleDealer(this.player.scoreHand());
        }

        /**
         * handleDealer
         * This is the code that gets executed after the player 
         * stands his hand.
         */
    public void handleDealer(int pscr) {
        int dscr = 0;
        TextView mDealer = (TextView)findViewById(R.id.textView5);
        String msg = "Player score: " + pscr;
        // All cards must be displayed which reset the flag and fixes a 
        // bug where after the cards are shuffled some player cards get
        // the displayBack field set from previously used dealer cards
        dealer.displayAll();
        dealer.display(mDealer);

        if (pscr > 21) {
            msg = msg + " BUST, LOSER!!!";
            bankroll -= bet;
        } else {
            for (dscr = dealer.scoreHand();
                    dscr < 17;
                    dscr = dealer.scoreHand()) {
                this.dealer.deal(card);
                dealer.display(mDealer);
            }

            // Code to score the game and settle the bet will go here
            if (pscr > dscr) {
                msg = msg + " Dealer score: "+ dscr+" ... You Win!!!";
                bankroll = bankroll + bet;
            } else if (dscr > 21) {
                msg = msg + " Dealer score: "+ dscr+": ... You Win!! Dealer has BUSTED";
                bankroll += bet;
            } else if (pscr == dscr) {
                msg = msg + " Dealer score: "+ dscr+" ... PUSH";
            } else {
                msg = msg + " Dealer score: "+ dscr+" ... LOSER!!!";
                bankroll -= bet;
            }
        }
        // Here should display a status message as to how 
        // the game was scored
        // update display of bankroll
    }

        /**
         * A placeholder fragment containing a simple view.
         */
        public static class PlaceholderFragment extends Fragment {

                public PlaceholderFragment() {
                }

                @Override
                public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                Bundle savedInstanceState) {
                        View rootView = inflater.inflate(R.layout.fragment_main, container,
                                        false);
                        return rootView;
                }
        }

}

CardDeck.java

package com.nasty.blackjack;

public class CardDeck {
    PlayingCard deck[] = new PlayingCard[52];
    int deckidx = 0; // the location of the next card to be dealt
    public CardDeck(BlackJackStrategy strategy) {
        for (int i = 0; i < 52; i++) {
            deck[i] = new PlayingCard(i, strategy.getCardScore(i));
        }

    }

    void shuffle() {
        int rindex;
        PlayingCard 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;
        }
        this.deckidx = 0;
    }

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

    public PlayingCard deal() {
        if (this.deckidx >= 52) {
            deckidx = 0;
            this.shuffle();
        }            
        return this.deck[this.deckidx++];
    }
}

Hand.java

package com.nasty.blackjack;

import java.util.ArrayList;

import android.widget.TextView;

public class Hand {
    ArrayList<PlayingCard> cards = new ArrayList<PlayingCard>();
//  ArrayList<PlayingCard> sorted = new ArrayList<PlayingCard>();

  private String displayName = "change in constructor";
  private BlackJackStrategy strategy = null;   

  /**
   * Default constructor that assumes BlackJackStrategy and labels itself "Hand"
   */
  public Hand()
  {
      this("Hand", new BlackJackStrategy());
  }

  /**
   * Assumes BlackJack Strategy but provides for a text name for the hand
   * @param displayName the title of the hand
   */
  public Hand(String displayName)
  {
      this(displayName, new BlackJackStrategy());
  }

  /**
   * The most general constructor where you can supply a strategy for a 
   * BlackJack strategy (this may be extended to provide for other games)
   * @param displayName the title of the hand
   * @param strategy BlackJack strategy being used
   */
  public Hand(String displayName, BlackJackStrategy strategy)
  {
      this.displayName = displayName;
      this.strategy = strategy;
//      this.setLayout(new FlowLayout());
//      Border blackline = BorderFactory.createLineBorder(Color.black);
//      TitledBorder hTitle = BorderFactory.createTitledBorder(blackline, this.displayName);
//      this.setBorder(hTitle);

//      this.cs = new GridBagConstraints();
/*      if ("Player".equals(this.displayName)) {
          this.cs.gridx = 0;
          this.cs.gridy = 2;
          this.cs.gridwidth = 3;            
      } else {
          this.cs.gridx = 0;
          this.cs.gridy = 1;
          this.cs.gridwidth = 3;                        
      }
      */
  }


  /**
   * Given a CardDeck it deals a card adding to the Hand ArrayList and to 
   * the JPanel list of displayable objects
   * @param deck CardDeck to deal from
   */
  public void deal(CardDeck deck)
  {
     PlayingCard c = deck.deal();
     this.cards.add(c);
     //this.add(c);       
  }

  public void dealHoleCard(CardDeck deck)
  {
      PlayingCard c = deck.deal();
      c.setDisplayBack(true);
      this.cards.add(c);
      //this.add(c);
  }

  public void displayAll()
  {
      for (PlayingCard c: this.cards) {
          if (c.getDisplayBack()) {
              c.setDisplayBack(false);
          }
      }
  }

  /**
   * sort hand is necessary for scoring a blackjack hand to account for 
   * aces being one or 11. 
   * @return ArrayList of PlayingCard in score ascending order
   */
  public ArrayList<PlayingCard> sortHand() {
      // sort hand make Aces 11 for sorting purposes
      // Use the insertion sort code and modify it by using the card_value
      // method as the way to sort

      ArrayList<PlayingCard> sorted = (ArrayList<PlayingCard>) this.cards.clone();

      // The clone statement does a deep copy of the array list
      // Just to prove it I print out the sorted field individually
      PlayingCard tmp;
      for (int i = 1; i < sorted.size(); i++) {
          for (int j = i;
                  j > 0
                  && sorted.get(j).card_value() < sorted.get(j-1).card_value();
                  j--) {
              // swap values

              tmp = sorted.get(j);
              sorted.set(j, sorted.get(j - 1));
              sorted.set(j - 1, tmp);
          }
      }
      return sorted;
  }

  /**
   * Call through to the BlackJack strategy to score the Hand
   * @return int value of score
   */
  public int scoreHand() {
      return this.strategy.getScore(this);
  }

  /** 
   * text based display to change android TextView text
   */
  public void display(TextView t) {
  //    System.out.println(this.displayName);
          String msg = new String("");
      for (PlayingCard i : cards) {
          if (i.getDisplayBack()) {
                  msg = new String(msg + "X" +" ");
          }
          else {
              msg = new String(msg + i.getCardText() + " ");
          }
      }
      // set the text view text passed in
      t.setText(msg);
  }

  /** 
   * text based display routine for testing purposes
   */
  public void display() {
      System.out.println(this.displayName);
      for (PlayingCard i : cards) {
          System.out.print(i.getCardText() + " ");
      }
      System.out.println();
  }

  public void discard()
  {
      // remove each card from the List of displayed items
//      for (PlayingCard c : this.cards) {
//          this.remove(c);
//      }

      // throw the current cards into the garbage by creating a new list
      this.cards = new ArrayList<PlayingCard>();
  }

}

BlackJackStrategy.java

package com.nasty.blackjack;

import java.util.ArrayList;

public class BlackJackStrategy {
    private int card_score[] = {11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10};
    public final static int RANK = 13;

    BlackJackStrategy() {
        super();
    }

    /**
     * Take an internal int cardno representation and return it's blackjack 
     * value
     * @param cardno an integer between 0 and 51 that represents a specific card
     * @return the blackjack scoring value of the card
     */
    public int getCardScore(int cardno) {
        return this.card_score[cardno%RANK];
    }

    /**
     * determine the score of a blackjack hand, sort the hand then figure out
     * which Aces should be 1 or 11.
     * @param h the Hand class passed in
     * @return returns the score of the hand
     */
    public int getScore(Hand h) {
        ArrayList<PlayingCard> sorted = h.sortHand();
        int score = 0;
        for (PlayingCard i : sorted) {
            // Need to test each card to see if it is an ACE
            // If an ACE and score is < 11 use 11 as ACE value otherwise use 1
            // I haven't done a proof but I believe that checking the score
            // less than 11 is sufficient to handle ACEs and you won't bust a
            // hand incorrectly
            if (i.card_value() == 11) {
                // Processing ACE
                if (score >= 11) {
                    // ACEs must all be 1
                    score += 1;
                }
                else {
                    score += 11;
                }
            }
            else {
                // Just add card_value it's not an ACE
                score += i.card_value();
            }
        }
        return score;
    }
}

PlayingCard.java

package com.nasty.blackjack;

public class PlayingCard {
    public static final int RANK=13;
    public static final int SUIT=4;
    private int cardno;
    private int score = 0;
    private boolean diplayBack = false;

    private String faceVal = "A23456789TJQK";
    private String suit = "CSHD";

    public PlayingCard(int cardno, int score)
    {
        this.cardno = cardno;
        this.score = score;
        this.diplayBack = false;
       // this.setIcon(CardImageFactory.makeCardIcon(cardno%RANK, cardno%SUIT));
    }

    /**
     * Get the card scoring value for blackjack face cards = 10 ace = 11 
     * all other cards equal their face value. Aces can equal 1 at times and 
     * is handled elsewhere
     * @return 
     */
    public int card_value()
    {
        return this.score;
    }

    /**
     * convert cardno into text string of face value and suit
     * @return 
     */
    public String getCardText() {
        int card_val = this.cardno % 13;
        int card_suit = this.cardno % 4;
        return this.faceVal.substring(card_val, card_val + 1)
                + this.suit.substring(card_suit, card_suit + 1);
    }

    public boolean getDisplayBack() {
        return this.diplayBack;
    }
    public void setDisplayBack(boolean disp) {
        this.diplayBack = disp;
/*        if (this.diplayBack) {
            this.setIcon(new ImageIcon(System.getProperty("user.home")+"/Downloads/classic-cards/b1fv.png"));
        }
        else {
            this.setIcon(CardImageFactory.makeCardIcon(cardno%RANK, cardno%SUIT));
        }
        */
    }

}

Author: Nasty Old Dog

Validate XHTML 1.0