Source of TicTacToeServer.java


  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:  *************************************************************************/