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.Label;
8 import javafx.scene.control.ScrollPane;
9 import javafx.scene.control.TextArea;
10 import javafx.scene.layout.BorderPane;
11 import javafx.scene.layout.GridPane;
12 import javafx.scene.layout.Pane;
13 import javafx.scene.paint.Color;
14 import javafx.scene.shape.Ellipse;
15 import javafx.scene.shape.Line;
16 import javafx.stage.Stage;
17
18 public class TicTacToeClient extends Application
19 implements TicTacToeConstants {
20
21 private boolean myTurn = false;
22
23
24 private char myToken = ' ';
25
26
27 private char otherToken = ' ';
28
29
30 private Cell[][] cell = new Cell[3][3];
31
32
33 private Label lblTitle = new Label();
34
35
36 private Label lblStatus = new Label();
37
38
39 private int rowSelected;
40 private int columnSelected;
41
42
43 private DataInputStream fromServer;
44 private DataOutputStream toServer;
45
46
47 private boolean continueToPlay = true;
48
49
50 private boolean waiting = true;
51
52
53 private String host = "localhost";
54
55 @Override
56 public void start(Stage primaryStage) {
57
58 GridPane pane = new GridPane();
59 for (int i = 0; i < 3; i++)
60 for (int j = 0; j < 3; j++)
61 pane.add(cell[i][j] = new Cell(i, j), j, i);
62
63 BorderPane borderPane = new BorderPane();
64 borderPane.setTop(lblTitle);
65 borderPane.setCenter(pane);
66 borderPane.setBottom(lblStatus);
67
68
69 Scene scene = new Scene(borderPane, 320, 350);
70 primaryStage.setTitle("TicTacToeClient");
71 primaryStage.setScene(scene);
72 primaryStage.show();
73
74
75 connectToServer();
76 }
77
78 private void connectToServer() {
79 try {
80
81 Socket socket = new Socket(host, 8000);
82
83
84 fromServer = new DataInputStream(socket.getInputStream());
85
86
87 toServer = new DataOutputStream(socket.getOutputStream());
88 }
89 catch (Exception ex) {
90 ex.printStackTrace();
91 }
92
93
94 new Thread(() -> {
95 try {
96
97 int player = fromServer.readInt();
98
99
100 if (player == PLAYER1) {
101 myToken = 'X';
102 otherToken = 'O';
103 Platform.runLater(() -> {
104 lblTitle.setText("Player 1 with token 'X'");
105 lblStatus.setText("Waiting for player 2 to join");
106 });
107
108
109 fromServer.readInt();
110
111
112 Platform.runLater(() ->
113 lblStatus.setText("Player 2 has joined. I start first"));
114
115
116 myTurn = true;
117 }
118 else if (player == PLAYER2) {
119 myToken = 'O';
120 otherToken = 'X';
121 Platform.runLater(() -> {
122 lblTitle.setText("Player 2 with token 'O'");
123 lblStatus.setText("Waiting for player 1 to move");
124 });
125 }
126
127
128 while (continueToPlay) {
129 if (player == PLAYER1) {
130 waitForPlayerAction();
131 sendMove();
132 receiveInfoFromServer();
133 }
134 else if (player == PLAYER2) {
135 receiveInfoFromServer();
136 waitForPlayerAction();
137 sendMove();
138 }
139 }
140 }
141 catch (Exception ex) {
142 ex.printStackTrace();
143 }
144 }).start();
145 }
146
147
148 private void waitForPlayerAction() throws InterruptedException {
149 while (waiting) {
150 Thread.sleep(100);
151 }
152
153 waiting = true;
154 }
155
156
157 private void sendMove() throws IOException {
158 toServer.writeInt(rowSelected);
159 toServer.writeInt(columnSelected);
160 }
161
162
163 private void receiveInfoFromServer() throws IOException {
164
165 int status = fromServer.readInt();
166
167 if (status == PLAYER1_WON) {
168
169 continueToPlay = false;
170 if (myToken == 'X') {
171 Platform.runLater(() -> lblStatus.setText("I won! (X)"));
172 }
173 else if (myToken == 'O') {
174 Platform.runLater(() ->
175 lblStatus.setText("Player 1 (X) has won!"));
176 receiveMove();
177 }
178 }
179 else if (status == PLAYER2_WON) {
180
181 continueToPlay = false;
182 if (myToken == 'O') {
183 Platform.runLater(() -> lblStatus.setText("I won! (O)"));
184 }
185 else if (myToken == 'X') {
186 Platform.runLater(() ->
187 lblStatus.setText("Player 2 (O) has won!"));
188 receiveMove();
189 }
190 }
191 else if (status == DRAW) {
192
193 continueToPlay = false;
194 Platform.runLater(() ->
195 lblStatus.setText("Game is over, no winner!"));
196
197 if (myToken == 'O') {
198 receiveMove();
199 }
200 }
201 else {
202 receiveMove();
203 Platform.runLater(() -> lblStatus.setText("My turn"));
204 myTurn = true;
205 }
206 }
207
208 private void receiveMove() throws IOException {
209
210 int row = fromServer.readInt();
211 int column = fromServer.readInt();
212 Platform.runLater(() -> cell[row][column].setToken(otherToken));
213 }
214
215
216 public class Cell extends Pane {
217
218 private int row;
219 private int column;
220
221
222 private char token = ' ';
223
224 public Cell(int row, int column) {
225 this.row = row;
226 this.column = column;
227 this.setPrefSize(2000, 2000);
228 setStyle("-fx-border-color: black");
229 this.setOnMouseClicked(e -> handleMouseClick());
230 }
231
232
233 public char getToken() {
234 return token;
235 }
236
237
238 public void setToken(char c) {
239 token = c;
240 repaint();
241 }
242
243 protected void repaint() {
244 if (token == 'X') {
245 Line line1 = new Line(10, 10,
246 this.getWidth() - 10, this.getHeight() - 10);
247 line1.endXProperty().bind(this.widthProperty().subtract(10));
248 line1.endYProperty().bind(this.heightProperty().subtract(10));
249 Line line2 = new Line(10, this.getHeight() - 10,
250 this.getWidth() - 10, 10);
251 line2.startYProperty().bind(
252 this.heightProperty().subtract(10));
253 line2.endXProperty().bind(this.widthProperty().subtract(10));
254
255
256 this.getChildren().addAll(line1, line2);
257 }
258 else if (token == 'O') {
259 Ellipse ellipse = new Ellipse(this.getWidth() / 2,
260 this.getHeight() / 2, this.getWidth() / 2 - 10,
261 this.getHeight() / 2 - 10);
262 ellipse.centerXProperty().bind(
263 this.widthProperty().divide(2));
264 ellipse.centerYProperty().bind(
265 this.heightProperty().divide(2));
266 ellipse.radiusXProperty().bind(
267 this.widthProperty().divide(2).subtract(10));
268 ellipse.radiusYProperty().bind(
269 this.heightProperty().divide(2).subtract(10));
270 ellipse.setStroke(Color.BLACK);
271 ellipse.setFill(Color.WHITE);
272
273 getChildren().add(ellipse);
274 }
275 }
276
277
278 private void handleMouseClick() {
279
280 if (token == ' ' && myTurn) {
281 setToken(myToken);
282 myTurn = false;
283 rowSelected = row;
284 columnSelected = column;
285 lblStatus.setText("Waiting for the other player to move");
286 waiting = false;
287 }
288 }
289 }
290
291
295 public static void main(String[] args) {
296 launch(args);
297 }
298 }