Wednesday, May 22, 2013

BlackJack and GridBagLayout

BlackJack and GridBagLayout

BlackJack and GridBagLayout

Introduction

In a flurry of hacking the Graphics Based BlackJack program was thrown together. However the Graphics are inadequate in a number of ways, the biggest of which is the choice of Layout Manager. The FlowLayout moves objects around depending on the area of the JFrame after a mouse resize operation. It's functional for testing game logic but not worthy to be considered a real prototype for a graphical BlackJack program.

The designers of the Java Swing API think so as well. Not necessarily of this BlackJack program in particularly, but they had the foresight to provide different LayoutManagers and make them general enough to handle a variety of graphic object placement issues. The Java Tutorials are again used to investigate the functionality of one of the more versatile Layout Managers: the GridBagLayout manager (see the References for a link).

Essential Elements of GridBagLayout

The functionality is similar to creating a table in a word processor or using a spreadsheet. A grid is defined implicitly and through the use of constraint objects the graphics objects are place in the grid. Implicitly in this sense means that the Swing GridBagLayout manager class infers the grid size from the grid coordinates provided in the constraint objects. Then using an overloaded "add" method to the ContentPane of the JFrame object both the graphics object and it's corresponding constraints object are added to the JFrame ContentPane.

The changes required to the BlackJack class will be to add a new contraint object definition with each object to be displayed. The pseudo code will take the following form:

Object definition and initialization
Constraint definition 
contentPane.add(SwingObject, Constraints);
// repeat the above for each object to be placed in the GridBagLayout

Grid Design

The following block diagram shows the arrangement of the Swing objects and shows the grid coordinates for each object:

From the above diagram the grid coordinates of where the objects are placed in the JFrame relative to each other are established:

Object Coordinates (gridx,gridy) Span
Message 0,0 3 boxes
Bankroll 3,0 1 box
Dealer Hand 0,1 3 boxes
betLabel 3,1 1 box
Player Hand 0,2 3 boxes
betTextField 3,2 1 box
hitButton 0,3 1 box
standButton 1,3 1 box
continueButton 2,3 1 box
betButton 3,3 1 box

GridBag Constraints

