diff --git a/src/main/java/uta/cse3310/App.java b/src/main/java/uta/cse3310/App.java index 813867b..479a342 100644 --- a/src/main/java/uta/cse3310/App.java +++ b/src/main/java/uta/cse3310/App.java @@ -8,6 +8,10 @@ import java.util.List; import java.util.Scanner; import java.util.Vector; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + import org.java_websocket.WebSocket; import org.java_websocket.drafts.Draft; import org.java_websocket.drafts.Draft_6455; @@ -21,6 +25,7 @@ public class App extends WebSocketServer { private Vector activeGames = new Vector<>(); private int gameId = 1; private int connectionId = 0; + private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private Statistics stats; private HttpServer httpServer; @@ -49,43 +54,50 @@ public void onOpen(WebSocket conn, ClientHandshake handshake) { Game game = null; for (Game g : activeGames) { - if (g.getPlayers().size() < 4) { // Assuming a maximum of 4 players per game + if (g.getPlayers().size() < 4) { // Assuming a maximum of 4 players per game game = g; System.out.println("Found a match"); break; } } + Player newPlayer = new Player("Player" + connectionId, PlayerType.HUMAN); + if (game == null) { List players = new ArrayList<>(); - players.add(new Player("Player" + connectionId, PlayerType.HUMAN)); // Initialize at least one player + players.add(newPlayer); // Initialize at least one player + try { - game = new Game(players, "src/main/resources/words.txt", "src/main/resources/stakes.txt", new Statistics(), new Scanner(System.in)); + game = new Game(players, "src/main/resources/words.txt", "src/main/resources/stakes.txt", + new Statistics()); } catch (IOException e) { e.printStackTrace(); return; } game.setGameId(gameId++); activeGames.add(game); - + System.out.println("Creating a new Game"); } else { - game.addPlayer(new Player("Player" + connectionId, PlayerType.HUMAN)); + game.addPlayer(newPlayer); System.out.println("Joining an existing game"); } - + conn.setAttachment(game); // Attach the game to the connection - - conn.setAttachment(game); // Attach the game to the connection Gson gson = new Gson(); - String jsonString = gson.toJson(event); - conn.send(jsonString); - System.out.println("> " + jsonString); - jsonString = gson.toJson(game); - System.out.println("< " + jsonString); - broadcastToGame(game, jsonString); // Broadcast to the specific game + String playerJson = gson.toJson(newPlayer.getId()); + conn.send(playerJson); + System.out.println("> Player ID" + playerJson); + + String gameJson = gson.toJson(event); + conn.send(gameJson); + System.out.println("> " + gameJson); + + gameJson = gson.toJson(game); + // System.out.println("< " + gameJson); + broadcastToGame(game, gameJson); // Broadcast to the specific game if (game.getPlayers().size() == 2) { game.startGame(); @@ -106,21 +118,37 @@ public void onClose(WebSocket conn, int code, String reason, boolean remote) { @Override public void onMessage(WebSocket conn, String message) { - System.out.println("< " + message); Gson gson = new GsonBuilder().create(); UserEvent event = gson.fromJson(message, UserEvent.class); Game game = conn.getAttachment(); if (game != null) { + Player currentPlayer = game.getCurrentRound().getCurrentPlayer(); + + if (!event.getPlayerId().equals(currentPlayer.getId())) { + System.out.println("It's not this player's turn."); + return; // Ignore the action if it's not the current player's turn + } + + System.out.println("Received message from player ID: " + event.getPlayerId() + " with action: " + event.getAction()); + game.update(event); - String jsonString = gson.toJson(game); - System.out.println("> " + jsonString); - broadcastToGame(game, jsonString); // Broadcast to the specific game - if (game.playerActionComplete) { - game.getCurrentRound().playerActionTaken(); - game.playerActionComplete = false; + + // After processing the input, determine if the turn should continue or advance + if (!game.getCurrentRound().isRoundActive()) { + game.moveToNextRoundOrEndGame(); + } else if (!game.correctGuess){ + // Advance to the next turn + game.getCurrentRound().advanceTurn(); } + + String jsonString = gson.toJson(game); + broadcastToGame(game, jsonString); + } else { + System.out.println("No game attached to the WebSocket connection."); } + System.out.println("Finished onMessage for player ID: " + event.getPlayerId()); + } @Override diff --git a/src/main/java/uta/cse3310/Game.java b/src/main/java/uta/cse3310/Game.java index 655ed1d..0b4f466 100644 --- a/src/main/java/uta/cse3310/Game.java +++ b/src/main/java/uta/cse3310/Game.java @@ -12,19 +12,20 @@ public class Game { private static final int MAX_PLAYERS = 4; private List players; - private List rounds; + List rounds; private int currentRoundIndex; public boolean isGameActive; private Statistics stats; private int gameId; private boolean inTestingMode; public boolean playerActionComplete; - private transient Scanner scanner; // Marked as transient + //private transient Scanner scanner; // Marked as transient + boolean correctGuess; + //private TurnStartListener turnStartListener; - public Game(List players, String wordFilePath, String stakeFilePath, Statistics stats, Scanner scanner) throws IOException { + public Game(List players, String wordFilePath, String stakeFilePath, Statistics stats) throws IOException { this.players = players; this.rounds = new ArrayList<>(); - this.scanner = scanner; this.currentRoundIndex = 0; this.isGameActive = false; this.stats = stats; // Keep the passed stats @@ -33,23 +34,20 @@ public Game(List players, String wordFilePath, String stakeFilePath, Sta this.playerActionComplete = false; for (int i = 0; i < 3; i++) { - rounds.add(new Round(players, wordFilePath, stakeFilePath, scanner)); + rounds.add(new Round(players, wordFilePath, stakeFilePath)); } } - public Game(List players, String wordFilePath, String stakeFilePath, Statistics stats, WordList wordlist, Scanner scanner) throws IOException { + public Game(List players, List rounds, int currentRoundIndex, boolean isGameActive, int gameId, boolean playerActionComplete, boolean correctGuess) { this.players = players; - this.scanner = scanner; - this.rounds = new ArrayList<>(); - this.currentRoundIndex = 0; - this.isGameActive = true; - this.stats = stats; // Keep the passed stats - this.gameId = 0; - this.inTestingMode = false; - - for (int i = 0; i < 3; i++) { - rounds.add(new Round(players, wordlist, stakeFilePath, scanner));//error with this function - } + this.rounds = rounds; + this.currentRoundIndex = currentRoundIndex; + this.isGameActive = isGameActive; + this.gameId = gameId; + this.playerActionComplete = playerActionComplete; + this.correctGuess = correctGuess; + // Initialize transient fields or any other required fields as needed + //this.scanner = new Scanner(System.in); // or set it to null if it should be handled differently } public void setTestingMode(boolean inTestingMode) { @@ -60,6 +58,10 @@ public List getPlayers() { return players; } + public List getRounds() { + return rounds; + } + public void setGameId(int gameId) { this.gameId = gameId; } @@ -87,36 +89,39 @@ public void update(UserEvent event) { System.err.println("Received null event"); return; } - + String action = event.getAction(); if (action == null) { System.err.println("Event action is null"); return; } - + Round currentRound = rounds.get(currentRoundIndex); Player currentPlayer = currentRound.getCurrentPlayer(); - + + switch (action) { case "BUY_VOWEL": - currentRound.buyVowel(currentPlayer, event.getValue().charAt(0)); - playerActionComplete = true; + correctGuess = currentRound.buyVowel(currentPlayer, event.getValue().charAt(0)); break; case "SELECT_CONSONANT": - currentRound.selectConsonant(currentPlayer, event.getValue().charAt(0)); + correctGuess = currentRound.selectConsonant(currentPlayer, event.getValue().charAt(0)); + System.out.println("Consonant selected by " + currentPlayer.getName()); break; case "SOLVE_PUZZLE": - currentRound.solvePuzzle(currentPlayer, event.getValue()); + correctGuess = currentRound.solvePuzzle(currentPlayer, event.getValue()); break; default: System.out.println("Unknown action: " + action); } - //currentRound.playerActionTaken(); - - // Debugging log System.out.println("Updated game state: " + new Gson().toJson(this)); + + if (playerActionComplete) { + System.out.println("Player " + currentPlayer.getName() + " action complete."); + } } + @@ -128,11 +133,12 @@ public void startGame() { return; } System.out.println("Game started with " + players.size() + " players."); + isGameActive = true; + /* if (!inTestingMode) { for(int i =0; i < 3; i++){ System.out.println("back in game"); - isGameActive = true; startNextRound(); System.out.println("finished start next round method"); @@ -142,14 +148,24 @@ public void startGame() { System.out.println("All round complete"); } } - } - //startNextRound(); + } */ + startNextRound(); } - public void startNextRound() { + public void moveToNextRoundOrEndGame() { + if (currentRoundIndex < rounds.size() - 1) { + System.out.println("Round is over. Moving to the next round."); + currentRoundIndex++; // Move to the next round + startNextRound(); // Start the next round + } else { + System.out.println("All rounds complete. Ending the game."); + endGame(determineWinner()); // End the game and declare the winner + } + } + + public void startNextRound(){ Round currentRound = rounds.get(currentRoundIndex); currentRound.startRound(); - currentRoundIndex++; } public void playRound() { @@ -190,7 +206,7 @@ public Round getCurrentRound() { return rounds.get(currentRoundIndex); } - public void determineWinner() { + public Player determineWinner() { Player winner = null; int highestScore = 0; for (Player player : players) { @@ -207,6 +223,8 @@ public void determineWinner() { } else { System.out.println("No winner determined."); } + + return winner; } public void resetGame() { diff --git a/src/main/java/uta/cse3310/GameTimer.java b/src/main/java/uta/cse3310/GameTimer.java index 94c2f77..27e74b1 100644 --- a/src/main/java/uta/cse3310/GameTimer.java +++ b/src/main/java/uta/cse3310/GameTimer.java @@ -4,14 +4,15 @@ import java.util.TimerTask; public class GameTimer { - private transient Timer timer; + private transient Timer timer; private long startTime; private long elapsedTime; private boolean isRunning; - //private TimerTask timerTask; + private final long timeLimit; - public GameTimer() { + public GameTimer(long timeLimit) { this.timer = new Timer(); + this.timeLimit = timeLimit; // Time limit in milliseconds this.elapsedTime = 0; this.isRunning = false; } @@ -22,15 +23,18 @@ public void start() { } this.startTime = System.currentTimeMillis(); this.isRunning = true; - - // Create a new TimerTask instance each time the timer is started + TimerTask timerTask = new TimerTask() { @Override public void run() { elapsedTime = System.currentTimeMillis() - startTime; + if (getRemainingTime() <= 0) { + stop(); + // Handle timer expiration (e.g., advance the turn) + } } }; - + timer.scheduleAtFixedRate(timerTask, 0, 1000); // Update elapsed time every second } @@ -51,7 +55,11 @@ public void reset() { } public long getElapsedTime() { - return elapsedTime / 1000; // Return elapsed time in seconds + return elapsedTime; // Elapsed time in milliseconds + } + + public long getRemainingTime() { + return timeLimit - getElapsedTime(); // Remaining time in milliseconds } public boolean isRunning() { diff --git a/src/main/java/uta/cse3310/Player.java b/src/main/java/uta/cse3310/Player.java index 68d1601..2ecc74e 100644 --- a/src/main/java/uta/cse3310/Player.java +++ b/src/main/java/uta/cse3310/Player.java @@ -14,11 +14,13 @@ public class Player { private List guessedConsonants; private PlayerType playerType; + private static final int TURN_TIME_LIMIT = 1000000; + public Player(String name, PlayerType playerType) { this.id = UUID.randomUUID().toString(); this.name = name; this.score = 1000; - this.timer = new GameTimer(); + this.timer = new GameTimer(TURN_TIME_LIMIT); this.boughtVowels = new ArrayList<>(); this.guessedConsonants = new ArrayList<>(); this.playerType = playerType; @@ -92,9 +94,3 @@ public void setPlayerType(PlayerType playerType) { this.playerType = playerType; } } - - - - - - diff --git a/src/main/java/uta/cse3310/Round.java b/src/main/java/uta/cse3310/Round.java index e24d21c..c16444f 100644 --- a/src/main/java/uta/cse3310/Round.java +++ b/src/main/java/uta/cse3310/Round.java @@ -4,9 +4,6 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Scanner; -import java.util.Timer; -import java.util.TimerTask; public class Round { private Word word; @@ -21,16 +18,12 @@ public class Round { HashSet lettersguessed = new HashSet<>(); HashSet correctguesses = new HashSet<>(); private HashSet lettersinword; - private transient Scanner scanner; public boolean waitingForInput = false; - private final Object turnLock = new Object(); - public final Object broadcastLock = new Object(); - + private Player currentPlayer; @SuppressWarnings("static-access") - public Round(List players, String wordFilePath, String stakeFilePath, Scanner scanner) throws IOException { + public Round(List players, String wordFilePath, String stakeFilePath) throws IOException { this.players = players; - this.scanner = scanner; wordlist.gatherwords(); this.wordsforgame = new ArrayList<>(wordlist.getArrList()); System.out.println("words chosen" + wordsforgame); @@ -38,10 +31,10 @@ public Round(List players, String wordFilePath, String stakeFilePath, Sc this.word = new Word(wordsforgame); this.stake = new Stake(stakeFilePath); this.currentPlayerIndex = 0; - this.isRoundActive = true; + this.isRoundActive = false; } - @SuppressWarnings("static-access") + /*@SuppressWarnings("static-access") public Round(List players, WordList wordlist, String stakeFilePath, Scanner scanner) throws IOException { this.players = players; this.wordlist = wordlist; @@ -52,13 +45,16 @@ public Round(List players, WordList wordlist, String stakeFilePath, Scan this.word = new Word(wordsforgame); this.stake = new Stake(stakeFilePath); this.currentPlayerIndex = 0; - this.isRoundActive = true; - } + this.isRoundActive = false; + }*/ public void startRound() { - this.isRoundActive = true; - nextTurn(); - System.out.println("Returning after nextTurn"); + isRoundActive = true; + //while (isRoundActive) { + nextTurn(); + //System.out.println("finished nextTurn method"); + //} + //System.out.println("Round is over"); } public void nextTurn() { @@ -66,116 +62,105 @@ public void nextTurn() { System.out.println("Round is not active."); return; } + + currentPlayer = getCurrentPlayer(); + System.out.println("Current player: " + currentPlayer.getName() + " has " + TURN_TIME_LIMIT + " seconds to guess."); - Player currentPlayer = players.get(currentPlayerIndex); + // Start the player's timer currentPlayer.getTimer().reset(); currentPlayer.getTimer().start(); - System.out.println("Current player: " + currentPlayer.getName() + " has " + TURN_TIME_LIMIT + " seconds to guess."); - waitingForInput = true; - - synchronized (turnLock) { - new Timer().schedule(new TimerTask() { - @Override - public void run() { - synchronized (turnLock) { - if (currentPlayer.getTimer().getElapsedTime() >= TURN_TIME_LIMIT || !waitingForInput) { - System.out.println("Time is up for player " + currentPlayer.getName()); - currentPlayer.getTimer().stop(); - waitingForInput = false; - turnLock.notifyAll(); - System.out.println("Reached inside loop"); - advanceTurn(); - } - } - } - }, TURN_TIME_LIMIT * 1000); - - while (waitingForInput) { - try { - turnLock.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - System.out.println("Thread interrupted: " + e.getMessage()); - } - } - } + // The game flow will now wait for input, which will be handled in the onMessage method } - public void playerActionTaken() { - synchronized (turnLock) { - waitingForInput = false; - turnLock.notifyAll(); - } - } - - public void buyVowel(Player currentPlayer, char vowel) { - if ("aeiou".indexOf(vowel) != -1 && currentPlayer.getScore() >= VOWEL_COST && !currentPlayer.hasBoughtVowel(vowel)) { - currentPlayer.deductScore(VOWEL_COST); - currentPlayer.buyVowel(vowel); - processGuess(currentPlayer, vowel); - //playerActionTaken(); // Notify the waiting thread + public boolean buyVowel(Player player, char vowel) { + boolean isCorrect; + if ("aeiou".indexOf(vowel) != -1 && player.getScore() >= VOWEL_COST && !player.hasBoughtVowel(vowel)) { + player.deductScore(VOWEL_COST); + player.buyVowel(vowel); + isCorrect = processGuess(player, vowel); + return isCorrect; } else { System.out.println("Invalid vowel or not enough points or already bought. Try again."); + return false; } } - public void selectConsonant(Player currentPlayer, char consonant) { - if ("aeiou".indexOf(consonant) == -1 && !currentPlayer.hasGuessedConsonant(consonant)) { - currentPlayer.guessConsonant(consonant); - processGuess(currentPlayer, consonant); - playerActionTaken(); // Notify the waiting thread + public boolean selectConsonant(Player player, char consonant) { + boolean isCorrect; + if ("aeiou".indexOf(consonant) == -1 && !player.hasGuessedConsonant(consonant)) { + player.guessConsonant(consonant); + isCorrect = processGuess(player, consonant); + System.out.println("guess processed"); + return isCorrect; } else { System.out.println("Invalid consonant or already guessed. Try again."); + return false; } } - public void solvePuzzle(Player currentPlayer, String solution) { - String solutions = solution.replace(" ", ""); - if (word.solve(solutions)) { - System.out.println(currentPlayer.getName() + " solved the puzzle!"); - currentPlayer.addScore(10); + public boolean solvePuzzle(Player player, String solution) { + String string = solution.replace(" ", ""); + + if (word.solve(string)) { + System.out.println(player.getName() + " solved the puzzle!"); + player.addScore(10); correctguesses.addAll(lettersinword); // Ensure all letters are marked as correct isRoundActive = false; - playerActionTaken(); // Notify the waiting thread - System.out.println("Going to next round"); + System.out.println("Moving to next round."); + return true; } else { - System.out.println("Incorrect solution by player " + currentPlayer.getName()); - advanceTurn(); + System.out.println("Incorrect solution by player " + player.getName()); + player.getTimer().stop(); + return false; } } @SuppressWarnings("static-access") - private void processGuess(Player currentPlayer, char guessedLetter) { + private boolean processGuess(Player player, char guessedLetter) { int isCorrect = word.iscorrect(guessedLetter, lettersinword); - lettersguessed.add(guessedLetter); // Add guessed letter to the list of guessed letters + lettersguessed.add(guessedLetter); + + // if the guess is correct add points to player, add guessed letter, current player doesnt change (they get another turn) if (isCorrect == 1) { System.out.println("Correct guess!"); int points = stake.calculatePoints(guessedLetter); - currentPlayer.addScore(points); + player.addScore(points); correctguesses.add(guessedLetter); - System.out.println("Player " + currentPlayer.getName() + " awarded " + points + " points."); + System.out.println("Player " + player.getName() + " awarded " + points + " points."); System.out.println("\nCorrect guesses: " + correctguesses); + + //puzzle is solved if (correctguesses.equals(lettersinword)) { System.out.println("Word guessed correctly! Round over."); + player.getTimer().stop(); //stop timer isRoundActive = false; - //playerActionTaken(); // Notify the waiting thread - } else { - currentPlayer.getTimer().reset(); - //nextTurn(); - } - } else { + return true; + } + + //reset the player's timer and allow them to guess again + player.getTimer().reset(); + player.getTimer().start(); + System.out.println("Player " + player.getName() + " continues with a new timer."); + return true; + + } else { //incorrect guess System.out.println("Incorrect guess."); - advanceTurn(); + player.getTimer().stop(); + return false; } } public void advanceTurn() { - Player currentPlayer = players.get(currentPlayerIndex); + //Stop the timer for the current player + Player currentPlayer = getCurrentPlayer(); currentPlayer.getTimer().stop(); + + //Move to next player currentPlayerIndex = (currentPlayerIndex + 1) % players.size(); - if (isRoundActive) { - nextTurn(); - } + + //start the next player's turn + nextTurn(); + } public void resetRound() throws IOException { @@ -203,4 +188,4 @@ public HashSet getCorrectGuesses() { public HashSet getLettersGuessed() { return lettersguessed; } -} +} \ No newline at end of file