public class TicTacToeServer extends JFrame
1: // Fig. 24.13: TicTacToeServer.java
2: // This class maintains a game of Tic-Tac-Toe for two clients.
3: import java.awt.BorderLayout;
4: import java.net.ServerSocket;
5: import java.net.Socket;
6: import java.io.IOException;
7: import java.util.Formatter;
8: import java.util.Scanner;
9: import java.util.concurrent.ExecutorService;
10: import java.util.concurrent.Executors;
11: import java.util.concurrent.locks.Lock;
12: import java.util.concurrent.locks.ReentrantLock;
13: import java.util.concurrent.locks.Condition;
14: import javax.swing.JFrame;
15: import javax.swing.JTextArea;
16: import javax.swing.SwingUtilities;
17:
18: public class TicTacToeServer extends JFrame
19: {
20: private String[] board = new String[ 9 ]; // tic-tac-toe board
21: private JTextArea outputArea; // for outputting moves
22: private Player[] players; // array of Players
23: private ServerSocket server; // server socket to connect with clients
24: private int currentPlayer; // keeps track of player with current move
25: private final static int PLAYER_X = 0; // constant for first player
26: private final static int PLAYER_O = 1; // constant for second player
27: private final static String[] MARKS = { "X", "O" }; // array of marks
28: private ExecutorService runGame; // will run players
29: private Lock gameLock; // to lock game for synchronization
30: private Condition otherPlayerConnected; // to wait for other player
31: private Condition otherPlayerTurn; // to wait for other player's turn
32:
33: // set up tic-tac-toe server and GUI that displays messages
34: public TicTacToeServer()
35: {
36: super( "Tic-Tac-Toe Server" ); // set title of window
37:
38: // create ExecutorService with a thread for each player
39: runGame = Executors.newFixedThreadPool( 2 );
40: gameLock = new ReentrantLock(); // create lock for game
41:
42: // condition variable for both players being connected
43: otherPlayerConnected = gameLock.newCondition();
44:
45: // condition variable for the other player's turn
46: otherPlayerTurn = gameLock.newCondition();
47:
48: for ( int i = 0; i < 9; i++ )
49: board[ i ] = new String( "" ); // create tic-tac-toe board
50: players = new Player[ 2 ]; // create array of players
51: currentPlayer = PLAYER_X; // set current player to first player
52:
53: try
54: {
55: server = new ServerSocket( 12345, 2 ); // set up ServerSocket
56: } // end try
57: catch ( IOException ioException )
58: {
59: ioException.printStackTrace();
60: System.exit( 1 );
61: } // end catch
62:
63: outputArea = new JTextArea(); // create JTextArea for output
64: add( outputArea, BorderLayout.CENTER );
65: outputArea.setText( "Server awaiting connections\n" );
66:
67: setSize( 300, 300 ); // set size of window
68: setVisible( true ); // show window
69: } // end TicTacToeServer constructor
70:
71: // wait for two connections so game can be played
72: public void execute()
73: {
74: // wait for each client to connect
75: for ( int i = 0; i < players.length; i++ )
76: {
77: try // wait for connection, create Player, start runnable
78: {
79: players[ i ] = new Player( server.accept(), i );
80: runGame.execute( players[ i ] ); // execute player runnable
81: } // end try
82: catch ( IOException ioException )
83: {
84: ioException.printStackTrace();
85: System.exit( 1 );
86: } // end catch
87: } // end for
88:
89: gameLock.lock(); // lock game to signal player X's thread
90:
91: try
92: {
93: players[ PLAYER_X ].setSuspended( false ); // resume player X
94: otherPlayerConnected.signal(); // wake up player X's thread
95: } // end try
96: finally
97: {
98: gameLock.unlock(); // unlock game after signalling player X
99: } // end finally
100: } // end method execute
101:
102: // display message in outputArea
103: private void displayMessage( final String messageToDisplay )
104: {
105: // display message from event-dispatch thread of execution
106: SwingUtilities.invokeLater(
107: new Runnable()
108: {
109: public void run() // updates outputArea
110: {
111: outputArea.append( messageToDisplay ); // add message
112: } // end method run
113: } // end inner class
114: ); // end call to SwingUtilities.invokeLater
115: } // end method displayMessage
116:
117: // determine if move is valid
118: public boolean validateAndMove( int location, int player )
119: {
120: // while not current player, must wait for turn
121: while ( player != currentPlayer )
122: {
123: gameLock.lock(); // lock game to wait for other player to go
124:
125: try
126: {
127: otherPlayerTurn.await(); // wait for player's turn
128: } // end try
129: catch ( InterruptedException exception )
130: {
131: exception.printStackTrace();
132: } // end catch
133: finally
134: {
135: gameLock.unlock(); // unlock game after waiting
136: } // end finally
137: } // end while
138:
139: // if location not occupied, make move
140: if ( !isOccupied( location ) )
141: {
142: board[ location ] = MARKS[ currentPlayer ]; // set move on board
143: currentPlayer = ( currentPlayer + 1 ) % 2; // change player
144:
145: // let new current player know that move occurred
146: players[ currentPlayer ].otherPlayerMoved( location );
147:
148: gameLock.lock(); // lock game to signal other player to go
149:
150: try
151: {
152: otherPlayerTurn.signal(); // signal other player to continue
153: } // end try
154: finally
155: {
156: gameLock.unlock(); // unlock game after signaling
157: } // end finally
158:
159: return true; // notify player that move was valid
160: } // end if
161: else // move was not valid
162: return false; // notify player that move was invalid
163: } // end method validateAndMove
164:
165: // determine whether location is occupied
166: public boolean isOccupied( int location )
167: {
168: if ( board[ location ].equals( MARKS[ PLAYER_X ] ) ||
169: board [ location ].equals( MARKS[ PLAYER_O ] ) )
170: return true; // location is occupied
171: else
172: return false; // location is not occupied
173: } // end method isOccupied
174:
175: // place code in this method to determine whether game over
176: public boolean isGameOver()
177: {
178: return false; // this is left as an exercise
179: } // end method isGameOver
180:
181: // private inner class Player manages each Player as a runnable
182: private class Player implements Runnable
183: {
184: private Socket connection; // connection to client
185: private Scanner input; // input from client
186: private Formatter output; // output to client
187: private int playerNumber; // tracks which player this is
188: private String mark; // mark for this player
189: private boolean suspended = true; // whether thread is suspended
190:
191: // set up Player thread
192: public Player( Socket socket, int number )
193: {
194: playerNumber = number; // store this player's number
195: mark = MARKS[ playerNumber ]; // specify player's mark
196: connection = socket; // store socket for client
197:
198: try // obtain streams from Socket
199: {
200: input = new Scanner( connection.getInputStream() );
201: output = new Formatter( connection.getOutputStream() );
202: } // end try
203: catch ( IOException ioException )
204: {
205: ioException.printStackTrace();
206: System.exit( 1 );
207: } // end catch
208: } // end Player constructor
209:
210: // send message that other player moved
211: public void otherPlayerMoved( int location )
212: {
213: output.format( "Opponent moved\n" );
214: output.format( "%d\n", location ); // send location of move
215: output.flush(); // flush output
216: } // end method otherPlayerMoved
217:
218: // control thread's execution
219: public void run()
220: {
221: // send client its mark (X or O), process messages from client
222: try
223: {
224: displayMessage( "Player " + mark + " connected\n" );
225: output.format( "%s\n", mark ); // send player's mark
226: output.flush(); // flush output
227:
228: // if player X, wait for another player to arrive
229: if ( playerNumber == PLAYER_X )
230: {
231: output.format( "%s\n%s", "Player X connected",
232: "Waiting for another player\n" );
233: output.flush(); // flush output
234:
235: gameLock.lock(); // lock game to wait for second player
236:
237: try
238: {
239: while( suspended )
240: {
241: otherPlayerConnected.await(); // wait for player O
242: } // end while
243: } // end try
244: catch ( InterruptedException exception )
245: {
246: exception.printStackTrace();
247: } // end catch
248: finally
249: {
250: gameLock.unlock(); // unlock game after second player
251: } // end finally
252:
253: // send message that other player connected
254: output.format( "Other player connected. Your move.\n" );
255: output.flush(); // flush output
256: } // end if
257: else
258: {
259: output.format( "Player O connected, please wait\n" );
260: output.flush(); // flush output
261: } // end else
262:
263: // while game not over
264: while ( !isGameOver() )
265: {
266: int location = 0; // initialize move location
267:
268: if ( input.hasNext() )
269: location = input.nextInt(); // get move location
270:
271: // check for valid move
272: if ( validateAndMove( location, playerNumber ) )
273: {
274: displayMessage( "\nlocation: " + location );
275: output.format( "Valid move.\n" ); // notify client
276: output.flush(); // flush output
277: } // end if
278: else // move was invalid
279: {
280: output.format( "Invalid move, try again\n" );
281: output.flush(); // flush output
282: } // end else
283: } // end while
284: } // end try
285: finally
286: {
287: try
288: {
289: connection.close(); // close connection to client
290: } // end try
291: catch ( IOException ioException )
292: {
293: ioException.printStackTrace();
294: System.exit( 1 );
295: } // end catch
296: } // end finally
297: } // end method run
298:
299: // set whether or not thread is suspended
300: public void setSuspended( boolean status )
301: {
302: suspended = status; // set value of suspended
303: } // end method setSuspended
304: } // end class Player
305: } // end class TicTacToeServer
306:
307: /**************************************************************************
308: * (C) Copyright 1992-2005 by Deitel & Associates, Inc. and *
309: * Pearson Education, Inc. All Rights Reserved. *
310: * *
311: * DISCLAIMER: The authors and publisher of this book have used their *
312: * best efforts in preparing the book. These efforts include the *
313: * development, research, and testing of the theories and programs *
314: * to determine their effectiveness. The authors and publisher make *
315: * no warranty of any kind, expressed or implied, with regard to these *
316: * programs or to the documentation contained in these books. The authors *
317: * and publisher shall not be liable in any event for incidental or *
318: * consequential damages in connection with, or arising out of, the *
319: * furnishing, performance, or use of these programs. *
320: *************************************************************************/