-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathConnectClient.java
434 lines (391 loc) · 12.7 KB
/
ConnectClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
import java.io.*;
import java.net.*;
import java.util.Random;
import com.sun.prism.paint.RadialGradient;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.LinearGradientBuilder;
import javafx.scene.paint.RadialGradientBuilder;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class ConnectClient extends Application
implements ConnectConstants {
private boolean myTurn = false;
// Indicate the token for the player
private char myToken = '.';
// Indicate the token for the other player
private char otherToken = '.';
// Create and initialize cells
Cell[][] cell = new Cell[ConnectConstants.TRACK_LENGTH][ConnectConstants.TRACKS];
// Create and initialize a ntoification bar
private Label lblStatus = new Label();
// Indicate selected row and column by the current move
private int rowSelected;
private int columnSelected;
// Input and output streams from/to server
private DataInputStream fromServer;
private DataOutputStream toServer;
private boolean continueToPlay = true;
private boolean waiting = true;
//random value for dropping blocks
Random random = new Random(ConnectConstants.RANDOM_SEED);
// Host name or ip
private String host = "localhost"; //Or through azure @ 104.208.38.138 shoutout Microsoft
@Override // Override the start method in the Application class
public void start(Stage primaryStage) {
setUserAgentStylesheet(STYLESHEET_MODENA);
GridPane pane = new GridPane();
pane.setStyle("-fx-background-color: linear-gradient(to bottom right, yellow, gold)");
for (int i = 0; i < ConnectConstants.TRACK_LENGTH; i++)
for (int j = 0; j < ConnectConstants.TRACKS; j++){
pane.add(cell[i][j] = new Cell(i, j), j, i);
cell[i][j].setToken('.');
}
BorderPane borderPane = new BorderPane();
borderPane.setCenter(pane);
borderPane.setBottom(lblStatus);
// Create a scene and place it in the stage
Scene scene = new Scene(borderPane, 50 * ConnectConstants.TRACKS, 50 * ConnectConstants.TRACK_LENGTH + 10);
primaryStage.setTitle("ConnectThem"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show(); // Display the stage
// Connect to the server
connectToServer();
}
private void connectToServer() {
try {
// Create a socket to connect to the server
Socket socket = new Socket(host, 8000);
// Create an input stream to receive data from the server
fromServer = new DataInputStream(socket.getInputStream());
// Create an output stream to send data to the server
toServer = new DataOutputStream(socket.getOutputStream());
}
catch (Exception ex) {
ex.printStackTrace();
}
// Control the game on a separate thread
new Thread(() -> {
try {
// Get notification from the server
int player = fromServer.readInt();
// get player number
if (player == PLAYER1) {
myToken = 'a';
otherToken = 'b';
Platform.runLater(() -> {
lblStatus.setText("Waiting for Player 2 to join.");
});
// Receive startup notification from the server
fromServer.readInt(); // Whatever read is ignored
// The other player has joined
Platform.runLater(() ->
lblStatus.setText("Player 2 has joined. You go first."));
// It is your turn
myTurn = true;
}
else if (player == PLAYER2) {
myToken = 'b';
otherToken = 'a';
Platform.runLater(() -> {
lblStatus.setText("Waiting for Player 1 to move.");
});
}
// Continue to play
while (continueToPlay) {
if (player == PLAYER1) {
waitForPlayerAction(); // Wait for player 1 to move
sendMove(); // Send the move to the server
receiveInfoFromServer(); // Receive info from the server
}
else if (player == PLAYER2) {
receiveInfoFromServer(); // Receive info from the server
waitForPlayerAction(); // Wait for player 2 to move
sendMove(); // Send player 2's move to the server
}
//Removed because apparently being awesome is against TCP protocol
//dropARando();
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}).start();
}
private void dropARando() throws IOException {
System.out.println("Dropping brick");
//drop a block into a random column
int col = random.nextInt(ConnectConstants.TRACKS);
int row = 0;
for (int i = 0; i < ConnectConstants.TRACK_LENGTH; i++) {
row = i;
}
cell[row][col].token = 'c';
//Doesn't like this
cell[row][col].repaint();
waiting = false;
}
/** Wait for the player to mark a cell */
private void waitForPlayerAction() throws InterruptedException {
while (waiting) {
Thread.sleep(100);
}
waiting = true;
}
/** Send this player's move to the server */
private void sendMove() throws IOException {
toServer.writeInt(rowSelected);
toServer.writeInt(columnSelected);
}
private void paintBlack(){
for (int row = 0; row < ConnectConstants.TRACK_LENGTH; row++) {
for (int col = 0; col < ConnectConstants.TRACKS; col++) {
if (cell[row][col].token == '.') {
cell[row][col].setToken('c');
}
}
}
}
/** Receive info from the server */
private void receiveInfoFromServer() throws IOException {
// Receive game status
int status = fromServer.readInt();
if (status == PLAYER1_WON) {
// Player 1 won, stop playing
continueToPlay = false;
if (myToken == 'a') {
Platform.runLater(() -> {
lblStatus.setText("I won!");
lblStatus.setTextFill(Color.RED);
});
}
else if (myToken == 'b') {
Platform.runLater(() -> {
lblStatus.setText("Player 1 has won!");
lblStatus.setTextFill(Color.RED);
});
receiveMove();
}
//paintBlack();
}
else if (status == PLAYER2_WON) {
// Player 2 won, stop playing
continueToPlay = false;
if (myToken == 'b') {
Platform.runLater(() -> {
lblStatus.setText("I won!");
lblStatus.setTextFill(Color.BLUE);
});
}
else if (myToken == 'a') {
Platform.runLater(() -> {
lblStatus.setText("Player 2 has won!");
lblStatus.setTextFill(Color.BLUE);
});
receiveMove();
}
//paintBlack();
}
else if (status == DRAW) {
// No winner, game is over
continueToPlay = false;
Platform.runLater(() ->
lblStatus.setText("Game is over, no winner!"));
if (myToken == 'b') {
receiveMove();
}
}
else {
receiveMove();
Platform.runLater(() -> lblStatus.setText("My turn"));
myTurn = true; // It is my turn
}
}
private void receiveMove() throws IOException {
// Get the other player's move
int row = fromServer.readInt();
int column = fromServer.readInt();
Platform.runLater(() -> cell[row][column].setToken(otherToken));
}
// An inner class for a cell
public class Cell extends Pane {
// Indicate the row and column of this cell in the board
private int row;
private int column;
// Token used for this cell
private char token = '.';
public Cell(int row, int column) {
this.row = row;
this.column = column;
this.setPrefSize(2000, 2000); // What happens without this?
setStyle("-fx-border-color: black"); // Set cell's border
this.setOnMouseClicked(e -> handleMouseClick());
}
public boolean isColFull(int col) {
char token = cell[0][col].getToken();
if (token != '.') {
return true;
}
else return false;
}
/** Return token */
public char getToken() {
return token;
}
/** Set a new token */
public void setToken(char c) {
token = c;
repaint();
}
protected void repaint() {
Ellipse ellipse;
if (token == 'a') {
javafx.scene.paint.RadialGradient gradient1 = RadialGradientBuilder.create()
.focusAngle(0)
.focusDistance(.1)
.centerX(80)
.centerY(45)
.radius(120)
.proportional(false)
.cycleMethod(CycleMethod.NO_CYCLE)
.stops(new Stop(0, Color.RED), new Stop(1, Color.DARKRED))
.build();
ellipse = new Ellipse(this.getWidth() / 2,
this.getHeight() / 2, this.getWidth() / 2 - 10,
this.getHeight() / 2 - 10);
ellipse.centerXProperty().bind(
this.widthProperty().divide(2));
ellipse.centerYProperty().bind(
this.heightProperty().divide(2));
ellipse.radiusXProperty().bind(
this.widthProperty().divide(2).subtract(10));
ellipse.radiusYProperty().bind(
this.heightProperty().divide(2).subtract(10));
ellipse.setStroke(Color.BLACK);
//ellipse.setFill(Color.RED);
ellipse.setFill(gradient1);
this.getChildren().add(ellipse);
ellipse = new Ellipse(this.getWidth() / 2,
this.getHeight() / 2, this.getWidth() / 2 - 15,
this.getHeight() / 2 - 15);
ellipse.centerXProperty().bind(
this.widthProperty().divide(2));
ellipse.centerYProperty().bind(
this.heightProperty().divide(2));
ellipse.radiusXProperty().bind(
this.widthProperty().divide(2).subtract(20));
ellipse.radiusYProperty().bind(
this.heightProperty().divide(2).subtract(20));
ellipse.setStroke(Color.BLACK);
//ellipse.setFill(Color.RED);
ellipse.setFill(gradient1);
this.getChildren().add(ellipse);
}
else if (token == 'b') {
javafx.scene.paint.RadialGradient gradient1 = RadialGradientBuilder.create()
.focusAngle(0)
.focusDistance(.1)
.centerX(80)
.centerY(45)
.radius(120)
.proportional(false)
.cycleMethod(CycleMethod.NO_CYCLE)
.stops(new Stop(0, Color.BLUE), new Stop(1, Color.DARKBLUE))
.build();
ellipse = new Ellipse(this.getWidth() / 2,
this.getHeight() / 2, this.getWidth() / 2 - 10,
this.getHeight() / 2 - 10);
ellipse.centerXProperty().bind(
this.widthProperty().divide(2));
ellipse.centerYProperty().bind(
this.heightProperty().divide(2));
ellipse.radiusXProperty().bind(
this.widthProperty().divide(2).subtract(10));
ellipse.radiusYProperty().bind(
this.heightProperty().divide(2).subtract(10));
ellipse.setStroke(Color.BLACK);
//ellipse.setFill(Color.BLUE);
ellipse.setFill(gradient1);
this.getChildren().add(ellipse);
ellipse = new Ellipse(this.getWidth() / 2,
this.getHeight() / 2, this.getWidth() / 2 - 15,
this.getHeight() / 2 - 15);
ellipse.centerXProperty().bind(
this.widthProperty().divide(2));
ellipse.centerYProperty().bind(
this.heightProperty().divide(2));
ellipse.radiusXProperty().bind(
this.widthProperty().divide(2).subtract(20));
ellipse.radiusYProperty().bind(
this.heightProperty().divide(2).subtract(20));
ellipse.setStroke(Color.BLACK);
//ellipse.setFill(Color.BLUE);
ellipse.setFill(gradient1);
this.getChildren().add(ellipse);
}
else if (token == 'c') {
Rectangle rect = new Rectangle(this.getWidth(), this.getHeight(), this.getWidth()/2, this.getHeight()/2);
rect.setStroke(Color.BLACK);
rect.setFill(Color.BLACK);
this.getChildren().add(rect);
}
else if (token == '.') {
ellipse = new Ellipse(this.getWidth() / 2,
this.getHeight() / 2, this.getWidth() / 2 - 10,
this.getHeight() / 2 - 10);
ellipse.centerXProperty().bind(
this.widthProperty().divide(2));
ellipse.centerYProperty().bind(
this.heightProperty().divide(2));
ellipse.radiusXProperty().bind(
this.widthProperty().divide(2).subtract(10));
ellipse.radiusYProperty().bind(
this.heightProperty().divide(2).subtract(10));
ellipse.setStroke(Color.BLACK);
//ellipse.setFill(Color.rgb(255, 255, 204));
javafx.scene.paint.RadialGradient gradient1 = RadialGradientBuilder.create()
.focusAngle(0)
.focusDistance(.1)
.centerX(80)
.centerY(45)
.radius(120)
.proportional(false)
.cycleMethod(CycleMethod.NO_CYCLE)
.stops(new Stop(0, Color.RED), new Stop(1, Color.BLACK))
.build();
ellipse.setFill(gradient1);
this.getChildren().add(ellipse);
}
}
/* Handle a mouse click event */
private void handleMouseClick() {
// If cell is not occupied and the player has the turn
if (myTurn && !isColFull(column)) {
myTurn = false;
rowSelected = row;
columnSelected = column;
for (int i = 0; i < ConnectConstants.TRACK_LENGTH; i++) {
if (cell[i][column].getToken() == '.') {
rowSelected = i;
}
}
cell[rowSelected][columnSelected].setToken(myToken); // Set the player's token in the cell
lblStatus.setText("Waiting for the other player to move");
waiting = false; // Just completed a successful move
}
}
}
public static void main (String[] args) {
Application.launch(args);
}
}