package Client; import javax.sound.sampled.*; import java.io.*; import java.net.Socket; import java.net.URL; import java.util.*; import Client.utils.AnsiColor; public class Client { private static final String SPACING = " "; private static final String IP_ADDRESS = "127.0.0.1"; private static final int PORT_NUMBER = 3737; static final int SIZE = 10; static final Random rand = new Random(); private final Socket clientSocket; BattleShipCell[][] playerBoard; BattleShipCell[][] enemyBoard; static int lastEnemyRow; static int lastEnemyCol; volatile boolean isBoardSet = false; boolean isTurn = false; volatile boolean otherPlayerJoined = false; DataOutputStream out; public Client(Socket clientSocket) { this.clientSocket = clientSocket; try { //TODO: initialize out this.out = new DataOutputStream(clientSocket.getOutputStream()); //TODO: create a ClientNetworkReceiver //TODO: start it in another thread System.out.println("Enter Username: "); Scanner usernameScanner = new Scanner(System.in); String username = usernameScanner.next(); sendUsername(username); initializeBoards(); System.out.println("Waiting for the other player to join"); while(!otherPlayerJoined) { Thread.onSpinWait(); } printBoards(); while(true) { while(!isTurn) { Thread.onSpinWait(); } System.out.println("Your Turn Now!"); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); Scanner scanner = new Scanner(System.in); lastEnemyRow = -1; lastEnemyCol = -1; while (lastEnemyRow < 0 || lastEnemyRow >= 10 || lastEnemyCol < 0 || lastEnemyCol >= 10) { System.out.println("Type Row and Column (e.g., A1):"); String input = scanner.nextLine().trim().toUpperCase(); if (input.matches("^[A-J]([1-9]|10)$")) { lastEnemyCol = input.charAt(0) - 'A'; lastEnemyRow = Integer.parseInt(input.substring(1)) - 1; if (lastEnemyRow >= 0 && lastEnemyRow < 10 && lastEnemyCol >= 0 && lastEnemyCol < 10) { System.out.println("You selected Row: " + (lastEnemyRow + 1) + ", Column: " + (char)('A' + lastEnemyCol)); } else { System.out.println("Invalid input! Row must be between 1 and 10, and Column must be between A and J."); lastEnemyRow = -1; lastEnemyCol = -1; } } else { System.out.println("Invalid format! Please enter a valid input like A1, B10, J5."); } } System.out.println("Move is being sent. Waiting for processing"); attack(); } } catch (IOException e) { System.out.println("Could not connect to the server!\n\n"); } } //Actions private void attack() { //TODO: send attack message to server isTurn = false; } private void sendUsername(String username) throws IOException { //TODO: send username to server } public void setEnemyResult(boolean hit, String shipName) { enemyBoard[lastEnemyRow][lastEnemyCol].setEnemyShip(hit); enemyBoard[lastEnemyRow][lastEnemyCol].setMarked(true); printBoards(); playSound((hit?"hit_sound.wav":"miss_sound.wav")); System.out.println((hit?"You hit the target!":"You missed!")); } public void setPlayerNumber(int i) { if(i == 0) isTurn = true; } public void setOtherPlayerJoined() { otherPlayerJoined = true; } public void finishGame(boolean won){ //TODO: } public String getHit(int row, int col){ String message = ""; playerBoard[row][col].setMarked(true); String sankBattleShipName = "NONE"; if(playerBoard[row][col].isShip() && playerBoard[row][col].getBattleShip().isAllHit()){ sankBattleShipName = playerBoard[row][col].getBattleShip().getType(); } message = playerBoard[row][col].isShip() ? ("HIT," + sankBattleShipName) : "MISS,NONE"; printBoards(); boolean hit = playerBoard[row][col].isShip(); System.out.println((hit?"They hit a target!":"They missed!")); try { Thread.sleep(1000); } catch (InterruptedException e) { } isTurn = true; return message; } private static void playSound(String soundFileName) { try { // Load the sound file from the resources folder URL soundFileURL = Client.class.getClassLoader().getResource( soundFileName); if (soundFileURL == null) { System.out.println("Sound file not found: " + soundFileName); return; } AudioInputStream audioStream = AudioSystem.getAudioInputStream(soundFileURL); Clip clip = AudioSystem.getClip(); clip.open(audioStream); clip.start(); } catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) { System.out.println("Error playing sound: " + e.getMessage()); } } //Board public void initializeBoards() { playerBoard = createBoard(); enemyBoard = new BattleShipCell[SIZE][SIZE]; for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++){ enemyBoard[i][j] = new BattleShipCell(); } } isBoardSet = true; } public void printBoards() { clearScreen(); System.out.printf(" %-40s%s%-40s%n", "PLAYER BOARD", SPACING ," ENEMY BOARD"); printColumnHeaders(); System.out.print(SPACING); printColumnHeaders(); System.out.println(); for (int row = 0; row < SIZE; row++) { printRowSeparator(); System.out.print(SPACING); printRowSeparator(); System.out.println(); printRowContent(playerBoard[row], row); System.out.print(SPACING); printRowContent(enemyBoard[row], row); System.out.println(); } printRowSeparator(); System.out.print(SPACING); printRowSeparator(); System.out.println(); } private void clearScreen() { for (int i = 0; i < 20; i++) { System.out.println(); } } private void printColumnHeaders() { System.out.print(" "); for (char c : "ABCDEFGHIJ".toCharArray()) { System.out.print(AnsiColor.CYAN + " " + c + " " + AnsiColor.RESET); } } private void printRowSeparator() { System.out.print(AnsiColor.BORDER + " +" + AnsiColor.RESET); for (int i = 0; i < SIZE; i++) { System.out.print(AnsiColor.BORDER + "---+" + AnsiColor.RESET); } } private void printRowContent(BattleShipCell[] rowData, int rowNum) { System.out.printf(AnsiColor.BORDER +"%2d |" + AnsiColor.RESET, rowNum + 1); for (BattleShipCell cell : rowData) { System.out.printf( " %s " , cell); System.out.print(AnsiColor.BORDER +"|" + AnsiColor.RESET); } } public BattleShipCell[][] createBoard() { BattleShipCell[][] board = new BattleShipCell[SIZE][SIZE]; // Initialize grids with water for (int i = 0; i < SIZE; i++) { for (int j = 0; j < SIZE; j++) { board[i][j] = new BattleShipCell(); // water } } for (Map.Entry entry : BattleShip.ships.entrySet()) { BattleShip battleShip = new BattleShip(entry.getKey()); placeShip(board, battleShip); } return board; } public static void placeShip(BattleShipCell[][] board, BattleShip battleShip) { boolean placed = false; while(!placed) { boolean horizontal = rand.nextBoolean(); int row = rand.nextInt(SIZE); int col = rand.nextInt(SIZE); placed = tryPlace(board, row, col, horizontal, battleShip); } } private static boolean tryPlace(BattleShipCell[][] board, int row, int col, boolean horizontal, BattleShip battleShip) { int length = battleShip.getLength(); if(horizontal && col + length <= SIZE) { for(int i = 0; i < length; i++) { if(board[row][col + i].isShip()) { return false; } } for(int i = 0; i < length; i++) { board[row][col + i].setBattleShip(battleShip); } return true; } if(!horizontal && row + length <= SIZE) { for(int i = 0; i < length; i++) { if(board[row + i][col].isShip()) { return false; } } for(int i = 0; i < length; i++) { board[row + i][col].setBattleShip(battleShip); battleShip.addShipCell(board[row + i][col]); } return true; } return false; } public static void main(String[] args) throws IOException { Socket clientSocket = null; //TODO: get IP_Address try { //initialize clientSocket //initialize Client } finally { try { if (clientSocket != null) { clientSocket.close(); } } catch (IOException e) { System.out.println("Error closing resources: " + e.getMessage()); } } } }