Listing A. Presentation layer for SET game.

Java Primer
The Game of SET & Software Design
Vince Huston
Listing A. Presentation layer for SET game.


// Presentation layer for SET game

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import java.util.*;

public class SetGameGUI extends JPanel implements ActionListener {
  private Card[]    deckOfCards = new Card[27];
  private JFrame    parentFrame;
  private Border[]  border = new Border[2];
  private JLabel    setsFound = new JLabel( "0" );
  private JLabel    setsPresent;
  private JButton   refillSlots = new JButton( "Refill slots" );;
  private ArrayList emptySlots = new ArrayList();
  private ArrayList selectList = new ArrayList();
  private int       nextCard = 12, rows = 4;

  ///// Deal out more cards to fill all empty slots /////
  private void refillSlotsHandler() {
    JButton btn;
    String str;

    ///// Deal out replacement cards /////
    for (int i=0; i < emptySlots.size(); i++) {
      if (nextCard > 26) break;
      btn = (JButton) emptySlots.get(i);
      str = Integer.toString( deckOfCards[nextCard++].getNumber() + 1 );
      btn.setIcon( new ImageIcon( str + ".gif" ) );
      btn.setActionCommand( str );
    }
    emptySlots.clear();

    ///// Compute the number of sets present /////
    int numberOfSets = computeSetsPresent();

    ///// If there are no sets present, then add a new row /////
    while ((numberOfSets == 0) && (nextCard < 27)) {
      System.out.println( "numberOfSets was 0" );
      setLayout( new GridLayout(++rows,3,10,10) );

      JButton[] btns = new JButton[3];
      for (int i=0; i < 3; i++) {
        if (nextCard > 26) break;
        str = Integer.toString( deckOfCards[nextCard++].getNumber() + 1 );
        btns[i] = new JButton( new ImageIcon( str + ".gif" ) );
        btns[i].addActionListener( this );
        btns[i].setBorder( border[0] );
        btns[i].setActionCommand( str );
        add( btns[i] );
      }
      numberOfSets = computeSetsPresent();
    }
    setsPresent.setText( Integer.toString( numberOfSets ) );
  }

  private void handleHighlighting( JButton b ) {
    if (selectList.contains( b )) {
      b.setBorder( border[0] );
      b.repaint();
      int index = selectList.indexOf( b );
      selectList.remove( index );
      return;
    }
    if (selectList.size() == 3) return;
    selectList.add( b );
    b.setBorder( border[1] );
    b.repaint();
  }
 
  private Card findCardInDeck( int in ) {
    for (int i=0; i < deckOfCards.length; i++)
      if (deckOfCards[i].getNumber() == in) return deckOfCards[i];
    return null;
  }

  private int computeSetsPresent() {
    java.awt.Component[] wids = getComponents();
    ArrayList arr = new ArrayList();
    ArrayList ans = new ArrayList();
    int numberOfSets = 0;

    ///// Create a list of the Cards present (remove empty slots)     /////
    ///// Create a parallel list of offsets for display of the answer /////
    for (int i=0, num; i < wids.length; i++) {
      num = Integer.parseInt( ((AbstractButton)wids[i]).getActionCommand() );
      if (num != 0) {
        arr.add( findCardInDeck( num-1 ) );
        ans.add( new Integer( i+1 ) );
    } }

    ///// Enumerate all possible combinations.  For 12 cards taken 3 at a time /////
    ///// the number is 220 = 12 * 11 * 10 / 3!.  For 6 cards taken 3 at a     /////
    ///// time, the answer is 20 = 6 * 5 * 4 / 3!.                             /////
    Set thisSet;
    for (int i=0;   i < arr.size() - 2; i++)
      for (int j=i+1; j < arr.size() - 1; j++)
        for (int k=j+1; k < arr.size();     k++) {
          thisSet = new Set( (Card)arr.get(i), (Card)arr.get(j), (Card)arr.get(k) );
          if (thisSet.isValid()) {
            System.out.print( ans.get(i) +" " +ans.get(j) +" " +ans.get(k) +"   " );
            numberOfSets++;
        } }
    if (numberOfSets > 0) System.out.println();
    return numberOfSets;
  }

  /////////////////// MAIN EVENT HANDLER ///////////////////
  public void actionPerformed( ActionEvent e ) {
    if (e.getSource() == refillSlots) {
      refillSlotsHandler();
      return;
    }
    JButton btn = (JButton) e.getSource() ;
    if (btn.getActionCommand().equals("0")) return;
    handleHighlighting( btn );
    if (selectList.size() < 3) return;

    Card[] arr = new Card[3];
    for (int i=0; i < 3; i++)
      arr[i] = findCardInDeck( Integer.parseInt(
         ((AbstractButton)selectList.get(i)).getActionCommand() ) - 1 );

    Set theSet = new Set( arr[0], arr[1], arr[2] );
    if (theSet.isValid()) {
      for (int i=0, val; i < 3; i++) {
        btn = (JButton) selectList.get(i);
        btn.setBorder( border[0] );
        btn.setIcon( null );
        btn.setActionCommand( "0" );
        emptySlots.add( btn );
      }
      String str = setsFound.getText();
      setsFound.setText( "" + (Integer.parseInt( str ) + 1) );
      setsPresent.setText( Integer.toString( computeSetsPresent() ) );
      selectList.clear();
    } else
      JOptionPane.showMessageDialog( parentFrame, "NOT a set" );
  }

  public SetGameGUI( JFrame parent ) {
    parentFrame = parent;
    setLayout( new GridLayout(4,3,10,10) );
    border[0] = BorderFactory.createLineBorder( Color.lightGray, 7 );
    border[1] = BorderFactory.createLineBorder( Color.black, 7 );

    ///// Create the domain layer //////
    for (int i=0; i < deckOfCards.length; i++)
      deckOfCards[i] = new Card();

    ///// Deal out the first 12 cards /////
    JButton[] btn = new JButton[12];
    String str;
    for (int i=0; i < 12; i++) {
      str = Integer.toString( deckOfCards[i].getNumber() + 1 );
      btn[i] = new JButton( new ImageIcon( str + ".gif" ) );
      btn[i].addActionListener( this );
      btn[i].setBorder( border[0] );
      btn[i].setActionCommand( str );
      add( btn[i] );
    }

    ///// Create the widgets at the bottom of the frame /////
    JPanel bottom = new JPanel();
    parentFrame.getContentPane().add( bottom, BorderLayout.SOUTH );
    bottom.add( new JLabel( "Sets found:" ) );
    bottom.add( setsFound );
    bottom.add( new JLabel( "    " ) );
    bottom.add( new JLabel( "Sets present:" ) );
    setsPresent = new JLabel( Integer.toString( computeSetsPresent() ) );
    bottom.add( setsPresent );
    bottom.add( new JLabel( "    " ) );
    refillSlots.addActionListener( this );
    bottom.add( refillSlots );
  }

  public static void main( String[] args ) {
    JFrame f = new JFrame( "Set" );
    f.addWindowListener( new WindowAdapter() {
       public void windowClosing( WindowEvent e ) { System.exit( 0 ); } } );
    f.getContentPane().add( new SetGameGUI( f ) );
    f.pack();
    f.setLocation( 40, 40 );
    f.setVisible( true );
} }