Sunday, May 19, 2013

BlackJack Graphics: Adding Functional Buttons

Graphical BlackJack: Need Some Buttons

Graphical BlackJack: Need Some Buttons

Introduction

Programatic control of a Graphics Program is structurally different from how the control of a text based program occurs. Text based programs have what I term "in-line" control. Meaning the program prints a prompt and waits for a reply. It's has a linear structure, hence the term "in-line". Graphics programs have EventListeners. EventListeners are method call-backs that must be set up to respond to Mouse clicks or pressing the return key. This means that the program logic for BlackJack is going to be spread around to these event listeners. This type of structure is going to take a little design work to make sure the game plays smoothly and is able to update all of the fields that track Bankroll, Bets, Wins, and Losses.

Some new Swing Objects

Now that this BlackJack program is going graphic, User input will need to be dealt with. The major input of the text based BlackJack was for HIT or Stand, and entering the amount bet. Once that user input is taken care of the program was able to complete the game automatically by

JButton

The one thing that will be needed is a JButton object. Buttons are used to present choices to the User. Anyone that has used a word processing program knows that when they go to save a "Dialog" box appears where they can change the name of the file and then there are 2 Buttons at the bottom. One is the "Save" button and the other the "Cancel" button. This provides the user a choice to save the file or to cancel and continue to make changes.

In BlackJack the following JButtons will be needed

  • Hit Button
  • Stand Button
  • Accept Bet Button
  • There is no need of a quit button because the JFrame is enabled to quit when the user presses the red "X" button built into the frame

The JButton accepts a String in the constructor to display the name of the JButton

JTextField

There needs to be a text field to enter the Bet amount that gets accepted by the Accept Bet Button. JTextField is a swing object that allows the user to input text.

The Code Changes

Just adding the 3 JButtons and the JTextField would change the code in the BlackJack run method as follows:

   public void run() {
//SwingUtilities.invokeLater(new Runnable() {
//            public void run() {
        JFrame frame = new JFrame("BlackJack");
        Container contentPane = frame.getContentPane();


        // Set the layout manager for the JFrame using the contentPaine
        FlowLayout layout = new FlowLayout();
        contentPane.setLayout(layout);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(300,500));

//        PlayingCard p1 = new PlayingCard(2,2);
//        contentPane.add(p1);
        JButton hitButton = new JButton("Hit");
        JButton standButton = new JButton("Stand");
        JButton betButton = new JButton("Bet");
        JTextField betField = new JTextField();
        JLabel betLabel = new JLabel("Bet Amount:");

        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(dealer);
        frame.add(player);
        frame.add(hitButton);
        frame.add(standButton);
        frame.add(betLabel);
        frame.add(betField);
        frame.add(betButton);
        frame.pack();
        frame.setVisible(true);

Output

Issues

  • There seem to be some sizing issues where a Hand will not display all it's cards
  • The JTextField should be large enough to see what you type
  • In general the FlowLayout manager is not such a good fit now that there are buttons involved. Better control of the display area would be nice.
  • None of the buttons cause the program to respond to mouse clicks.

JButton Event Handler

Event Handler is a class that has methods defined to respond to various graphics user inputs. Mouse Clicks and Keyboard presses are the typical example. But if this were an animated game then a Joystick may be used and Event Handlers would be created to handle those kind of events as well.

There are any number of ways to handle events for the BlackJack game the most straight forward would be to turn the BlackJack class itself into an EventHandler and override the methods on the event handler in the BlackJack class. For JButton the event handler is called an Action Listener and it is an interface specification which means there is no default implementation. The BlackJack class will have to override the method call "actionPerformed" specified by the interface.

To allow this one method to handle multiple buttons each button sets a text string command by calling the method setActionCommand on the JButton. The actionPerformed method would then test the ActionEvent parameter passed in to see which command is being sent and then act accordingly.

The following method calls will be used

  • betButton.setActionCommand("bet");
  • hitButton.setActionCommand("hit");
  • standButton.setActionCommand("stand");
  • need setActionListener(this) called with each of the buttons.
  • The actionPerformed method will be outfitted to add a card when the Hit button is pressed
  • Let's move the JButton definitions to fields in the BlackJack class since JButtons can be disabled and that may be functionality that could come in handy.
  • The JTextField needs to be a field so the actionPerformed method can pull the data out of it when the Bet button is clicked

Here are the changes made so far in the BlackJack Class:

public class BlackJack implements ActionListener{

