1  import java.rmi.*;
  2  
  3  import javafx.application.Application;
  4  import javafx.application.Platform;
  5  import javafx.stage.Stage;
  6  import javafx.scene.Scene;
  7  import javafx.scene.control.Label;
  8  import javafx.scene.layout.BorderPane;
  9  import javafx.scene.layout.GridPane;
 10  import javafx.scene.layout.Pane;
 11  import javafx.scene.paint.Color;
 12  import javafx.scene.shape.Line;
 13  import javafx.scene.shape.Ellipse;
 14  
 15  import java.rmi.registry.Registry;
 16  import java.rmi.registry.LocateRegistry;
 17  
 18  public class TicTacToeClientRMI extends Application {
 19    // marker is used to indicate the token type
 20    private char marker;
 21  
 22    // myTurn indicates whether the player can move now
 23    private boolean myTurn = false;
 24  
 25    // Indicate which player has a turn, initially it is the X player
 26    private char whoseTurn = 'X';
 27  
 28    // Create and initialize cell
 29    private Cell[][] cell =  new Cell[3][3];
 30  
 31    // Create and initialize a status label
 32    private Label lblStatus = new Label("X's turn to play");
 33  
 34    // ticTacToe is the game server for coordinating 
 35    // with the players
 36    private TicTacToeInterface ticTacToe;
 37  
 38    private Label lblIdentification = new Label();
 39  
 40    @Override // Override the start method in the Application class
 41    public void start(Stage primaryStage) {
 42      // Pane to hold cell
 43      GridPane pane = new GridPane(); 
 44      for (int i = 0; i < 3; i++)
 45        for (int j = 0; j < 3; j++)
 46          pane.add(cell[i][j] = new Cell(i, j), j, i);
 47  
 48      BorderPane borderPane = new BorderPane();
 49      borderPane.setCenter(pane);
 50      borderPane.setTop(lblStatus);
 51      borderPane.setBottom(lblIdentification);
 52      
 53      // Create a scene and place it in the stage
 54      Scene scene = new Scene(borderPane, 450, 170);
 55      primaryStage.setTitle("TicTacToe"); // Set the stage title
 56      primaryStage.setScene(scene); // Place the scene in the stage
 57      primaryStage.show(); // Display the stage   
 58      
 59      new Thread( () -> {
 60      try {
 61        initializeRMI();
 62      }
 63      catch (Exception ex) {
 64        ex.printStackTrace();
 65      }}).start();
 66    }
 67  
 68    /** Initialize RMI */
 69    protected boolean initializeRMI() throws Exception {
 70      String host = "";
 71  
 72      try {
 73        Registry registry = LocateRegistry.getRegistry(host);
 74        ticTacToe = (TicTacToeInterface) 
 75          registry.lookup("TicTacToeImpl");
 76        System.out.println
 77          ("Server object " + ticTacToe + " found");
 78      } 
 79      catch (Exception ex) {
 80        System.out.println(ex);
 81      }
 82  
 83      // Create callback for use by the 
 84      // server to control the client
 85      CallBackImpl callBackControl = new CallBackImpl(this);
 86  
 87      if (
 88        (marker = 
 89          ticTacToe.connect((CallBack)callBackControl)) != ' ')
 90      {
 91        System.out.println("connected as " + marker + " player.");
 92        Platform.runLater(() ->
 93          lblIdentification.setText("You are player " + marker));
 94        return true;
 95      }
 96      else {
 97        System.out.println("already two players connected as ");
 98        return false;
 99      }
100    }
101  
102    /** Set variable myTurn to true or false */
103    public void setMyTurn(boolean myTurn) {
104      this.myTurn = myTurn;
105    }
106  
107    /** Set message on the status label */
108    public void setMessage(String message) {
109      Platform.runLater(() -> lblStatus.setText(message));
110    }
111  
112    /** Mark the specified cell using the token */
113    public void mark(int row, int column, char token) {
114      cell[row][column].setToken(token);
115    }
116  
117    // An inner class for a cell
118    public class Cell extends Pane {
119      // marked indicates whether the cell has been used
120      private boolean marked = false;
121  
122      // row and column indicate where the cell appears on the board
123      int row, column;
124  
125     // Token used for this cell
126      private char token = ' ';
127  
128      public Cell(final int row, final int column) {
129        this.row = row;
130        this.column = column;
131        setStyle("-fx-border-color: black"); 
132        this.setPrefSize(2000, 2000);
133        this.setOnMouseClicked(e -> handleMouseClick());
134      }
135  
136      /** Return token */
137      public char getToken() {
138        return token;
139      }
140  
141      /** Set a new token */
142      public void setToken(char c) {
143        token = c;
144        marked = true;
145        
146        if (token == 'X') {
147          Line line1 = new Line(10, 10, 
148            this.getWidth() - 10, this.getHeight() - 10);
149          line1.endXProperty().bind(this.widthProperty().subtract(10));
150          line1.endYProperty().bind(this.heightProperty().subtract(10));
151          Line line2 = new Line(10, this.getHeight() - 10, 
152            this.getWidth() - 10, 10);
153          line2.startYProperty().bind(
154            this.heightProperty().subtract(10));
155          line2.endXProperty().bind(this.widthProperty().subtract(10));
156          
157          // Add the lines to the pane
158          Platform.runLater(() ->
159            this.getChildren().addAll(line1, line2)); 
160        }
161        else if (token == 'O') {
162          Ellipse ellipse = new Ellipse(this.getWidth() / 2, 
163            this.getHeight() / 2, this.getWidth() / 2 - 10, 
164            this.getHeight() / 2 - 10);
165          ellipse.centerXProperty().bind(
166            this.widthProperty().divide(2));
167          ellipse.centerYProperty().bind(
168              this.heightProperty().divide(2));
169          ellipse.radiusXProperty().bind(
170              this.widthProperty().divide(2).subtract(10));        
171          ellipse.radiusYProperty().bind(
172              this.heightProperty().divide(2).subtract(10));   
173          ellipse.setStroke(Color.BLACK);
174          ellipse.setFill(Color.WHITE);
175          
176          Platform.runLater(() ->
177            getChildren().add(ellipse)); // Add the ellipse to the pane
178        }
179      }
180  
181      /* Handle a mouse click event */
182      private void handleMouseClick() {
183        if (myTurn && !marked) {
184          // Mark the cell
185          setToken(marker);
186  
187          // Notify the server of the move
188          try {
189            ticTacToe.myMove(row, column, marker);
190          }
191          catch (RemoteException ex) {
192            System.out.println(ex);
193          }
194        }
195      }
196    }
197    
198    /**
199     * The main method is only needed for the IDE with limited
200     * JavaFX support. Not needed for running from the command line.
201     */
202    public static void main(String[] args) {
203      launch(args);
204    }
205  }