-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathTicTacToeServer.java
258 lines (228 loc) · 9.74 KB
/
TicTacToeServer.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
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class TicTacToeServer {
private static Map<String, RoomThread> rooms = new ConcurrentHashMap<>();
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(1234)) {
System.out.println("Server started. Waiting for clients...");
while (true) {
Socket clientSocket = serverSocket.accept();
// 새로운 클라이언트 연결 시 핸들러 스레드 생성
new Thread(new ClientHandler(clientSocket)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 클라이언트와의 통신을 처리하는 핸들러 클래스
static class ClientHandler implements Runnable {
private Socket socket;
private PrintWriter out;
private BufferedReader in;
private RoomThread joinedRoom;
public ClientHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 클라이언트와 입출력 스트림 연결
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
String input;
while ((input = in.readLine()) != null) {
// 명령과 인자를 분리
String[] parts = input.split(" ", 2);
String command = parts[0];
String argument = parts.length > 1 ? parts[1] : "";
switch (command) {
case "CREATE":
createRoom(argument);
break;
case "JOIN":
joinRoom(argument);
break;
case "MOVE":
handleMove(argument);
break;
case "LEAVE":
leaveRoom();
break;
default:
out.println("ERROR Unknown command.");
break;
}
}
} catch (IOException e) {
// 클라이언트가 연결을 끊었을 때 처리
if (joinedRoom != null) {
joinedRoom.removeClient(out);
}
System.out.println("Client disconnected");
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void leaveRoom() {
if (joinedRoom != null) {
joinedRoom.removeClient(out);
joinedRoom = null;
out.println("LEFT"); // 클라이언트에게 방에서 나갔음을 알려줌
try {
socket.close(); // 클라이언트 소켓 종료
} catch (IOException e) {
e.printStackTrace();
}
} else {
out.println("ERROR You are not in a room.");
}
}
private static int roomCounter = 1; // 방 번호를 추적하는 정적 변수
// 방을 생성하는 메서드
private void createRoom(String roomName) {
String roomId = String.valueOf(roomCounter++);
RoomThread room = new RoomThread(roomId);
rooms.put(roomId, room);
// 방 생성 디버깅 메시지 출력
System.out.println("Room created with ID: " + roomId);
// 방 스레드 실행
new Thread(room).start();
out.println("ROOM_CREATED " + roomId);
// 방 생성 후 자동으로 참가
joinRoom(roomId);
}
// 방에 참가하는 메서드
private void joinRoom(String roomId) {
RoomThread room = rooms.get(roomId);
if (room != null) {
if (room.addClient(socket, out)) {
joinedRoom = room;
out.println("JOINED " + roomId);
System.out.println("Client joined room: " + roomId);
} else {
out.println("ERROR Room is full or game in progress.");
}
} else {
out.println("ERROR Room not found.");
}
}
// 클라이언트의 MOVE 명령을 처리하는 메서드
private void handleMove(String move) {
if (joinedRoom != null) {
joinedRoom.processMove(out, move);
} else {
out.println("ERROR You are not in a room.");
}
}
}
// 방의 상태를 관리하는 클래스
static class RoomThread implements Runnable {
private String roomId; // 방 ID
private List<PrintWriter> clients = new ArrayList<>(); // 방에 연결된 클라이언트 목록
private GameLogic gameLogic; // 게임 로직 관리 객체
private String currentPlayer = "X"; // 초기 플레이어는 X로 설정하도록 수정
private int player1Score = 0;
private int player2Score = 0;
private int gameCount = 0;
public RoomThread(String roomId) {
this.roomId = roomId;
// 게임 로직 객체 초기화 시 currentPlayer 반영
initializeGameLogic();
}
// 게임 로직을 초기화하는 메서드
private void initializeGameLogic() {
if (gameCount % 2 == 0) {
currentPlayer = "O"; // 짝수 번째 게임에서는 O가 먼저 시작
} else {
currentPlayer = "X"; // 홀수 번째 게임에서는 X가 먼저 시작
}
gameLogic = new GameLogic(currentPlayer.charAt(0)); // 'X' 또는 'O'를 첫 번째 플레이어로 설정
}
// 플레이어를 전환하는 메서드
public void switchPlayer() {
currentPlayer = (currentPlayer.equals("X")) ? "O" : "X";
}
// 클라이언트를 방에 추가하는 메서드
public synchronized boolean addClient(Socket socket, PrintWriter out) {
if (clients.size() >= 2) {
System.out.println("Room ID: " + roomId + ", client size: " + clients.size() + " - Room full");
return false;
}
clients.add(out);
System.out.println("Room ID: " + roomId + ", client added, current size: " + clients.size());
out.println("JOINED " + roomId);
if (clients.size() == 2) {
broadcast("GAME_START X");
System.out.println("Room ID: " + roomId + " - Game started");
broadcast("TURN " + currentPlayer);
}
return true;
}
// 클라이언트를 방에서 제거하는 메서드
public synchronized void removeClient(PrintWriter out) {
clients.remove(out);
if (clients.isEmpty()) {
System.out.println("Room ID: " + roomId + " - All clients disconnected");
}
}
// MOVE 명령을 처리하는 메서드
public synchronized void processMove(PrintWriter out, String move) {
String[] parts = move.split(",");
int row = Integer.parseInt(parts[0]);
int col = Integer.parseInt(parts[1]);
if (!gameLogic.makeMove(row, col)) {
out.println("ERROR Invalid move.");
return;
}
char currentPlayerChar = gameLogic.getCurrentPlayer();
broadcast("MOVE " + row + "," + col + " " + currentPlayerChar);
if (gameLogic.checkWin(row, col)) {
if(currentPlayerChar == 'X'){
player1Score++;
} else{
player2Score++;
}
broadcast("RESULT " + currentPlayerChar + " wins! Score: X=" + player1Score + ", O=" + player2Score);
resetRoom();
} else if (gameLogic.isBoardFull()) {
broadcast("RESULT Draw! Score: X=" + player1Score + ", O=" + player2Score);
resetRoom();
} else {
gameLogic.switchPlayer();
currentPlayer = String.valueOf(gameLogic.getCurrentPlayer());
broadcast("TURN " + gameLogic.getCurrentPlayer());
}
}
// 방에 메시지를 전송하는 메서드
private void broadcast(String message) {
for (PrintWriter client : clients) {
client.println(message);
}
}
// 게임을 초기화하는 메서드
private void resetRoom() {
gameLogic.resetBoard();
// 게임 번호 증가 후, 플레이어 순서 변경
gameCount++;
initializeGameLogic(); // 게임 로직 초기화
broadcast("TURN " + currentPlayer);
broadcast("RESET");
}
@Override
public void run() {
System.out.println("Room " + roomId + " is running...");
}
}
}