/** * ================================================================================ * SiPro SocketArena™ - Assignment: Networked Tic-Tac-Toe TCP Client * ================================================================================ * COURSE : Distributed Computing & Systems Programming * ASSIGNMENT : Networked Two-Player Tic-Tac-Toe Client Application * LANGUAGE : Java (SE 11+) * OBJECTIVE : Connect to the concurrent TCP game server, send the JOIN command, * parse incoming board states and turn directives, prompt the user * for grid coordinates, and transmit MOVE commands. * PROTOCOL : RFC-SIPRO-TTT (ASCII Line-Delimited TCP Protocol) * * STUDENT INSTRUCTIONS: * Complete all 8 fill-in-the-blank sections marked with: * // >>> FILL IN THE BLANK [X]: ... <<< * * COMPILATION & EXECUTION: * javac TicTacToeClient.java * java TicTacToeClient [YourName] * ================================================================================ */ import java.io.*; import java.net.*; import java.util.Scanner; public class TicTacToeClient { public static final String DEFAULT_HOST = "127.0.0.1"; public static final int PORT = 9099; /** * Renders a 9-character string as a standard 3x3 Tic-Tac-Toe grid. */ public static void renderBoard(String boardStr) { if (boardStr == null || boardStr.length() != 9) { return; } char[] b = new char[9]; for (int i = 0; i < 9; i++) { char c = boardStr.charAt(i); b[i] = (c != ' ') ? c : '.'; } System.out.println("\n 0 1 2 (Cols)"); System.out.println("0 " + b[0] + " | " + b[1] + " | " + b[2]); System.out.println(" ---+---+---"); System.out.println("1 " + b[3] + " | " + b[4] + " | " + b[5]); System.out.println(" ---+---+---"); System.out.println("2 " + b[6] + " | " + b[7] + " | " + b[8]); System.out.println(); } public static void runClient(String host, int port, String playerName) { Socket socket = null; Scanner scanner = new Scanner(System.in); try { // >>> FILL IN THE BLANK [1]: Create a TCP stream client socket <<< // socket = new Socket(); socket = null; // REPLACE THIS LINE // >>> FILL IN THE BLANK [2]: Connect client socket to the server (host, port) <<< // socket.connect(new InetSocketAddress(host, port)); // System.out.println("[CONNECTED] Established TCP connection to " + host + ":" + port); // REPLACE THIS BLOCK if (socket == null) { System.out.println("[SIMULATION] Socket not initialized. Please complete Blank 1 & 2."); return; } BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true); // >>> FILL IN THE BLANK [3]: Frame and send the initial JOIN message ("JOIN ") <<< // out.println("JOIN " + playerName); // REPLACE THIS LINE System.out.println("[REGISTERED] Sent join request as '" + playerName + "'. Waiting for matchmaking..."); String mySymbol = null; String opponentName = null; while (true) { // >>> FILL IN THE BLANK [4]: Read incoming newline-delimited line from server stream <<< // String line = in.readLine(); String line = null; // REPLACE THIS LINE if (line == null) { System.out.println("\n[DISCONNECTED] Server closed the connection."); break; } line = line.trim(); if (line.isEmpty()) continue; String[] tokens = line.split("\\s+"); String msgType = tokens[0].toUpperCase(); // >>> FILL IN THE BLANK [5]: Handle server protocol directives <<< switch (msgType) { case "WAIT": System.out.println("[MATCHMAKING] " + (line.length() > 5 ? line.substring(5) : "Waiting...")); break; case "START": // Syntax: START mySymbol = tokens[1]; opponentName = tokens.length > 2 ? tokens[2] : "Opponent"; System.out.println("\n======================================================="); System.out.println(" GAME STARTED! You are playing as: [" + mySymbol + "]"); System.out.println(" Your Opponent: " + opponentName); System.out.println("======================================================="); break; case "BOARD": // Syntax: BOARD <9-char-string> if (tokens.length > 1) { // >>> FILL IN THE BLANK [7]: Call renderBoard() with board string (tokens[1]) <<< // renderBoard(tokens[1]); } break; case "YOUR_TURN": System.out.println("👉 YOUR TURN (" + mySymbol + ")! Enter row and col coordinates (0, 1, or 2):"); boolean validInput = false; while (!validInput) { System.out.print("Enter 'row col' (or 'quit'): "); String userIn = scanner.nextLine().trim(); if (userIn.equalsIgnoreCase("quit")) { out.println("QUIT"); return; } String[] parts = userIn.split("\\s+"); if (parts.length == 2) { try { int r = Integer.parseInt(parts[0]); int c = Integer.parseInt(parts[1]); if (r >= 0 && r <= 2 && c >= 0 && c <= 2) { // >>> FILL IN THE BLANK [6]: Format and transmit MOVE command ("MOVE ") <<< // out.println("MOVE " + r + " " + c); validInput = true; } else { System.out.println("Row and column indices must be 0, 1, or 2."); } } catch (NumberFormatException e) { System.out.println("Coordinates must be numeric integers (e.g. '1 1')."); } } else { System.out.println("Invalid format. Enter two numbers separated by a space (e.g. '0 2')."); } } break; case "OPPONENT_TURN": System.out.println("⏳ Waiting for opponent (" + opponentName + ") to make a move..."); break; case "INVALID_MOVE": String reason = line.length() > 13 ? line.substring(13) : "Invalid move attempt."; System.out.println("⚠️ [INVALID MOVE]: " + reason); break; case "MATCH_OVER": // Syntax: MATCH_OVER String status = tokens.length > 1 ? tokens[1] : "COMPLETED"; StringBuilder explanation = new StringBuilder(); for (int i = 2; i < tokens.length; i++) { explanation.append(tokens[i]).append(" "); } System.out.println("\n*******************************************************"); if ("VICTORY".equals(status)) { System.out.println("🏆 VICTORY! " + explanation); } else if ("DEFEAT".equals(status)) { System.out.println("💀 DEFEAT! " + explanation); } else if ("DRAW".equals(status)) { System.out.println("🤝 DRAW! " + explanation); } else { System.out.println("ℹ️ GAME OVER: " + status + " - " + explanation); } System.out.println("*******************************************************\n"); return; case "ERROR": System.out.println("❌ [SERVER ERROR]: " + line); return; } } } catch (ConnectException e) { System.err.println("[ERROR] Could not connect to " + host + ":" + port + ". Ensure TicTacToeServer is running!"); } catch (IOException e) { System.err.println("[CLIENT EXCEPTION] " + e.getMessage()); } finally { // >>> FILL IN THE BLANK [8]: Close client socket cleanly <<< // if (socket != null && !socket.isClosed()) { // try { socket.close(); } catch (IOException ignored) {} // } System.out.println("[CLIENT] Session terminated."); } } public static void main(String[] args) { String name = (args.length > 0) ? args[0] : "Player_" + (int)(Math.random() * 1000); runClient(DEFAULT_HOST, PORT, name); } }