COMP2396
Loading...
Searching...
No Matches
GameServer.java
Go to the documentation of this file.
1import java.io.*;
2import java.net.*;
3
4/**
5 * Tic-Tac-Toe Server to handle two-player games over the network.
6 * COMP2396 Assignment 5
7 * @author Cheng Ho Ming, Eric (3036216734)
8 */
9public class GameServer {
10 private static final int PORT = 8901;
11 private static PlayerHandler playerX;
12 private static PlayerHandler playerO;
13 private static char[][] board = new char[3][3];
14 private static boolean gameEnded = false;
15
16 /**
17 * Main method to run the server.
18 * @param args Command-line arguments
19 * @throws Exception
20 */
21 public static void main(String[] args) throws Exception {
22 ServerSocket listener = new ServerSocket(PORT);
23 System.out.println("Tic-Tac-Toe Server is Running...");
24 try {
25 while (true) {
26 Socket socket = listener.accept();
27 if (playerX == null) {
28 playerX = new PlayerHandler(socket, 'X');
29 System.out.println("Player X connected.");
30 Thread playerX_Thread = new Thread(playerX);
31 playerX_Thread.start();
32 } else if (playerO == null) {
33 playerO = new PlayerHandler(socket, 'O');
34 System.out.println("Player O connected.");
35 Thread playerO_Thread = new Thread(playerO);
36 playerO_Thread.start();
37 } else {
38 // Reject additional players to prevent overload
39 PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
40 out.println("SERVERFULL");
41 socket.close();
42 }
43 }
44 } finally {
45 listener.close();
46 }
47 }
48
49 /**
50 * Make a move on the board.
51 * @param row The row of the move
52 * @param col The column of the move
53 * @param symbol The player's symbol ('X' or 'O')
54 * @return True if the move is valid, false otherwise
55 */
56 private synchronized static boolean makeMove(int row, int col, char symbol) {
57 if (board[row][col] == '\0') {
58 board[row][col] = symbol;
59 return true;
60 }
61 return false;
62 }
63
64 /**
65 * Check if a player has won the game.
66 * @return The winning player's symbol ('X' or 'O'), or 'D' for draw, or '\0' if no winner
67 */
68 private synchronized static char checkWin() {
69 // Check rows
70 for (int i = 0; i < 3; i++) {
71 if (board[i][0] == board[i][1] &&
72 board[i][1] == board[i][2] &&
73 board[i][0] != '\0') {
74 return board[i][0];
75 }
76 }
77 // Check columns
78 for (int i = 0; i < 3; i++) {
79 if (board[0][i] == board[1][i] &&
80 board[1][i] == board[2][i] &&
81 board[0][i] != '\0') {
82 return board[0][i];
83 }
84 }
85 // Check diagonals
86 if (board[0][0] == board[1][1] &&
87 board[1][1] == board[2][2] &&
88 board[0][0] != '\0') {
89 return board[0][0];
90 }
91 if (board[0][2] == board[1][1] &&
92 board[1][1] == board[2][0] &&
93 board[0][2] != '\0') {
94 return board[0][2];
95 }
96 // Check for draw
97 boolean draw = true;
98 for (int i = 0; i < 3 && draw; i++) {
99 for (int j = 0; j < 3 && draw; j++) {
100 if (board[i][j] == '\0') {
101 draw = false;
102 }
103 }
104 }
105 if (draw) return 'D';
106 return '\0';
107 }
108
109 /**
110 * Internal class to handle player connections.
111 */
112 private static class PlayerHandler implements Runnable {
113 private Socket socket;
114 private char symbol; // 'X' or 'O'
115 private BufferedReader in;
116 private PrintWriter out;
117 private String playerName;
118 private boolean nameSubmitted = false;
119
120 /**
121 * Constructor for the player handler.
122 * @param socket The player's socket object
123 * @param symbol The player's symbol ('X' or 'O')
124 */
125 public PlayerHandler(Socket socket, char symbol) {
126 this.socket = socket;
127 this.symbol = symbol;
128 }
129
130 /**
131 * Run the player handler.
132 * Handle player connections and messages.
133 * Handle game logic.
134 */
135 public void run() {
136 try {
137 in = new BufferedReader(
138 new InputStreamReader(socket.getInputStream()));
139 out = new PrintWriter(socket.getOutputStream(), true);
140
141 // First, get the player's name
142 out.println("SUBMITNAME");
143 playerName = in.readLine();
144 if (playerName == null || playerName.trim().isEmpty()) {
145 out.println("INVALIDNAME");
146 socket.close();
147 return;
148 }
149 nameSubmitted = true;
150 out.println("NAMEACCEPTED " + playerName);
151 System.out.println("Player " + symbol + ": " + playerName + " has joined.");
152
153 // Check if both players have submitted their names
154 if (playerX != null && playerX.nameSubmitted &&
155 playerO != null && playerO.nameSubmitted) {
156 initializeGame();
157 }
158
159 // Handle messages from the player
160 String command;
161 while ((command = in.readLine()) != null) {
162 if (command.startsWith("MOVE")) {
163 String[] parts = command.split(" ");
164 if (parts.length != 3) {
165 out.println("INVALID");
166 continue;
167 }
168 int row, col;
169 try {
170 row = Integer.parseInt(parts[1]);
171 col = Integer.parseInt(parts[2]);
172 } catch (NumberFormatException e) {
173 out.println("INVALID");
174 continue;
175 }
176 synchronized(GameServer.class) {
177 if ((symbol == 'X' && this == playerX) ||
178 (symbol == 'O' && this == playerO)) {
179 boolean valid = makeMove(row, col, symbol);
180 if (valid) {
181 broadcast("MOVE " + symbol + " " + row + " " + col);
182 char result = checkWin();
183 if (result == symbol) {
184 out.println("VICTORY");
185 getOpponent().out.println("DEFEAT");
186 gameEnded = true;
187 } else if (result == 'D') {
188 broadcast("TIE");
189 gameEnded = true;
190 } else {
191 // Switch turns
192 getOpponent().out.println("YOURMOVE");
193 this.out.println("WAIT");
194 }
195 } else {
196 out.println("INVALID");
197 }
198 }
199 }
200 } else if (command.startsWith("RESET")) {
201 synchronized(GameServer.class) {
202 if (gameEnded) {
203 resetGame();
204 }
205 }
206 }
207 }
208 } catch (IOException e) {
209 System.out.println("Error handling player " + symbol + ": " + e);
210 } finally {
211 try { socket.close(); } catch (IOException e) {}
212 synchronized(GameServer.class) {
213 if (symbol == 'X') playerX = null;
214 else playerO = null;
215 if (!gameEnded) {
216 broadcast("OTHERLEFT");
217 // resetGame();
218 }
219 }
220 }
221 }
222
223 /**
224 * Initialize the game.
225 */
226 private void initializeGame() {
227 broadcast("GAMESTART " + playerX.playerName + " " + playerO.playerName);
228 playerX.out.println("YOURMOVE");
229 playerO.out.println("WAIT");
230 System.out.println("Game started between " + playerX.playerName + " (X) and " + playerO.playerName + " (O).");
231 }
232
233 /**
234 * Reset the game state.
235 */
236 private void resetGame() {
237 board = new char[3][3];
238 gameEnded = false;
239 playerX.out.println("RESET");
240 playerO.out.println("RESET");
241 playerX.out.println("YOURMOVE");
242 playerO.out.println("WAIT");
243 System.out.println("Game has been reset.");
244 }
245
246 /**
247 * Get the opponent's handler.
248 * @return The opponent's handler
249 */
250 private PlayerHandler getOpponent() {
251 return (this == playerX) ? playerO : playerX;
252 }
253
254 /**
255 * Broadcast a message to all players.
256 * @param message The message to broadcast
257 */
258 private void broadcast(String message) {
259 if (playerX != null) playerX.out.println(message);
260 if (playerO != null) playerO.out.println(message);
261 }
262 }
263}
Tic-Tac-Toe Server to handle two-player games over the network.
static synchronized char checkWin()
Check if a player has won the game.
static synchronized boolean makeMove(int row, int col, char symbol)
Make a move on the board.
static void main(String[] args)
Main method to run the server.
static boolean gameEnded
static char[][] board
static final int PORT
static PlayerHandler playerO
static PlayerHandler playerX