COMP2396
Loading...
Searching...
No Matches
game.java
Go to the documentation of this file.
1import javax.swing.*;
2import javax.swing.plaf.metal.MetalButtonUI;
3import java.awt.*;
4import java.awt.event.*;
5import java.io.*;
6import java.net.*;
7import java.util.concurrent.atomic.AtomicBoolean;
8
9/**
10 * A two-player Tic Tac Toe game with GUI over the network.
11 * COMP2396 Assignment 5
12 * @author Cheng Ho Ming, Eric (3036216734)
13 */
14public class game {
15 JFrame frame;
16 JLabel welcomeLabel;
17 JTextField playerNameField;
18 JButton submitButton;
19 JPanel gameBoard;
20 JPanel scorePanel;
21 JLabel player1Score, player2Score, drawScore;
22 enum State {
23 EMPTY, PLAYER, OPPONENT
24 }
25 static char[][] board = new char[3][3];
26 int board_filled = 0;
27
28 // Networking components
29 Socket socket;
30 BufferedReader in;
31 PrintWriter out;
32 String playerName;
33 char mySymbol;
34 String opponentName;
35 AtomicBoolean myTurn = new AtomicBoolean(false);
36
37 /**
38 * Main method to start the game.
39 * Initialize the game board and GUI.
40 * @param args Does nothing
41 */
42 public static void main(String[] args) {
43 game gui = new game();
44 SwingUtilities.invokeLater(new Runnable() {
45 public void run() {
46 gui.go();
47 }
48 });
49 }
50
51 /**
52 * Initialize the GUI and establish connection to server.
53 */
54 public void go() {
55 // Initialize empty board
56 for (int i = 0; i < 3; i++) {
57 for (int j = 0; j < 3; j++) {
58 board[i][j] = '\0';
59 }
60 }
61
62 // Setup GUI
63 frame = new JFrame("Tic Tac Toe");
64 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
65
66 // Top display
67 JPanel topPanel = new JPanel();
68 frame.add(topPanel, BorderLayout.NORTH);
69 welcomeLabel = new JLabel("Enter your player name...");
70 topPanel.add(welcomeLabel);
71
72 // Bottom display with name input and time
73 JPanel bottomPanel = new JPanel();
74 bottomPanel.setLayout(new GridBagLayout());
75 frame.add(bottomPanel, BorderLayout.SOUTH);
76
77 // Player name input: holds text, textfield, and submit button
78 JPanel playerNameInputElements = new JPanel();
79 bottomPanel.add(playerNameInputElements);
80 playerNameInputElements.setLayout(new GridBagLayout());
81
82 // Text
83 JLabel playerNameLabel = new JLabel("Enter your name: ");
84 playerNameInputElements.add(playerNameLabel);
85
86 // Text field for input
87 playerNameField = new JTextField(20);
88 playerNameInputElements.add(playerNameField);
89
90 // Submit button
91 submitButton = new JButton("Submit");
92 playerNameInputElements.add(submitButton);
93 submitButton.addActionListener(new ActionListener() {
94 public void actionPerformed(ActionEvent event) {
95 playerName = playerNameField.getText().trim();
96 if (playerName.isEmpty()) {
97 JOptionPane.showMessageDialog(frame, "Name cannot be empty.");
98 return;
99 }
100 submitName();
101 }
102 });
103
104 // Time display
105 JLabel timeLabel = new JLabel(getLocalTime());
106 GridBagConstraints c = new GridBagConstraints();
107 c.gridy = 1;
108 bottomPanel.add(timeLabel, c);
109
110 // Update time every second
111 Timer timer = new Timer(1000, new ActionListener() {
112 public void actionPerformed(ActionEvent e) {
113 timeLabel.setText(getLocalTime());
114 }
115 });
116 timer.start();
117
118 // Menu bar
119 JMenuBar menuBar = new JMenuBar();
120 frame.setJMenuBar(menuBar);
121
122 // Menu: Control
123 JMenu controlMenu = new JMenu("Control");
124 menuBar.add(controlMenu);
125 JMenuItem exit = new JMenuItem("Exit");
126 exit.addActionListener(new ActionListener() {
127 public void actionPerformed(ActionEvent event) {
129 System.exit(0);
130 }
131 });
132 controlMenu.add(exit);
133
134 // Menu: Help
135 JMenu helpMenu = new JMenu("Help");
136 menuBar.add(helpMenu);
137 JMenuItem instruction = new JMenuItem("Instruction");
138 helpMenu.add(instruction);
139 instruction.addActionListener(new ActionListener() {
140 public void actionPerformed(ActionEvent event) {
141 Object[] options = {"Okay"};
142 JOptionPane.showOptionDialog(frame, "Some information about the game:\n\n"
143 + "Criteria for a valid move:\n"
144 + "- The move is not occupied by any mark.\n"
145 + "- The move is made in the player's turn.\n"
146 + "- The move is made within the 3 x 3 board.\n"
147 + "\n"
148 + "The game would continue and switch among the opposite player until it reaches either one of the following conditions:\n"
149 + "- Player 1 wins.\n"
150 + "- Player 2 wins.\n"
151 + "- Draw.\n"
152 + "- One of the players leaves the game.", "Game Information", JOptionPane.DEFAULT_OPTION, JOptionPane.INFORMATION_MESSAGE, null, options, options[0]);
153 }
154 });
155
156 // The 3x3 game board
157 gameBoard = new JPanel();
158 frame.add(gameBoard, BorderLayout.CENTER);
159 gameBoard.setLayout(new GridLayout(3,3));
160 for (int i = 0; i < 9; i++) {
161 JButton button = new JButton();
162 button.setText(" ");
163 // White background
164 button.setBackground(Color.WHITE);
165 // Disable at beginning
166 button.setEnabled(false);
167 gameBoard.add(button);
168 // Actual game logic handling
169 final int const_i = i;
170 button.addActionListener(new ActionListener() {
171 public void actionPerformed(ActionEvent event) {
172 if (!myTurn.get()) {
173 JOptionPane.showMessageDialog(frame, "Not your turn!");
174 return;
175 }
176 if (!button.getText().equals(" ")) {
177 JOptionPane.showMessageDialog(frame, "Cell already occupied!");
178 return;
179 }
180 // Send move to server
181 int row = const_i / 3;
182 int col = const_i % 3;
183 out.println("MOVE " + row + " " + col);
184 }
185 });
186 }
187
188 // Score display on the right
189 scorePanel = new JPanel();
190 scorePanel.setBorder(BorderFactory.createTitledBorder("Score"));
191 frame.add(scorePanel, BorderLayout.EAST);
192 // Labels inside the score panel
193 scorePanel.setLayout(new GridBagLayout());
194 GridBagConstraints c2 = new GridBagConstraints();
195 c2.anchor = GridBagConstraints.WEST;
196 c2.insets = new Insets(5,5,5,5);
197
198 // Player score
199 JLabel player1ScoreText = new JLabel("Player 1 Wins:");
200 c2.gridy = 0;
201 c2.gridx = 0;
202 scorePanel.add(player1ScoreText, c2);
203 player1Score = new JLabel("0");
204 c2.gridx = 1;
205 scorePanel.add(player1Score, c2);
206
207 // Opponent score
208 JLabel player2ScoreText = new JLabel("Player 2 Wins:");
209 c2.gridx = 0;
210 c2.gridy = 1;
211 scorePanel.add(player2ScoreText, c2);
212 player2Score = new JLabel("0");
213 c2.gridx = 1;
214 scorePanel.add(player2Score, c2);
215
216 // Draw score
217 JLabel drawScoreText = new JLabel("Draws:");
218 c2.gridx = 0;
219 c2.gridy = 2;
220 scorePanel.add(drawScoreText, c2);
221 drawScore = new JLabel("0");
222 c2.gridx = 1;
223 scorePanel.add(drawScore, c2);
224
225 // Frame settings
226 frame.setSize(600,600);
227 frame.setVisible(true);
228
229 // Initialize networking
231 }
232
233 /**
234 * Establish connection to the server.
235 */
236 private void initializeConnection() {
237 try {
238 // Connect to the server on localhost
239 socket = new Socket("127.0.0.1", 8901);
240 in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
241 out = new PrintWriter(socket.getOutputStream(), true);
242
243 // Start a thread to listen for messages from the server
244 Thread listener = new Thread(new Runnable() {
245 public void run() {
246 try {
247 String response;
248 while ((response = in.readLine()) != null) {
249 if (response.startsWith("SUBMITNAME")) {
250 // Server requests player to submit name, no action needed here
251 } else if (response.startsWith("INVALIDNAME")) {
252 // Invalid name, prompt user
253 SwingUtilities.invokeLater(new Runnable() {
254 public void run() {
255 JOptionPane.showMessageDialog(frame, "Invalid name submitted. Connection closing.");
257 System.exit(0);
258 }
259 });
260 } else if (response.startsWith("NAMEACCEPTED")) {
261 // Name accepted, wait for both players to join
262 SwingUtilities.invokeLater(new Runnable() {
263 public void run() {
264 welcomeLabel.setText("WELCOME " + playerName);
265 frame.setTitle("Tic Tac Toe - Player: " + playerName.toUpperCase());
266 }
267 });
268 } else if (response.startsWith("GAMESTART")) {
269 String[] parts = response.split(" ");
270 if (parts.length >= 3) {
271 String playerXName = parts[1];
272 String playerOName = parts[2];
273 if (mySymbol == '\0') { // First time receiving GAMESTART
274 if (playerXName.equals(playerName)) {
275 mySymbol = 'X';
276 opponentName = playerOName;
277 } else {
278 mySymbol = 'O';
279 opponentName = playerXName;
280 }
281 if (mySymbol == 'X') {
282 welcomeLabel.setText("Game started. Now is your turn.");
283 myTurn.set(true);
284 enableGameBoard(true);
285 } else {
286 welcomeLabel.setText("WELCOME " + playerName);
287 myTurn.set(false);
288 enableGameBoard(false);
289 }
290 }
291 }
292 } else if (response.startsWith("MOVE")) {
293 String[] parts = response.split(" ");
294 if (parts.length != 4) continue;
295 char symbol = parts[1].charAt(0);
296 int row = Integer.parseInt(parts[2]);
297 int col = Integer.parseInt(parts[3]);
298 SwingUtilities.invokeLater(new Runnable() {
299 public void run() {
300 updateBoard(symbol, row, col);
301 if (symbol != mySymbol) {
302 welcomeLabel.setText("Your opponent has moved. Now is your turn.");
303 myTurn.set(true);
304 enableGameBoard(true);
305 } else {
306 welcomeLabel.setText("Valid move, wait for your opponent.");
307 myTurn.set(false);
308 enableGameBoard(false);
309 }
310 }
311 });
312 } else if (response.startsWith("VICTORY")) {
313 SwingUtilities.invokeLater(new Runnable() {
314 public void run() {
315 int option = JOptionPane.showConfirmDialog(frame, "Congratulations. You wins! Do you want to play again?", "Game Over", JOptionPane.YES_NO_OPTION);
316 if (option == JOptionPane.YES_OPTION) {
317 out.println("RESET");
318 } else {
319 closeConnection();
320 System.exit(0);
321 }
322 if (mySymbol == 'X') {
323 player1Score.setText(Integer.parseInt(player1Score.getText()) + 1 + "");
324 } else {
325 player2Score.setText(Integer.parseInt(player2Score.getText()) + 1 + "");
326 }
327 }
328 });
329 } else if (response.startsWith("DEFEAT")) {
330 SwingUtilities.invokeLater(new Runnable() {
331 public void run() {
332 int option = JOptionPane.showConfirmDialog(frame, "You Lose! Do you want to play again?", "Game Over", JOptionPane.YES_NO_OPTION);
333 if (option == JOptionPane.YES_OPTION) {
334 out.println("RESET");
335 } else {
337 System.exit(0);
338 }
339 if (mySymbol == 'X') {
340 player2Score.setText(Integer.parseInt(player2Score.getText()) + 1 + "");
341 } else {
342 player1Score.setText(Integer.parseInt(player1Score.getText()) + 1 + "");
343 }
344 }
345 });
346 } else if (response.startsWith("TIE")) {
347 SwingUtilities.invokeLater(new Runnable() {
348 public void run() {
349 int option = JOptionPane.showConfirmDialog(frame, "It's a Draw! Do you want to play again?", "Game Over", JOptionPane.YES_NO_OPTION);
350 if (option == JOptionPane.YES_OPTION) {
351 out.println("RESET");
352 } else {
354 System.exit(0);
355 }
356 drawScore.setText(Integer.parseInt(drawScore.getText()) + 1 + "");
357 }
358 });
359 } else if (response.startsWith("YOURMOVE")) {
360 SwingUtilities.invokeLater(new Runnable() {
361 public void run() {
362 welcomeLabel.setText("Your opponent has moved. Now is your turn.");
363 myTurn.set(true);
364 enableGameBoard(true);
365 }
366 });
367 } else if (response.startsWith("WAIT")) {
368 SwingUtilities.invokeLater(new Runnable() {
369 public void run() {
370 welcomeLabel.setText("Valid move, wait for your opponent.");
371 myTurn.set(false);
372 enableGameBoard(false);
373 }
374 });
375 } else if (response.startsWith("MESSAGE")) {
376 String msg = response.substring(8);
377 SwingUtilities.invokeLater(new Runnable() {
378 public void run() {
379 welcomeLabel.setText(msg);
380 }
381 });
382 } else if (response.startsWith("INVALID")) {
383 SwingUtilities.invokeLater(new Runnable() {
384 public void run() {
385 JOptionPane.showMessageDialog(frame, "Invalid Move!");
386 }
387 });
388 } else if (response.startsWith("OTHERLEFT")) {
389 SwingUtilities.invokeLater(new Runnable() {
390 public void run() {
391 JOptionPane.showMessageDialog(frame, "Game Ends. One of the players left.");
392 welcomeLabel.setText("Game started. Wait for your opponent.");
393 resetBoard();
394 enableGameBoard(false);
395 }
396 });
397 } else if (response.startsWith("RESET")) {
398 SwingUtilities.invokeLater(new Runnable() {
399 public void run() {
400 resetBoard();
401 }
402 });
403 } else if (response.startsWith("SERVERFULL")) {
404 SwingUtilities.invokeLater(new Runnable() {
405 public void run() {
406 JOptionPane.showMessageDialog(frame, "Server is full. Try again later.");
408 System.exit(0);
409 }
410 });
411 }
412 }
413 } catch (IOException e) {
414 System.out.println("Connection lost.");
415 SwingUtilities.invokeLater(new Runnable() {
416 public void run() {
417 JOptionPane.showMessageDialog(frame, "Connection to server lost.");
418 resetBoard();
419 }
420 });
421 }
422 }
423 });
424 listener.start();
425
426 } catch (IOException e) {
427 JOptionPane.showMessageDialog(frame, "Cannot connect to the server.");
428 System.exit(0);
429 }
430 }
431
432 /**
433 * Submit the player's name to the server.
434 */
435 private void submitName() {
436 out.println(playerName);
437 // Update GUI
438 welcomeLabel.setText("Waiting for another player to join...");
439 playerNameField.setEnabled(false);
440 submitButton.setEnabled(false);
441 }
442
443 /**
444 * Update the board with the move.
445 * @param symbol The symbol to be placed on the board
446 * @param row The row of the move
447 * @param col The column of the move
448 */
449 private void updateBoard(char symbol, int row, int col) {
450 board[row][col] = symbol;
451 int index = row * 3 + col;
452 JButton button = (JButton)gameBoard.getComponent(index);
453 button.setText(String.valueOf(symbol));
454 button.setFont(new Font("Arial", Font.BOLD, 40));
455 if (symbol == 'X') {
456 button.setForeground(Color.GREEN);
457 } else {
458 button.setForeground(Color.RED);
459 }
460 }
461
462 /**
463 * Reset the board to start a new game or due to opponent leaving.
464 */
465 private void resetBoard() {
466 board_filled = 0;
467 for (int i = 0; i < 3; i++) {
468 for (int j=0; j <3; j++) {
469 board[i][j] = '\0';
470 }
471 }
472 Component[] components = gameBoard.getComponents();
473 for (Component component : components) {
474 JButton button = (JButton)component;
475 button.setText(" ");
476 button.setEnabled(false);
477 button.setUI(new MetalButtonUI() {
478 protected Color getDisabledTextColor() {
479 return button.getForeground();
480 }
481 });
482 }
483 if (mySymbol == 'X') {
484 welcomeLabel.setText("WELCOME " + playerName + ". Your move.");
485 myTurn.set(true);
486 enableGameBoard(true);
487 } else if (mySymbol == 'O') {
488 welcomeLabel.setText("WELCOME " + playerName + ". Wait for opponent's move.");
489 myTurn.set(false);
490 enableGameBoard(false);
491 } else {
492 // No symbol, possibly due to unsuccessful connection
493 welcomeLabel.setText("Enter your player name...");
494 }
495 }
496
497 /**
498 * Enable or disable the game board buttons based on the player's turn.
499 * @param enable True to enable, false to disable
500 */
501 private void enableGameBoard(boolean enable) {
502 Component[] components = gameBoard.getComponents();
503 for (Component component : components) {
504 JButton button = (JButton)component;
505 button.setEnabled(false);
506 if (button.getText().equals(" ")) {
507 button.setEnabled(enable);
508 }
509 button.setUI(new MetalButtonUI() {
510 protected Color getDisabledTextColor() {
511 return button.getForeground();
512 }
513 });
514 button.setFocusPainted(false);
515 }
516 }
517
518 /**
519 * Get the current system time in 24-hour format without milliseconds.
520 * @return String representation of current time, e.g., "Current Time: 12:34:56"
521 */
522 private String getLocalTime() {
523 return "Current Time: " + java.time.LocalTime.now().format(java.time.format.DateTimeFormatter.ofPattern("HH:mm:ss"));
524 }
525
526 /**
527 * Close the connection to the server.
528 */
529 private void closeConnection() {
530 try {
531 if (out != null) out.close();
532 if (in != null) in.close();
533 if (socket != null) socket.close();
534 } catch (IOException e) {
535 // Ignore exceptions
536 }
537 }
538}
A simple Tic Tac Toe game with GUI.
Definition game.java:11
void closeConnection()
Close the connection to the server.
Definition game.java:529
void initializeConnection()
Establish connection to the server.
Definition game.java:236
String getLocalTime()
Get the current system time in 24 hour format.
Definition game.java:251
void go()
Initialize the GUI.
Definition game.java:43
void enableGameBoard(boolean enable)
Enable or disable the game board buttons based on the player's turn.
Definition game.java:501
void updateBoard(char symbol, int row, int col)
Update the board with the move.
Definition game.java:449
void resetBoard()
Reset the board to start a new game or due to opponent leaving.
Definition game.java:465
static void main(String[] args)
Main method to start the game.
Definition game.java:42
void submitName()
Submit the player's name to the server.
Definition game.java:435