The constraints needed to improve the placement of the BlackJack Swing Objects are as follows

  • For Grid placement
    • GridBagConstraint.gridx specifies the column of the grid
    • GridBagConstraint.gridy specifies the row of the grid
  • For Grid Span (ie. how manch grid boxes the object takes
    • GridBagConstraint.gridwidth specifies the number of columns the object uses
    • GridBagConstraint.gridheight specifies the number of rows the object uses

The objects are still somewhat tied together in the fact that if they share a row that has a taller object the other objects may not get displayed evenly. This is a detail best saved for when objects are actually placed and then investigate what constraints may be necessary to fix the display of the objects

First Cut at GridBag Code

The following must be exchanged for where the Objects are added to the frame content pane:

// Set Constraints of the displayed objects
// Add all the objects into the fram so they can be displayed
GridBagConstraints constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 0;
constraints.gridwidth = 3;
frame.add(infoLabel, constraints);

constraints = new GridBagConstraints();
constraints.gridx = 3;
constraints.gridy = 0;
constraints.gridwidth = 1;
frame.add(bankLabel, constraints);

constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 1;
constraints.gridwidth = 3;
frame.add(dealer, constraints);

constraints = new GridBagConstraints();
constraints.gridx = 3;
constraints.gridy = 1;
constraints.gridwidth = 1;
frame.add(betLabel, constraints);

constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 3;
frame.add(player, constraints);

constraints = new GridBagConstraints();
constraints.gridx = 3;
constraints.gridy = 2;
constraints.gridwidth = 1;
frame.add(betField, constraints);

constraints = new GridBagConstraints();
constraints.gridx = 0;
constraints.gridy = 3;
constraints.gridwidth = 1;
frame.add(hitButton, constraints);

constraints = new GridBagConstraints();
constraints.gridx = 1;
constraints.gridy = 3;
constraints.gridwidth = 1;
frame.add(standButton, constraints);

constraints = new GridBagConstraints();
constraints.gridx = 2;
constraints.gridy = 3;
constraints.gridwidth = 1;
frame.add(continueButton, constraints);

constraints = new GridBagConstraints();
constraints.gridx = 3;
constraints.gridy = 3;
constraints.gridwidth = 1;
frame.add(betButton, constraints);

frame.pack();
frame.setVisible(true);

The above code when added and run displays the objects in a nasty shrunken form it turns out the window is not sized properly and needs to be larger. Changing the frame preferredSize field will make the objects display in their new relative positions at the size they were in the previous version of the program.

Output 1

The following line of code should be changed near the beginning of the "run" method:

  • From
    • frame.setPreferredSize(new Dimension(550, 450));
  • To
    • frame.setPreferredSize(new Dimension(650, 550));

Now the larger frame displays all the elements as expected.

Output 2

Add Constraints to the resetFrame Method

Since resetFrame and the run method go through the same Objects and Constraints the above code using constraints was moved out of the "run" method and into a method called setConstraints.

/** 
 * This method centralizes the constraints for object placement in a 
 * GridBagLayout manager. It is used at start-up and to reset the 
 * game window after each round
 */
public void setConstraints()
{
            // Set Constraints of the displayed objects
    // Add all the objects into the fram so they can be displayed
    GridBagConstraints constraints = new GridBagConstraints();
    constraints.gridx = 0;
    constraints.gridy = 0;
    constraints.gridwidth = 3;
    frame.add(infoLabel, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 3;
    constraints.gridy = 0;
    constraints.gridwidth = 1;
    frame.add(bankLabel, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 0;
    constraints.gridy = 1;
    constraints.gridwidth = 3;
    frame.add(dealer, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 3;
    constraints.gridy = 1;
    constraints.gridwidth = 1;
    frame.add(betLabel, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 0;
    constraints.gridy = 2;
    constraints.gridwidth = 3;
    frame.add(player, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 3;
    constraints.gridy = 2;
    constraints.gridwidth = 1;
    frame.add(betField, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 0;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    frame.add(hitButton, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 1;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    frame.add(standButton, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 2;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    frame.add(continueButton, constraints);

    constraints = new GridBagConstraints();
    constraints.gridx = 3;
    constraints.gridy = 3;
    constraints.gridwidth = 1;
    frame.add(betButton, constraints);

}
/**
 * This reset the entire frame and reloads all the displayable objects in 
 * the proper order
 */
public void resetFrame() {
    frame.getContentPane().removeAll();
    player.setPreferredSize(new Dimension(500,150));
    dealer.setPreferredSize(new Dimension(500,150));

    // Set Constraints of the displayed objects
    // Add all the objects into the fram so they can be displayed
    this.setConstraints();

    frame.validate();
    frame.repaint();
}

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


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

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setPreferredSize(new Dimension(650, 550));


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

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

    // Disable Hit and Stand buttons
    hitButton.setEnabled(false);
    standButton.setEnabled(false);
    continueButton.setEnabled(false);

    // Set size of the bet JTextField 
    betField.setPreferredSize(new Dimension(75, 25));

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

    // Set a preferred size for the Hand JPanel
    player.setPreferredSize(new Dimension(500,150));
    dealer.setPreferredSize(new Dimension(500,150));

    // Set Constraints of the displayed objects
    // Add all the objects into the fram so they can be displayed
    this.setConstraints();

    frame.pack();
    frame.setVisible(true);
}

Conclusion

The new GridBagLayout manager keeps the game elements stationary on the screen during play. Previous changes to field size could have an effect on the FlowLayout manager and cause the objects to move around. This is annoying to the User of the program. The spacing of the elements under GridBagLayot is adequate and the game has a more professional feel to it from the FlowLayout manager.

In a perfect world the betLabel would share the space with betTextField. They go together so they should be near each other. The bankroll display looks a little out of place even with the info message at the top of the window. There are also shuffling issues to deal with. The testing so far has been done without shuffling the deck. The CardDeck class can also run out of cards as no arrangement has been made to reshuffle when the deck is low on cards.

One bug has been discovered during testing. The first hand is a push and the bank roll remains at 1000 in the subsequent round. But the next hand is a loser if Stand is pressed. The program takes out double the amount when the new Bankroll is displayed at the start of the next round.

References

  1. The Java Tutorials: How to Use GridBagLayout http://docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html

Author: Nasty Old Dog

Validate XHTML 1.0

No comments:

Post a Comment