diff --git a/players.dat b/players.dat index 0d497d6..e34c7e7 100644 Binary files a/players.dat and b/players.dat differ diff --git a/src/main/java/co/ppg2/Main.java b/src/main/java/co/ppg2/Main.java index 6b42745..5cf453d 100644 --- a/src/main/java/co/ppg2/Main.java +++ b/src/main/java/co/ppg2/Main.java @@ -61,6 +61,12 @@ public void start(Stage primaryStage) { // Start the timer for Player X gameTimer.startTimer(playerX.getUsername()); + + // add an exit for the application + primaryStage.setOnCloseRequest(event -> { + System.out.println("Game is closing..."); + System.exit(0); + }); } diff --git a/src/main/java/co/ppg2/controllers/GameController.java b/src/main/java/co/ppg2/controllers/GameController.java index 751a4c9..c4c73c7 100644 --- a/src/main/java/co/ppg2/controllers/GameController.java +++ b/src/main/java/co/ppg2/controllers/GameController.java @@ -9,13 +9,21 @@ public class GameController { + public GameController(String usernameX, String usernameO) { + if (usernameX == null || usernameX.trim().isEmpty() || usernameO == null || usernameO.trim().isEmpty()) { + throw new IllegalArgumentException("Both players must enter a valid username."); + } //makes sure the player must enter a username to proceed + this.playerX = new Player(usernameX); + this.playerO = new Player(usernameO); + + } private char whoseTurn = 'X'; private final CellBase[][] cells = new CellBase[3][3]; private final Player playerX; private final Player playerO; private GameTimer gameTimer; private GameView gameView; - private final ArrayList players; + private ArrayList players = null; public GameController(Player playerX, Player playerO) { @@ -35,6 +43,9 @@ public Player getCurrentPlayer() { } + /** + * Switches the turn to the other player and starts their timer + */ //javadoc comment example public void switchTurn() { if (gameTimer != null) { gameTimer.cancelTimer(); // Stop the timer for the current player @@ -105,21 +116,18 @@ public void setGameView(GameView gameView) { - public GameView getGameView() { return gameView; } - public Player getWinner(char token) { return (token == 'X') ? playerX : playerO; } - public void updateLeaderboard(char token) { Player winner = getWinner(token); Player loser = (token == 'X') ? playerO : playerX; @@ -137,12 +145,9 @@ public void updateLeaderboard(char token) { - PlayerDataController.savePlayers(players); - - // Show leaderboard with average time per move StringBuilder leaderboardDetails = new StringBuilder(); for (Player player : players) { @@ -152,10 +157,14 @@ public void updateLeaderboard(char token) { } - - LeaderboardPopup.showLeaderboard(players); } + //TODO: could make a basic utility method to check if a cell in the 2D array is empty + + public boolean isCellEmpty(int row, int col) { + return cells[row][col] == null || cells[row][col].getToken() == ' '; + } +//TODO: suggestion: remove unnecessary blank lines to improve readability } diff --git a/src/main/java/co/ppg2/controllers/PlayerDataController.java b/src/main/java/co/ppg2/controllers/PlayerDataController.java index a30123a..ae770aa 100644 --- a/src/main/java/co/ppg2/controllers/PlayerDataController.java +++ b/src/main/java/co/ppg2/controllers/PlayerDataController.java @@ -20,10 +20,16 @@ public static void savePlayers(ArrayList players) { public static ArrayList loadPlayers() { ArrayList players = new ArrayList<>(); - try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_NAME))) { - players = (ArrayList) ois.readObject(); - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); + File file = new File(FILE_NAME); + //TODO: consider adding conditional to check if file exists + if (file.exists()) { + try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_NAME))) { + players = (ArrayList) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + e.printStackTrace(); + } + } else { + System.out.println("Player file does not exist. Starting with an empty player list."); } return players; } diff --git a/src/main/java/co/ppg2/model/Player.java b/src/main/java/co/ppg2/model/Player.java index f40d126..e37fd65 100644 --- a/src/main/java/co/ppg2/model/Player.java +++ b/src/main/java/co/ppg2/model/Player.java @@ -3,11 +3,12 @@ import java.io.Serializable; public class Player implements Serializable { - private final String username; + private String username; private int wins; private int losses; public Player(String username) { + setUsername(username); this.username = username; this.wins = 0; this.losses = 0; @@ -17,6 +18,14 @@ public String getUsername() { return username; } + public void setUsername(String username) { + //added to make sure username is not empty + if (username == null || username.trim().isEmpty()) { + throw new IllegalArgumentException("Username can't be empty. Please enter a valid username."); + } + this.username = username; + } + public int getWins() { return wins; } diff --git a/src/main/java/co/ppg2/services/GameTimer.java b/src/main/java/co/ppg2/services/GameTimer.java index ab05982..ee5b256 100644 --- a/src/main/java/co/ppg2/services/GameTimer.java +++ b/src/main/java/co/ppg2/services/GameTimer.java @@ -41,6 +41,7 @@ public synchronized double getAverageTimePerMove(String playerName) { int moves = playerMoves.getOrDefault(playerName, 0); return moves == 0 ? 0.0 : (totalTime / (double) moves) / 1000.0; // Convert to seconds } + //TODO: Could add a pause button to pause the timer and resume once the button is pressed again. Would need to add the button in GameView @Override public void run() { @@ -50,6 +51,9 @@ public void run() { Platform.runLater(() -> { // Update GUI components if needed in future }); + + // simple add to log loop status + System.out.println("Timer is running..."); // log message in each iteration } catch (InterruptedException e) { Thread.currentThread().interrupt(); } diff --git a/src/main/java/co/ppg2/views/GameView.java b/src/main/java/co/ppg2/views/GameView.java index e15b1f4..af30108 100644 --- a/src/main/java/co/ppg2/views/GameView.java +++ b/src/main/java/co/ppg2/views/GameView.java @@ -5,6 +5,8 @@ import co.ppg2.controllers.PlayerDataController; import co.ppg2.model.Player; import javafx.scene.Scene; +import javafx.scene.control.Alert; // Importing Alert class +import javafx.scene.control.Alert.AlertType; // Importing AlertType class import javafx.scene.layout.BorderPane; import javafx.scene.layout.GridPane; import javafx.stage.Stage; @@ -19,10 +21,17 @@ public GameView(GameController gameController, Stage primaryStage) { this.primaryStage = primaryStage; } public void launchGame() { + // Validate player usernames before launching the game + if (gameController.getCurrentPlayer().getUsername().isEmpty() || + gameController.getWinner('O').getUsername().isEmpty()) { + showErrorPopup("Both players must enter a username to start the game."); + return; // Stop further execution if validation fails; popup to make sure plyer enters a name + } GridPane gridPane = new GridPane(); gridPane.setGridLinesVisible(true); + for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { CellEmpty cell = new CellEmpty(gameController, this, i, j); @@ -40,18 +49,26 @@ public void launchGame() { borderPane.setBottom(labelInstructions); - Scene scene = new Scene(borderPane, 450, 170); + Scene scene = new Scene(borderPane, 800, 600); //changed dimensions of window primaryStage.setTitle("TicTacToe"); primaryStage.setScene(scene); primaryStage.show(); } + // Method to show the error popup + private void showErrorPopup(String message) { + Alert alert = new Alert(AlertType.ERROR); + alert.setTitle("Validation Error"); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } public void updateLabel(String text) { labelInstructions.setText(text); } - +//TODO: Possibly make it so this message pops up in the middle of the screen or is larger public void handleTie() { updateLabel("It is a tie!"); LeaderboardPopup.showLeaderboard(PlayerDataController.loadPlayers()); @@ -68,3 +85,4 @@ public void handleGameOver(char token) { } +