    CardDeck card;
    Hand player;
    Hand dealer;
    BlackJackStrategy strategy = new BlackJackStrategy();
    JFrame frame = new JFrame("BlackJack");
    JButton hitButton = new JButton("Hit");
    JButton standButton = new JButton("Stand");
    JButton betButton = new JButton("Bet");
    JTextField betField = new JTextField();

    public BlackJack()
    {
        this.card = new CardDeck(this.strategy);   
    }

    /**
     * Handles all the graphics user input
     * @param ae ActionEvent generated by JButtons in the frame
     */
    @Override
    public void actionPerformed(ActionEvent ae) {
        if ("hit".equals(ae.getActionCommand())) {
            // Hit button press add a card to the player hand then redisplay frame
            this.player.deal(this.card);
            this.player.repaint();
            frame.validate();
            frame.repaint();
        }
    }

    /**
     * run method that does all the work of this class
     */
    public void run() {
//SwingUtilities.invokeLater(new Runnable() {
//            public void run() {
        Container contentPane = frame.getContentPane();


        // Set the layout manager for the JFrame using the contentPaine
        FlowLayout layout = new FlowLayout();
        contentPane.setLayout(layout);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(300,500));

//        PlayingCard p1 = new PlayingCard(2,2);
//        contentPane.add(p1);
        // Need a label for the JTextField
        JLabel betLabel = new JLabel("Bet Amount:");

        // set the different Action Command Strings
        betButton.setActionCommand("bet");
        hitButton.setActionCommand("hit");
        standButton.setActionCommand("stand");

        // set this BlackJack class as the handler for each of the buttons
        betButton.addActionListener(this);
        hitButton.addActionListener(this);
        standButton.addActionListener(this);

        // Set up some hands
        player = new Hand("Player");
        player.deal(this.card);
        player.deal(this.card);
        dealer = new Hand("Dealer");
        dealer.deal(this.card);
        dealer.deal(this.card);

        // Add all the objects into the fram so they can be displayed
        frame.add(dealer);
        frame.add(player);
        frame.add(hitButton);
        frame.add(standButton);
        frame.add(betLabel);
        frame.add(betField);
        frame.add(betButton);
        frame.pack();
        frame.setVisible(true);
//            }
//});

This doesn't work other than showing the buttons and displaying the 2 dealer cards and 2 player cards. The positions have been change since all the buttons have their effects done on the player hand. This also has the added benefit of mimicking a BlackJack table where the player hand is at the bottom of the table and the dealer hand at the top.

Make Hit Button Work

Some investigation of the Hand displaying problems has to do with the fact that the text code is being performed and that is changing what is in the Hand fields for the Player and Dealer (they are being set to new Hand). Intially this is not a problem because because the Frame has not been manipulated. But now that the Hit button is being executed it will cause a problem and cards will be missing.

It's also time to fix how the JFrame is supposed to be called in the Java Tutorials.

  • First comment out all the text based code
  • Implement the Runnable interface on the BlackJack Class. The run method is already implemented so just add an "@Override" statement before the method definition
  • Add SwingUtilities.invokeLater(game); in place of the game.run() call

The Runnable interface is used for threading and it seems that Java Swing is happier when a graphics program is invoked in Java threading style using the SwingUtilities class rather than invoking the "run" method directly.

The new BlackJack Class is as follows:

/*
 * BlackJack a simple implementation upgrade for graphical card display
 */
package blackjack;

import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Scanner;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

/**
 * This class controls the game logic of this simple implementation of 
 * Black Jack. A more profession version would allow for multiplayer games
 * via a game server so you could play your friends on the internet. 
 * Some improvements would be:
 * <p>
 *    Adding insurance (players could insure bets against a dealer having blackjack) <p>
 *    Increase the payout for a blackjack hand to 1.5 x the original bet <p>
 *    Double Down <p>
 *    Splitting pairs <p>
 * 
 * @author Nasty Old Dog
 */
public class BlackJack implements ActionListener, Runnable{

    CardDeck card;
    Hand player;
    Hand dealer;
    BlackJackStrategy strategy = new BlackJackStrategy();
    JFrame frame = new JFrame("BlackJack");
    JButton hitButton = new JButton("Hit");
    JButton standButton = new JButton("Stand");
    JButton betButton = new JButton("Bet");
    JTextField betField = new JTextField();

    public BlackJack()
    {
        this.card = new CardDeck(this.strategy);   
    }

    /**
     * Handles all the graphics user input
     * @param ae ActionEvent generated by JButtons in the frame
     */
    @Override
    public void actionPerformed(ActionEvent ae) {
        if ("hit".equals(ae.getActionCommand())) {
            // Hit button press add a card to the player hand then redisplay frame
            this.player.deal(this.card);

            // Once the card is added you need to revalidate the frame and repaint
            frame.validate();
            frame.repaint();
        }
    }

