29.5. Processing Actions Using the Action Interface

 
[Page 838 ( continued )]

25.9. (Optional) Case Studies: Distributed TicTacToe Games

In §16.7, "Case Study: TicTacToe," you developed an applet for the TicTacToe game that enables two players to play on the same machine. In this section, you will learn how to develop a distributed TicTacToe game using multithreads and networking with socket streams. A distributed TicTacToe game enables users to play on different machines from anywhere on the Internet.


[Page 839]

You need to develop a server for multiple clients . The server creates a server socket, and accepts connections from every two players to form a session. Each session is a thread that communicates with the two players and determines the status of the game. The server can establish any number of sessions, as shown in Figure 25.15.

Figure 25.15. The server can create many sessions, each of which facilitates a TicTacToe game for two players.

For each session, the first client connecting to the server is identified as Player 1 with token 'X', and the second client connecting to the server is identified as Player 2 with token 'O'. The server notifies the players of their respective tokens. Once two clients are connected to it, the server starts a thread to facilitate the game between the two players by performing the steps repeatedly, as shown in Figure 25.16.

Figure 25.16. The server starts a thread to facilitate communications between the two players.


[Page 840]

The server does not have to be a graphical component, but creating it as a frame in which game information can be viewed is user -friendly. You can create a scroll pane to hold a text area in the frame and display game information in the text area. The server creates a thread to handle a game session when two players are connected to the server.

The client is responsible for interacting with the players. It creates a user interface with nine cells , and displays the game title and status to the players in the labels. The client class is very similar to the TicTacToe class presented in §16.7, "Case Study: TicTacToe." However, the client in this example does not determine the game status (win or draw), it simply passes the moves to the server and receives the game status from the server.

Based on the foregoing analysis, you can create the following classes:

  • TicTacToeServer serves all the clients in Listing 25.13.

  • HandleASession facilitates the game for two players in Listing 25.13. It is in the same file with TicTacToeServer.java.

  • TicTacToeClient models a player in Listing 25.14.

  • Cell models a cell in the game in Listing 25.14. It is an inner class in TicTacToeClient .

  • TicTacToeConstants is an interface that defines the constants shared by all the classes in the example in Listing 25.12.

The relationships of these classes are shown in Figure 25.17.

Figure 25.17. TicTacToeServer creates an instance of HandleASession for each session of two players. TicTacToeClient creates nine cells in the UI.
(This item is displayed on page 841 in the print version)

