Source of TicTacToeClient.java


  1: // Fig. 24.15: TicTacToeClient.java
  2: // Client that let a user play Tic-Tac-Toe with another across a network.
  3: import java.awt.BorderLayout;
  4: import java.awt.Dimension;
  5: import java.awt.Graphics;
  6: import java.awt.GridLayout;
  7: import java.awt.event.MouseAdapter;
  8: import java.awt.event.MouseEvent;
  9: import java.net.Socket;
 10: import java.net.InetAddress;
 11: import java.io.IOException;
 12: import javax.swing.JFrame;
 13: import javax.swing.JPanel;
 14: import javax.swing.JScrollPane;
 15: import javax.swing.JTextArea;
 16: import javax.swing.JTextField;
 17: import javax.swing.SwingUtilities;
 18: import java.util.Formatter;
 19: import java.util.Scanner;
 20: import java.util.concurrent.Executors;
 21: import java.util.concurrent.ExecutorService;
 22: 
 23: public class TicTacToeClient extends JFrame implements Runnable 
 24: {
 25:    private JTextField idField; // textfield to display player's mark
 26:    private JTextArea displayArea; // JTextArea to display output
 27:    private JPanel boardPanel; // panel for tic-tac-toe board
 28:    private JPanel panel2; // panel to hold board
 29:    private Square board[][]; // tic-tac-toe board
 30:    private Square currentSquare; // current square
 31:    private Socket connection; // connection to server
 32:    private Scanner input; // input from server
 33:    private Formatter output; // output to server
 34:    private String ticTacToeHost; // host name for server
 35:    private String myMark; // this client's mark
 36:    private boolean myTurn; // determines which client's turn it is
 37:    private final String X_MARK = "X"; // mark for first client
 38:    private final String O_MARK = "O"; // mark for second client
 39: 
 40:    // set up user-interface and board
 41:    public TicTacToeClient( String host )
 42:    { 
 43:       ticTacToeHost = host; // set name of server
 44:       displayArea = new JTextArea( 4, 30 ); // set up JTextArea
 45:       displayArea.setEditable( false );
 46:       add( new JScrollPane( displayArea ), BorderLayout.SOUTH );
 47: 
 48:       boardPanel = new JPanel(); // set up panel for squares in board
 49:       boardPanel.setLayout( new GridLayout( 3, 3, 0, 0 ) );
 50: 
 51:       board = new Square[ 3 ][ 3 ]; // create board
 52: 
 53:       // loop over the rows in the board
 54:       for ( int row = 0; row < board.length; row++ ) 
 55:       {
 56:          // loop over the columns in the board
 57:          for ( int column = 0; column < board[ row ].length; column++ ) 
 58:          {
 59:             // create square
 60:             board[ row ][ column ] = new Square( " ", row * 3 + column );
 61:             boardPanel.add( board[ row ][ column ] ); // add square       
 62:          } // end inner for
 63:       } // end outer for
 64: 
 65:       idField = new JTextField(); // set up textfield
 66:       idField.setEditable( false );
 67:       add( idField, BorderLayout.NORTH );
 68:       
 69:       panel2 = new JPanel(); // set up panel to contain boardPanel
 70:       panel2.add( boardPanel, BorderLayout.CENTER ); // add board panel
 71:       add( panel2, BorderLayout.CENTER ); // add container panel
 72: 
 73:       setSize( 300, 225 ); // set size of window
 74:       setVisible( true ); // show window
 75: 
 76:       startClient();
 77:    } // end TicTacToeClient constructor
 78: 
 79:    // start the client thread
 80:    public void startClient()
 81:    {
 82:       try // connect to server, get streams and start outputThread
 83:       {
 84:          // make connection to server
 85:          connection = new Socket( 
 86:             InetAddress.getByName( ticTacToeHost ), 12345 );
 87: 
 88:          // get streams for input and output
 89:          input = new Scanner( connection.getInputStream() );
 90:          output = new Formatter( connection.getOutputStream() );
 91:       } // end try
 92:       catch ( IOException ioException )
 93:       {
 94:          ioException.printStackTrace();         
 95:       } // end catch
 96: 
 97:       // create and start worker thread for this client
 98:       ExecutorService worker = Executors.newFixedThreadPool( 1 );
 99:       worker.execute( this ); // execute client
100:    } // end method startClient
101: 
102:    // control thread that allows continuous update of displayArea
103:    public void run()
104:    {
105:       myMark = input.nextLine(); // get player's mark (X or O)
106: 
107:       SwingUtilities.invokeLater( 
108:          new Runnable() 
109:          {         
110:             public void run()
111:             {
112:                // display player's mark
113:                idField.setText( "You are player \"" + myMark + "\"" );
114:             } // end method run
115:          } // end anonymous inner class
116:       ); // end call to SwingUtilities.invokeLater
117:          
118:       myTurn = ( myMark.equals( X_MARK ) ); // determine if client's turn
119: 
120:       // receive messages sent to client and output them
121:       while ( true )
122:       {
123:          if ( input.hasNextLine() )
124:             processMessage( input.nextLine() );
125:       } // end while
126:    } // end method run
127: 
128:    // process messages received by client
129:    private void processMessage( String message )
130:    {
131:       // valid move occurred
132:       if ( message.equals( "Valid move." ) ) 
133:       {
134:          displayMessage( "Valid move, please wait.\n" );
135:          setMark( currentSquare, myMark ); // set mark in square
136:       } // end if
137:       else if ( message.equals( "Invalid move, try again" ) ) 
138:       {
139:          displayMessage( message + "\n" ); // display invalid move
140:          myTurn = true; // still this client's turn
141:       } // end else if
142:       else if ( message.equals( "Opponent moved" ) ) 
143:       {
144:          int location = input.nextInt(); // get move location
145:          input.nextLine(); // skip newline after int location
146:          int row = location / 3; // calculate row
147:          int column = location % 3; // calculate column
148: 
149:          setMark(  board[ row ][ column ], 
150:             ( myMark.equals( X_MARK ) ? O_MARK : X_MARK ) ); // mark move                
151:          displayMessage( "Opponent moved. Your turn.\n" );
152:          myTurn = true; // now this client's turn
153:       } // end else if
154:       else
155:          displayMessage( message + "\n" ); // display the message
156:    } // end method processMessage
157: 
158:    // manipulate outputArea in event-dispatch thread
159:    private void displayMessage( final String messageToDisplay )
160:    {
161:       SwingUtilities.invokeLater(
162:          new Runnable() 
163:          {
164:             public void run() 
165:             {
166:                displayArea.append( messageToDisplay ); // updates output
167:             } // end method run
168:          }  // end inner class
169:       ); // end call to SwingUtilities.invokeLater
170:    } // end method displayMessage
171: 
172:    // utility method to set mark on board in event-dispatch thread
173:    private void setMark( final Square squareToMark, final String mark )
174:    {
175:       SwingUtilities.invokeLater(
176:          new Runnable() 
177:          {
178:             public void run()
179:             {
180:                squareToMark.setMark( mark ); // set mark in square
181:             } // end method run
182:          } // end anonymous inner class
183:       ); // end call to SwingUtilities.invokeLater
184:    } // end method setMark
185: 
186:    // send message to server indicating clicked square
187:    public void sendClickedSquare( int location )
188:    {
189:       // if it is my turn
190:       if ( myTurn ) 
191:       {
192:          output.format( "%d\n", location ); // send location to server
193:          output.flush();
194:          myTurn = false; // not my turn anymore
195:       } // end if
196:    } // end method sendClickedSquare
197: 
198:    // set current Square
199:    public void setCurrentSquare( Square square )
200:    {
201:       currentSquare = square; // set current square to argument
202:    } // end method setCurrentSquare
203: 
204:    // private inner class for the squares on the board
205:    private class Square extends JPanel 
206:    {
207:       private String mark; // mark to be drawn in this square
208:       private int location; // location of square
209:    
210:       public Square( String squareMark, int squareLocation )
211:       {
212:          mark = squareMark; // set mark for this square
213:          location = squareLocation; // set location of this square
214: 
215:          addMouseListener( 
216:             new MouseAdapter() {
217:                public void mouseReleased( MouseEvent e )
218:                {
219:                   setCurrentSquare( Square.this ); // set current square
220: 
221:                   // send location of this square
222:                   sendClickedSquare( getSquareLocation() );
223:                } // end method mouseReleased
224:             } // end anonymous inner class
225:          ); // end call to addMouseListener
226:       } // end Square constructor
227: 
228:       // return preferred size of Square
229:       public Dimension getPreferredSize() 
230:       { 
231:          return new Dimension( 30, 30 ); // return preferred size
232:       } // end method getPreferredSize
233: 
234:       // return minimum size of Square
235:       public Dimension getMinimumSize() 
236:       {
237:          return getPreferredSize(); // return preferred size
238:       } // end method getMinimumSize
239: 
240:       // set mark for Square
241:       public void setMark( String newMark ) 
242:       { 
243:          mark = newMark; // set mark of square
244:          repaint(); // repaint square
245:       } // end method setMark
246:    
247:       // return Square location
248:       public int getSquareLocation() 
249:       {
250:          return location; // return location of square
251:       } // end method getSquareLocation
252:    
253:       // draw Square
254:       public void paintComponent( Graphics g )
255:       {
256:          super.paintComponent( g );
257: 
258:          g.drawRect( 0, 0, 29, 29 ); // draw square
259:          g.drawString( mark, 11, 20 ); // draw mark   
260:       } // end method paintComponent
261:    } // end inner-class Square
262: } // end class TicTacToeClient
263: 
264: /**************************************************************************
265:  * (C) Copyright 1992-2005 by Deitel & Associates, Inc. and               *
266:  * Pearson Education, Inc. All Rights Reserved.                           *
267:  *                                                                        *
268:  * DISCLAIMER: The authors and publisher of this book have used their     *
269:  * best efforts in preparing the book. These efforts include the          *
270:  * development, research, and testing of the theories and programs        *
271:  * to determine their effectiveness. The authors and publisher make       *
272:  * no warranty of any kind, expressed or implied, with regard to these    *
273:  * programs or to the documentation contained in these books. The authors *
274:  * and publisher shall not be liable in any event for incidental or       *
275:  * consequential damages in connection with, or arising out of, the       *
276:  * furnishing, performance, or use of these programs.                     *
277:  *************************************************************************/