    /**
     * run method that does all the work of this class
     */
    @Override
    public void run() {
//SwingUtilities.invokeLater(new Runnable() {
//            public void run() {
        Container contentPane = frame.getContentPane();


        // Set the layout manager for the JFrame using the contentPaine
        FlowLayout layout = new FlowLayout();
        contentPane.setLayout(layout);

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setPreferredSize(new Dimension(300,500));

//        PlayingCard p1 = new PlayingCard(2,2);
//        contentPane.add(p1);
        // Need a label for the JTextField
        JLabel betLabel = new JLabel("Bet Amount:");

        // set the different Action Command Strings
        betButton.setActionCommand("bet");
        hitButton.setActionCommand("hit");
        standButton.setActionCommand("stand");

        // set this BlackJack class as the handler for each of the buttons
        betButton.addActionListener(this);
        hitButton.addActionListener(this);
        standButton.addActionListener(this);

        // Set up some hands
        player = new Hand("Player");
        player.deal(this.card);
        player.deal(this.card);
        dealer = new Hand("Dealer");
        dealer.deal(this.card);
        dealer.deal(this.card);

        // Add all the objects into the fram so they can be displayed
        frame.add(dealer);
        frame.add(player);
        frame.add(hitButton);
        frame.add(standButton);
        frame.add(betLabel);
        frame.add(betField);
        frame.add(betButton);
        frame.pack();
        frame.setVisible(true);
/*
        this.card.display();
        this.card.shuffle();
        this.card.display();

        // Now I just need to create the hand objects
        this.player = new Hand("Player Hand");
        this.dealer = new Hand("Dealer Hand");

        this.player.deal(card);
        this.dealer.deal(card);
        this.player.deal(card);
        this.dealer.deal(card);

        this.player.display();
        this.dealer.display();
        System.out.println("Player score = " + player.scoreHand());
        System.out.println("Dealer score = " + dealer.scoreHand());

          .
          .
          .

// Commented code cut out for space savings don't throw it away yet
// this code is going to need to be integrated into the actionPerformed
// method to run the game logic

          .
          .
          .

            // Code to score the game and settle the bet will go here
            // For now assume we win every time
            System.out.println("Player score: " + scr + "   Dealer: " + dscr);
            if (scr > dscr) {
                System.out.println("You Win!!!");
                bankroll = bankroll + bet;
            } else if (dscr > 21) {
                System.out.println("You Win!! Dealer has BUSTED");
                bankroll += bet;
            } else if (scr == dscr) {
                System.out.println("PUSH");
            } else {
                System.out.println("LOSER!!!");
                bankroll -= bet;
            }
            // reset hands to play a new game
            this.player = new Hand("Player Hand");
            this.dealer = new Hand("Dealer Hand");


        }
         * 
         */
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            CardImageFactory.initCardImageFactory("http://www.jfitz.com/cards/classic-playing-cards.png");
            BlackJack game = new BlackJack();

            SwingUtilities.invokeLater(game);
        } catch (MalformedURLException ex) {
            System.out.println(ex.getMessage());
        } catch (IOException ex) {
            System.out.println(ex.getMessage());
        }        
    }

 }

Output Intitial Window

Output Hit Button Pressed Once

Output Hit Button Pressed Twice

Conclusion

This is coming along nicely now. Graphically each feature added so far makes the program look and feel like a decent card game simulation. There are still a few things that need to be done.

  • The JTextField need to be stretched so that User Input can be captured and seen
  • The stand button needs to implemented (It doesn't do much graphically but triggers the program to handle the dealer Hand)
  • The initial entry into the program needs to be fixed so the Bet can be entered and the button logic is in place
    • Hit button should be disabled until the hands are dealt and the Bet button pressed
    • Stand button same as for Hit (There are enable and disable methods in the JButton class)
  • The Layout manager needs to be investigated so the buttons are fixed but the hands can expand
  • There needs to be a display of the player's current bankroll

For the Novice programmer take note of how bits and pieces of this project are being put together. When ever a developer is investigating a new library of code they usually step through small portions of the library's functionality. Then pull it all together into their design. This investigation into the Java Swing classes is no different. Java Swing is a big library with a large amount of options. It's important to get things functional and then to investigate the details when the simple use of the library no longer fits the design goals.

References

  1. The Java Tutorials, "How to Use Buttons, Check Boxes, and Radio Buttons" http://docs.oracle.com/javase/tutorial/uiswing/components/button.html

Author: Nasty Old Dog

Validate XHTML 1.0

No comments:

Post a Comment