Listing 25.12. TicTacToeConstants.java
 1    public interface   TicTacToeConstants {  2   public static int   PLAYER1 =   1   ;  // Indicate player 1  3   public static int   PLAYER2 =   2   ;  // Indicate player 2  4   public static int   PLAYER1_WON =   1   ;  // Indicate player 1 won  5   public static int   PLAYER2_WON =   2   ;  // Indicate player 2 won  6   public static int   DRAW =   3   ;  // Indicate a draw  7   public static int   CONTINUE =   4   ;  // Indicate to continue  8 } 

Listing 25.13. TicTacToeServer.java
(This item is displayed on pages 840 - 845 in the print version)
 1   import   java.io.*; 2   import   java.net.*; 3   import   javax.swing.*; 4   import   java.awt.*; 5   import   java.util.Date; 6 7    public   class TicTacToeServer   extends   JFrame  8   implements   TicTacToeConstants { 9   public static void   main(String[] args) { 10 TicTacToeServer frame =   new   TicTacToeServer(); 11 } 12 13   public   TicTacToeServer() { 14 JTextArea jtaLog =   new   JTextArea(); 15 16  // Create a scroll pane to hold text area  17 JScrollPane scrollPane =   new   JScrollPane(jtaLog); 18 19  // Add the scroll pane to the frame  20 add(scrollPane, BorderLayout.CENTER); 21 

[Page 841]
 22 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 23 setSize(   300   ,   300   ); 24 setTitle(   "TicTacToeServer"   ); 25 setVisible(   true   ); 26 27   try   { 28  // Create a server socket  29  ServerSocket serverSocket =   new   ServerSocket(   8000   );  30 jtaLog.append(   new   Date() + 31   ": Server started at socket 8000\n"   ); 32 33  // Number a session  34   int   sessionNo =   1   ; 35 36  // Ready to create a session for every two players  37   while   (   true   ) { 38 jtaLog.append(   new   Date() + 39   ": Wait for players to join session "   + sessionNo +   '\n'   ); 40 41  // Connect to player 1  42  Socket player1 = serverSocket.accept();  43 44 jtaLog.append(   new   Date() +   ": Player 1 joined session "   + 45 sessionNo +   '\n'   ); 

[Page 842]
 46 jtaLog.append(   "Player 1's IP address"   + 47 player1.getInetAddress().getHostAddress() +   '\n'   ); 48 49  // Notify that the player is Player 1  50   new   DataOutputStream( 51 player1.getOutputStream()).writeInt(PLAYER1); 52 53  // Connect to player 2  54  Socket player2 = serverSocket.accept();  55 56 jtaLog.append(   new   Date() + 57   ": Player 2 joined session "   + sessionNo +   '\n'   ); 58 jtaLog.append(   "Player 2's IP address"   + 59 player2.getInetAddress().getHostAddress() +   '\n'   ); 60 61  // Notify that the player is Player 2  62   new   DataOutputStream( 63 player2.getOutputStream()).writeInt(PLAYER2); 64 65  // Display this session and increment session number  66 jtaLog.append(   new   Date() +   ": Start a thread for session "   + 67 sessionNo++ +   '\n'   ); 68 69  // Create a new thread for this session of two players  70 HandleASession task =   new   HandleASession(player1, player2); 71 72  // Start the new thread  73 new Thread(task).start(); 74 } 75 } 76   catch   (IOException ex) { 78 System.err.println(ex); 79 } 80 } 81 } 82 83  // Define the thread class for handling a new session for two players  84    class   HandleASession   implements   Runnable, TicTacToeConstants  { 85   private   Socket player1; 86   private   Socket player2; 87 88  // Create and initialize cells  89   private char   [][] cell =   new char   [   3   ][   3   ]; 90 91   private   DataInputStream fromPlayer1; 92   private   DataOutputStream toPlayer1; 93   private   DataInputStream fromPlayer2; 94   private   DataOutputStream toPlayer2; 95 96  // Continue to play  97    private boolean   continueToPlay =   true   ;  98 99  /** Construct a thread */  100    public   HandleASession(Socket player1, Socket player2) {  101   this   .player1 = player1; 102   this   .player2 = player2; 103 104  // Initialize cells  105   for   (   int   i =     ; i <   3   ; i++) 106   for   (   int   j =     ; j <   3   ; j++) 107 cell[i][j] =   ' '   ; 

[Page 843]
 108 } 109 110  /** Implement the run() method for the thread */  111    public void   run() {  112   try   { 113  // Create data input and output streams  114 DataInputStream fromPlayer1 =   new   DataInputStream( 115 player1.getInputStream()); 116 DataOutputStream toPlayer1 =   new   DataOutputStream( 117 player1.getOutputStream()); 118 DataInputStream fromPlayer2 =   new   DataInputStream( 119 player2.getInputStream()); 120 DataOutputStream toPlayer2 =   new   DataOutputStream( 121 player2.getOutputStream()); 122 123  // Write anything to notify player 1 to start  124  // This is just to let player 1 know to start  125 toPlayer1.writeInt(   1   ); 126 127  // Continuously serve the players and determine and report  128  // the game status to the players  129   while   (   true   ) { 130  // Receive a move from player 1  131   int   row = fromPlayer1.readInt(); 132   int   column = fromPlayer1.readInt(); 133 cell[row][column] =   'X'   ; 134 135  // Check if Player 1 wins  136   if   (isWon(   'X'   )) { 137 toPlayer1.writeInt(PLAYER1_WON); 138 toPlayer2.writeInt(PLAYER1_WON); 139 sendMove(toPlayer2, row, column); 140   break   ;  // Break the loop  141 } 142   else if   (isFull()) {  // Check if all cells are filled  143 toPlayer1.writeInt(DRAW); 144 toPlayer2.writeInt(DRAW); 145 sendMove(toPlayer2, row, column); 146   break   ; 147 } 148   else   { 149  // Notify player 2 to take the turn  150 toPlayer2.writeInt(CONTINUE); 151 152  // Send player 1's selected row and column to player 2  153 sendMove(toPlayer2, row, column); 154 } 155 156  // Receive a move from Player 2  157 row = fromPlayer2.readInt(); 158 column = fromPlayer2.readInt(); 159 cell[row][column] =   'O'   ; 160 161  // Check if Player 2 wins  162   if   (isWon(   'O'   )) { 163 toPlayer1.writeInt(PLAYER2_WON); 164 toPlayer2.writeInt(PLAYER2_WON); 165 sendMove(toPlayer1, row, column); 166   break   ; 167 } 168   else   { 

[Page 844]
 169  // Notify player 1 to take the turn  170 toPlayer1.writeInt(CONTINUE); 171 172  // Send player 2's selected row and column to player 1  173 sendMove(toPlayer1, row, column); 174 } 175 } 176 } 177   catch   (IOException ex) { 178 System.err.println(ex); 179 } 180 } 181 182  /** Send the move to other player */  183    private void   sendMove(DataOutputStream out,   int   row,   int   column)  184   throws   IOException { 185 out.writeInt(row);  // Send row index  186 out.writeInt(column);  // Send column index  187 } 188 189  /** Determine if the cells are all occupied */  190    private boolean   isFull() {  191   for   (   int   i =     ; i <   3   ; i++) 192   for   (   int   j =     ; j <   3   ; j++) 193   if   (cell[i][j] ==   ' '   ) 194   return false   ;  // At least one cell is not filled  195 196  // All cells are filled  197   return true   ; 198 } 199 200  /** Determine if the player with the specified token wins */  201    private boolean   isWon(   char   token) {  202  // Check all rows  203   for   (   int   i =     ; i <   3   ; i++) 204   if   ((cell[i][     ] == token) 205 && (cell[i][   1   ] == token) 206 && (cell[i][   2   ] == token)) { 207   return true   ; 208 } 209 210  /** Check all columns */  211   for   (   int   j =     ; j <   3   ; j++) 212   if   ((cell[     ][j] == token) 213 && (cell[   1   ][j] == token) 214 && (cell[   2   ][j] == token)) { 215   return true   ; 216 } 217 218  /** Check major diagonal */  219   if   ((cell[     ][     ] == token) 220 && (cell[   1   ][   1   ] == token) 221 && (cell[   2   ][   2   ] == token)) { 222   return true   ; 223 } 224 225  /** Check subdiagonal */  226   if   ((cell[     ][   2   ] == token) 227 && (cell[   1   ][   1   ] == token) 228 && (cell[   2   ][     ] == token)) { 

[Page 845]
 229   return true   ; 230 } 231 232  /** All checked, but no winner */  233   return false   ; 234 } 235 } 

Listing 25.14. TicTacToeClient.java
(This item is displayed on pages 845 - 849 in the print version)
 1   import   java.awt.*; 2   import   java.awt.event.*; 3   import   javax.swing.*; 4   import   javax.swing.border.LineBorder; 5   import   java.io.*; 6   import   java.net.*; 7 8   public class   TicTacToeClient   extends   JApplet 9   implements   Runnable, TicTacToeConstants { 10  // Indicate whether the player has the turn  11   private boolean   myTurn =   false   ; 12 13  // Indicate the token for the player  14   private char   myToken =   ' '   ; 15 16  // Indicate the token for the other player  17   private char   otherToken =   ' '   ; 18 19  // Create and initialize cells  20   private   Cell[][] cell =   new   Cell[   3   ][   3   ]; 21 22  // Create and initialize a title label  23    private   JLabel jlblTitle =   new   JLabel();  24 25  // Create and initialize a status label  26   private   JLabel jlblStatus =   new   JLabel(); 27 28  // Indicate selected row and column by the current move  29   private int   rowSelected; 30   private int   columnSelected; 31 32  // Input and output streams from/to server  33   private   DataInputStream fromServer; 34   private   DataOutputStream toServer; 35 36  // Continue to play?  37   private boolean   continueToPlay =   true   ; 38 39  // Wait for the player to mark a cell  40   private boolean   waiting =   true   ; 41 42  // Indicate if it runs as application  43   private boolean   isStandAlone =   false   ; 44 45  // Host name or ip  46   private   String host =   "localhost"   ; 47 48  /** Initialize UI */  49   public void   init() { 50  // Panel p to hold cells  51 JPanel p =   new   JPanel(); 

[Page 846]
 52 p.setLayout(   new   GridLayout(   3   ,   3   ,     ,     )); 53   for   (   int   i =     ; i <   3   ; i++) 54   for   (   int   j =     ; j <   3   ; j++) 55 p.add(cell[i][j] =   new   Cell(i, j)); 56 57  // Set properties for labels and borders for labels and panel  58 p.setBorder(   new   LineBorder(Color.black,   1   )); 59 jlblTitle.setHorizontalAlignment(JLabel.CENTER); 60 jlblTitle.setFont(   new   Font(   "SansSerif"   , Font.BOLD,   16   )); 61 jlblTitle.setBorder(   new   LineBorder(Color.black,   1   )); 62 jlblStatus.setBorder(   new   LineBorder(Color.black,   1   )); 63 64  // Place the panel and the labels to the applet  65 add(jlblTitle, BorderLayout.NORTH); 66 add(p, BorderLayout.CENTER); 67 add(jlblStatus, BorderLayout.SOUTH); 68 69  // Connect to the server  70  connectToServer();  71 } 72 73    private void   connectToServer()  { 74   try   { 75  // Create a socket to connect to the server  76 Socket socket; 77   if   (isStandAlone) 78  socket =   new   Socket(host,   8000   );  79   else   80  socket =   new   Socket(getCodeBase().getHost(),   8000   );  81 82  // Create an input stream to receive data from the server  83 fromServer =   new   DataInputStream(socket.getInputStream()); 84 85  // Create an output stream to send data to the server  86 toServer =   new   DataOutputStream(socket.getOutputStream()); 87 } 88   catch   (Exception ex) { 89 System.err.println(ex); 90 } 91 92  // Control the game on a separate thread  93 Thread thread =   new   Thread(   this   ); 94 thread.start(); 95 } 96 97    public void   run()  { 98   try   { 99  // Get notification from the server  100   int   player = fromServer.readInt(); 101 102  // Am I player 1 or 2?  103   if   (player == PLAYER1) { 104 myToken =   'X'   ; 105 otherToken =   'O'   ; 106 jlblTitle.setText(   "Player 1 with token 'X'"   ); 107 jlblStatus.setText(   "Waiting for player 2 to join"   ); 108 109  // Receive startup notification from the server  110 fromServer.readInt();  // Whatever read is ignored  111 112  // The other player has joined  

[Page 847]
 113 jlblStatus.setText(   "Player 2 has joined. I start first"   ); 114 115  // It is my turn  116 myTurn =   true   ; 117 } 118   else if   (player == PLAYER2) { 119 myToken =   'O'   ; 120 otherToken =   'X'   ; 121 jlblTitle.setText(   "Player 2 with token 'O'"   ); 122 jlblStatus.setText(   "Waiting for player 1 to move"   ); 123 } 124 125  // Continue to play  126   while   (continueToPlay) { 127   if   (player == PLAYER1) { 128  waitForPlayerAction();   // Wait for player 1 to move  129  sendMove();   // Send the move to the server  130  receiveInfoFromServer();   // Receive info from the server  131 } 132   else if   (player == PLAYER2) { 133  receiveInfoFromServer();   // Receive info from the server  134  waitForPlayerAction();   // Wait for player 2 to move  135  sendMove()  ;  // Send player 2's move to the server  136 } 137 } 138 } 139   catch   (Exception ex) { 140 } 141 } 142 143  /** Wait for the player to mark a cell */  144  Private   void   waitForPlayerAction()   throws   InterruptedException  { 145   while   (waiting) { 146 Thread.sleep(   100   ); 147 } 148 149 waiting =   true   ; 150 } 151 152  /** Send this player's move to the server */  153  private   void   sendMove()   throws   IOException  { 154 toServer.writeInt(rowSelected);  // Send the selected row  155 toServer.writeInt(columnSelected);  // Send the selected column  156 } 157 158  /** Receive info from the server */  159    private void   receiveInfoFromServer()   throws   IOException  { 160  // Receive game status  161   int   status = fromServer.readInt(); 162 163   if   (status == PLAYER1_WON) { 164  // Player 1 won, stop playing  165 continueToPlay =   false   ; 166   if   (myToken ==   'X'   ) { 167 jlblStatus.setText(   "I won! (X)"   ); 168 } 169   else if   (myToken ==   'O'   ) { 170 jlblStatus.setText(   "Player 1 (X) has won!"   ); 171 receiveMove(); 172 } 173 } 

[Page 848]
 174   else if   (status == PLAYER2_WON) { 175  // Player 2 won, stop playing  176 continueToPlay =   false   ; 177   if   (myToken ==   'O'   ) { 178 jlblStatus.setText(   "I won! (O)"   ); 179 } 180   else if   (myToken ==   'X'   ) { 181 jlblStatus.setText(   "Player 2 (O) has won!"   ); 182 receiveMove(); 183 } 184 } 185   else if   (status == DRAW) { 186  // No winner, game is over  187 continueToPlay =   false   ; 188 jlblStatus.setText(   "Game is over, no winner!"   ); 189 190   if   (myToken ==   'O'   ) { 191 receiveMove(); 192 } 193 } 194   else   { 195 receiveMove(); 196 jlblStatus.setText(   "My turn"   ); 197 myTurn =   true   ;  // It is my turn  198 } 199 } 200 201    private void   receiveMove()   throws   IOException  { 202  // Get the other player's move  203   int   row = fromServer.readInt(); 204   int   column = fromServer.readInt(); 205 cell[row][column].setToken(otherToken); 206 } 207 208  // An inner class for a cell  209   public class   Cell   extends   JPanel { 210  // Indicate the row and column of this cell in the board  211   private int   row; 212   private int   column; 213 214  // Token used for this cell  215   private char   token =   ' '   ; 216 217   public   Cell(   int   row,   int   column) { 218   this   .row = row; 219   this   .column = column; 220 setBorder(   new   LineBorder(Color.black,   1   ));  // Set cell's border  221 addMouseListener(   new   ClickListener());  // Register listener  222 } 223 224  /** Return token */  225   public char   getToken() { 226   return   token; 227 } 228 229  /** Set a new token */  230   public void   setToken(   char   c) { 231 token = c; 232 repaint(); 233 } 234 

[Page 849]
 235  /** Paint the cell */  236   protected void   paintComponent(Graphics g) { 237   super   .paintComponent(g); 238 239   if   (token ==   'X'   ) { 240 g.drawLine(   10   ,   10   , getWidth() -   10   , getHeight() -   10   ); 241 g.drawLine(getWidth() -   10   ,   10   ,   10   , getHeight() -   10   ); 242 } 243   else if   (token ==   'O'   ) { 244 g.drawOval(   10   ,   10   , getWidth() -   20   , getHeight() -   20   ); 245 } 246 } 247 248  /** Handle mouse click on a cell */  249   private class   ClickListener   extends   MouseAdapter { 250   public void   mouseClicked(MouseEvent e) { 251  // If cell is not occupied and the player has the turn  252   if   ((token ==   ' '   ) && myTurn) { 253 setToken(myToken);  // Set the player's token in the cell  254 myTurn =   false   ; 255 rowSelected = row; 256 columnSelected = column; 257 jlblStatus.setText(   "Waiting for the other player to move"   ); 258 waiting =   false   ;  // Just completed a successful move  259 } 260 } 261 } 262 } 263 } 

The server can serve any number of sessions. Each session takes care of two players. The client can be a Java applet or a Java application. To run a client as a Java applet from a Web browser, the server must run from a Web server. Figures 25.18 and 25.19 show sample runs of the server and the clients.

Figure 25.18. TicTacToeServer accepts connection requests and creates sessions to serve pairs of players.


Figure 25.19. TicTacToeClient can run as an applet or an application.



[Page 850]

The TicTacToeConstants interface defines the constants shared by all the classes in the project. Each class that uses the constants needs to implement the interface. Centrally defining constants in an interface is a common practice in Java. For example, all the constants shared by Swing classes are defined in java.swing.SwingConstants .

Once a session is established, the server receives moves from the players in alternation . Upon receiving a move from a player, the server determines the status of the game. If the game is not finished, the server sends the status ( CONTINUE ) and the player's move to the other player. If the game is won or drawn, the server sends the status ( PLAYER1_WON , PLAYER2_WON , or DRAW ) to both players.

The implementation of Java network programs at the socket level is tightly synchronized. An operation to send data from one machine requires an operation to receive data from the other machine. As shown in this example, the server and the client are tightly synchronized to send or receive data.

 


Introduction to Java Programming-Comprehensive Version
Introduction to Java Programming-Comprehensive Version (6th Edition)
ISBN: B000ONFLUM
EAN: N/A
Year: 2004
Pages: 503

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net