1  import java.io.*;
  2  import java.net.*;
  3  import java.util.Date;
  4  import javafx.application.Application;
  5  import javafx.application.Platform;
  6  import javafx.scene.Scene;
  7  import javafx.scene.control.ScrollPane;
  8  import javafx.scene.control.TextArea;
  9  import javafx.stage.Stage;
 10  
 11  public class TicTacToeServer extends Application 
 12      implements TicTacToeConstants {
 13    private int sessionNo = 1; // Number a session
 14    
 15    @Override // Override the start method in the Application class
 16    public void start(Stage primaryStage) {
 17      TextArea taLog = new TextArea();
 18  
 19      // Create a scene and place it in the stage
 20      Scene scene = new Scene(new ScrollPane(taLog), 450, 200);
 21      primaryStage.setTitle("TicTacToeServer"); // Set the stage title
 22      primaryStage.setScene(scene); // Place the scene in the stage
 23      primaryStage.show(); // Display the stage
 24  
 25      new Thread( () -> {
 26        try {
 27          // Create a server socket
 28          ServerSocket serverSocket = new ServerSocket(8000);
 29          Platform.runLater(() -> taLog.appendText(new Date() +
 30            ": Server started at socket 8000\n"));
 31    
 32          // Ready to create a session for every two players
 33          while (true) {
 34            Platform.runLater(() -> taLog.appendText(new Date() +
 35              ": Wait for players to join session " + sessionNo + '\n'));
 36    
 37            // Connect to player 1
 38            Socket player1 = serverSocket.accept();
 39    
 40            Platform.runLater(() -> {
 41              taLog.appendText(new Date() + ": Player 1 joined session " 
 42                + sessionNo + '\n');
 43              taLog.appendText("Player 1's IP address" +
 44                player1.getInetAddress().getHostAddress() + '\n');
 45            });
 46    
 47            // Notify that the player is Player 1
 48            new DataOutputStream(
 49              player1.getOutputStream()).writeInt(PLAYER1);
 50    
 51            // Connect to player 2
 52            Socket player2 = serverSocket.accept();
 53    
 54            Platform.runLater(() -> {
 55              taLog.appendText(new Date() +
 56                ": Player 2 joined session " + sessionNo + '\n');
 57              taLog.appendText("Player 2's IP address" +
 58                player2.getInetAddress().getHostAddress() + '\n');
 59            });
 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            Platform.runLater(() -> 
 67              taLog.appendText(new Date() + 
 68                ": Start a thread for session " + sessionNo++ + '\n'));
 69    
 70            // Launch a new thread for this session of two players
 71            new Thread(new HandleASession(player1, player2)).start();
 72          }
 73        }
 74        catch(IOException ex) {
 75          ex.printStackTrace();
 76        }
 77      }).start();
 78    }
 79  
 80    // Define the thread class for handling a new session for two players
 81    class HandleASession implements Runnable, TicTacToeConstants {
 82      private Socket player1;
 83      private Socket player2;
 84    
 85      // Create and initialize cells
 86      private char[][] cell =  new char[3][3];
 87    
 88      private DataInputStream fromPlayer1;
 89      private DataOutputStream toPlayer1;
 90      private DataInputStream fromPlayer2;
 91      private DataOutputStream toPlayer2;
 92    
 93      // Continue to play
 94      private boolean continueToPlay = true;
 95    
 96      /** Construct a thread */
 97      public HandleASession(Socket player1, Socket player2) {
 98        this.player1 = player1;
 99        this.player2 = player2;
100    
101        // Initialize cells
102        for (int i = 0; i < 3; i++)
103          for (int j = 0; j < 3; j++)
104            cell[i][j] = ' ';
105      }
106    
107      /** Implement the run() method for the thread */
108      public void run() {
109        try {
110          // Create data input and output streams
111          DataInputStream fromPlayer1 = new DataInputStream(
112            player1.getInputStream());
113          DataOutputStream toPlayer1 = new DataOutputStream(
114            player1.getOutputStream());
115          DataInputStream fromPlayer2 = new DataInputStream(
116            player2.getInputStream());
117          DataOutputStream toPlayer2 = new DataOutputStream(
118            player2.getOutputStream());
119    
120          // Write anything to notify player 1 to start
121          // This is just to let player 1 know to start
122          toPlayer1.writeInt(1);
123    
124          // Continuously serve the players and determine and report
125          // the game status to the players
126          while (true) {
127            // Receive a move from player 1
128            int row = fromPlayer1.readInt();
129            int column = fromPlayer1.readInt();
130            cell[row][column] = 'X';
131    
132            // Check if Player 1 wins
133            if (isWon('X')) {
134              toPlayer1.writeInt(PLAYER1_WON);
135              toPlayer2.writeInt(PLAYER1_WON);
136              sendMove(toPlayer2, row, column);
137              break; // Break the loop
138            }
139            else if (isFull()) { // Check if all cells are filled
140              toPlayer1.writeInt(DRAW);
141              toPlayer2.writeInt(DRAW);
142              sendMove(toPlayer2, row, column);
143              break;
144            }
145            else {
146              // Notify player 2 to take the turn
147              toPlayer2.writeInt(CONTINUE);
148    
149              // Send player 1's selected row and column to player 2
150              sendMove(toPlayer2, row, column);
151            }
152    
153            // Receive a move from Player 2
154            row = fromPlayer2.readInt();
155            column = fromPlayer2.readInt();
156            cell[row][column] = 'O';
157    
158            // Check if Player 2 wins
159            if (isWon('O')) {
160              toPlayer1.writeInt(PLAYER2_WON);
161              toPlayer2.writeInt(PLAYER2_WON);
162              sendMove(toPlayer1, row, column);
163              break;
164            }
165            else {
166              // Notify player 1 to take the turn
167              toPlayer1.writeInt(CONTINUE);
168    
169              // Send player 2's selected row and column to player 1
170              sendMove(toPlayer1, row, column);
171            }
172          }
173        }
174        catch(IOException ex) {
175          ex.printStackTrace();
176        }
177      }
178    
179      /** Send the move to other player */
180      private void sendMove(DataOutputStream out, int row, int column)
181          throws IOException {
182        out.writeInt(row); // Send row index
183        out.writeInt(column); // Send column index
184      }
185    
186      /** Determine if the cells are all occupied */
187      private boolean isFull() {
188        for (int i = 0; i < 3; i++)
189          for (int j = 0; j < 3; j++)
190            if (cell[i][j] == ' ')
191              return false; // At least one cell is not filled
192    
193        // All cells are filled
194        return true;
195      }
196    
197      /** Determine if the player with the specified token wins */
198      private boolean isWon(char token) {
199        // Check all rows
200        for (int i = 0; i < 3; i++)
201          if ((cell[i][0] == token)
202              && (cell[i][1] == token)
203              && (cell[i][2] == token)) {
204            return true;
205          }
206    
207        /** Check all columns */
208        for (int j = 0; j < 3; j++)
209          if ((cell[0][j] == token)
210              && (cell[1][j] == token)
211              && (cell[2][j] == token)) {
212            return true;
213          }
214    
215        /** Check major diagonal */
216        if ((cell[0][0] == token)
217            && (cell[1][1] == token)
218            && (cell[2][2] == token)) {
219          return true;
220        }
221    
222        /** Check subdiagonal */
223        if ((cell[0][2] == token)
224            && (cell[1][1] == token)
225            && (cell[2][0] == token)) {
226          return true;
227        }
228    
229        /** All checked, but no winner */
230        return false;
231      }
232    }
233    
234    /**
235     * The main method is only needed for the IDE with limited
236     * JavaFX support. Not needed for running from the command line.
237     */
238    public static void main(String[] args) {
239      launch(args);
240    }
241  }