public class TicTacToeClient extends JFrame implements Runnable
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: *************************************************************************/