Thursday, May 16, 2013

BlackJack: Extending Hand Class for Graphics

Graphical BlackJack Displaying Hands

Graphical BlackJack Displaying Hands

Introduction

In the first two articles on Graphics based BlackJack the concern was getting a card image to display, then to display all card images to make sure the underlying object system was robust enough to simulate an entire deck of cards graphically. After some playing around with Graphics2D and the Java Swing classes the design decision was made to use upper level Swing classes as the basis for the graphics design. Now it's time to extend that design to the Hand class. A Hand object is being used to collect cards for the player and the dealer. It should know how to display itself in the JFrame so the Hands can be seen by the user in the JFRame. There should also be some way of separating the hands graphically so they don't run into each other and look like one big hand.

JPanel

The JPanel object allows the developer to collect graphics objects to be displayed, grouping them in a separate graphics area inside the JFrame. By making the Hand object inherit from JPanel Player hands and Dealer hands can be separated on the screen.

Changes to the Hand Class

  • public class Hand extends JPanel{
  • Constructors have a Layout line added to them
    • this.setLayout(new FlowLayout());
  • The deal method adds the PlayingCard to the internal Hand ArrayList and to the JPanel list of displayed objects with the line:
    • this.add(c);

Hand Code

/*
 * Hand.java
 * 
 */
package blackjack;

import java.awt.FlowLayout;
import java.util.ArrayList;
import javax.swing.JPanel;

/**
 * models a hand of cards as one would expect in any card game.
 *
 * @author Nasty Old Dog
 */
public class Hand extends JPanel{
    ArrayList<PlayingCard> cards = new ArrayList<PlayingCard>();
//    ArrayList<PlayingCard> sorted = new ArrayList<PlayingCard>();

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

    public Hand()
    {
        this.displayName = "Hand";
        this.strategy = new BlackJackStrategy();
        this.setLayout(new FlowLayout());
    }

    public Hand(String displayName)
    {
        this.displayName = displayName;
        this.strategy = new BlackJackStrategy();
        this.setLayout(new FlowLayout());
    }

    public Hand(String displayName, BlackJackStrategy strategy)
    {
        this.displayName = displayName;
        this.strategy = strategy;
        this.setLayout(new FlowLayout());
    }

    public void deal(CardDeck deck)
    {
        PlayingCard c = deck.deal();
       this.cards.add(c);
       this.add(c);

    }

    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
        // If you had to copy individually (like for the AP) the code would
        // follow the structure below
        System.out.print("Cloned: ");
        for (PlayingCard i : sorted)
            System.out.print(i.getCardText() + " ");
        System.out.print("Size: " + sorted.size());
        System.out.println();
        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;
    }

    public int scoreHand() {
        return this.strategy.getScore(this);
    }

    public void display() {
        System.out.println(this.displayName);
        for (PlayingCard i : cards) {
            System.out.print(i.getCardText() + " ");
        }
        System.out.println();
    }

}

Changes to BlackJack run Method

//        PlayingCard p1 = new PlayingCard(2,2);
//        contentPane.add(p1);
        player = new Hand("Player");
        player.deal(this.card);
        player.deal(this.card);
        player.deal(this.card);
        dealer = new Hand("Dealer");
        dealer.deal(this.card);
        dealer.deal(this.card);
        dealer.deal(this.card);
        dealer.deal(this.card);

//        this.card.addPlayingCardsToFrame(contentPane);
        frame.add(player);
        frame.add(dealer);
        frame.pack();
        frame.setVisible(true);

Output

So the player hand is on top and the dealer hand is on the bottom. I can tell for the following reasons:

  • The code only deals 3 cards to the player and 4 to the dealer
  • The cards have not yet been shuffled yet so while the suits will change the cards will be in numerical order
    • ace, 2 , 3 dealt to player
    • 4, 5, 6, 7 dealt to dealer
    • The suits should follow club, spade, heart, diamond (This change was made to match to the image file previously)
  • Something needs to be done to make it more obvious. Obviously a user should not have to understand the underlying code to know what their hand is.

Borders to the Rescue

According to the The Java Tutorials at the Oracle.com site. Every JComponent can have a border. JPanel inherits from JComponent so the following should place a black line border around the Hand cards:

  • this.setBorder(BorderFactory.createLineBorder(Color.black));
  • There is commented code in the Hand class to do the above

But the line while giving better separation doesn't provide much information as to which hand is which. Java Swing provides a TitledBorder object that can be used to draw a border and provide a piece of text that becomes a title of the area being sectioned off. The changes all take place in the Hand constructors"

    public Hand()
    {
        this.displayName = "Hand";
        this.strategy = new BlackJackStrategy();
        this.setLayout(new FlowLayout());
//        this.setBorder(BorderFactory.createLineBorder(Color.black));
//        this.setBorder(BorderFactory.createLoweredBevelBorder());
        Border blackline = BorderFactory.createLineBorder(Color.black);
        TitledBorder hTitle = BorderFactory.createTitledBorder(blackline, this.displayName);
        this.setBorder(hTitle);
    }

    public Hand(String displayName)
    {
        this.displayName = displayName;
        this.strategy = new BlackJackStrategy();
        this.setLayout(new FlowLayout());
//        this.setBorder(BorderFactory.createLineBorder(Color.black));
//        this.setBorder(BorderFactory.createLoweredBevelBorder());
        Border blackline = BorderFactory.createLineBorder(Color.black);
        TitledBorder hTitle = BorderFactory.createTitledBorder(blackline, this.displayName);
        this.setBorder(hTitle);
    }

    public Hand(String displayName, BlackJackStrategy strategy)
    {
        this.displayName = displayName;
        this.strategy = strategy;
        this.setLayout(new FlowLayout());
//        this.setBorder(BorderFactory.createLineBorder(Color.black));
//        this.setBorder(BorderFactory.createLoweredBevelBorder());
        Border blackline = BorderFactory.createLineBorder(Color.black);
        TitledBorder hTitle = BorderFactory.createTitledBorder(blackline, this.displayName);
        this.setBorder(hTitle);
    }

Output Hand as JPanel with Titled Border

Conclusion

Bit by Bit the graphics are starting to look a little like a real card game. There is one other issue with Hand that I foresee and it has to do the way BlackJack is played. In all the BlackJack discussions so far we have always displayed the dealer's hand completely. This was a testing device to make sure all operations were accounted for. At some point though the dealer's "hole" card will need to be dealt with. For now I'm good to let ride like it is now. It's still helpful from a testing standpoint to see all the cards.

The next thing to deal with is how to control the graphics based game. This is usually done with JButtons and JTextFields for user input. But these will be handled in Event method calls not in the run() method. There won't be a way to run the graphical and text based side by side. I have left the text based one in for testing purposes to make sure that this still plays BlackJack as before. But at some point as the graphics controls take over there won't be any need for the text based game.

References

Author: Nasty Old Dog

Validate XHTML 1.0

No comments:

Post a Comment