diff --git a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java index 2c3ad49..70b256a 100644 --- a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java +++ b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java @@ -55,7 +55,7 @@ public class GenericGameController> implements GameCo // Listen to updates eventFlow .listen(GUIEvents.GameEnded.class, this::onGameFinish, false) - .listen(GUIEvents.PlayerAttemptedMove.class, event -> {if (getCurrentPlayer() instanceof LocalPlayer lp){lp.setMove(event.move());}}, false); + .listen(GUIEvents.PlayerAttemptedMove.class, event -> {if (getCurrentPlayer() instanceof LocalPlayer lp){lp.setLastMove(event.move());}}, false); } public void start(){ diff --git a/game/src/main/java/org/toop/game/players/ArtificialPlayer.java b/game/src/main/java/org/toop/game/players/ArtificialPlayer.java index d141503..c3df033 100644 --- a/game/src/main/java/org/toop/game/players/ArtificialPlayer.java +++ b/game/src/main/java/org/toop/game/players/ArtificialPlayer.java @@ -4,52 +4,52 @@ import org.toop.framework.gameFramework.model.player.*; import org.toop.framework.gameFramework.model.game.TurnBasedGame; /** - * Represents a player controlled by an AI in a game. - *

- * This player uses an {@link AbstractAI} instance to determine its moves. The generic - * parameter {@code T} specifies the type of {@link GameR} the AI can handle. - *

+ * Represents a player controlled by an AI. * - * @param the specific type of game this AI player can play + * @param the type of turn-based game */ public class ArtificialPlayer> extends AbstractPlayer { - /** The AI instance used to calculate moves. */ private final AI ai; /** - * Constructs a new ArtificialPlayer using the specified AI. + * Creates a new AI-controlled player. * - * @param ai the AI instance that determines moves for this player + * @param ai the AI controlling this player + * @param name the player's name */ public ArtificialPlayer(AI ai, String name) { super(name); this.ai = ai; } + /** + * Creates a copy of another AI-controlled player. + * + * @param other the player to copy + */ public ArtificialPlayer(ArtificialPlayer other) { super(other); this.ai = other.ai.deepCopy(); } /** - * Determines the next move for this player using its AI. - *

- * This method overrides {@link AbstractPlayer#getMove(GameR)}. Because the AI is - * typed to {@code T}, a runtime cast is required. It is the caller's - * responsibility to ensure that {@code gameCopy} is of type {@code T}. - *

+ * Determines the player's move using the AI. * - * @param gameCopy a copy of the current game state - * @return the integer representing the chosen move - * @throws ClassCastException if {@code gameCopy} is not of type {@code T} + * @param gameCopy a copy of the current game + * @return the move chosen by the AI */ protected long determineMove(T gameCopy) { return ai.getMove(gameCopy); } + /** + * Creates a deep copy of this AI player. + * + * @return a copy of this player + */ @Override public ArtificialPlayer deepCopy() { - return new ArtificialPlayer(this); + return new ArtificialPlayer<>(this); } } diff --git a/game/src/main/java/org/toop/game/players/LocalPlayer.java b/game/src/main/java/org/toop/game/players/LocalPlayer.java index f5c2daa..4ae135b 100644 --- a/game/src/main/java/org/toop/game/players/LocalPlayer.java +++ b/game/src/main/java/org/toop/game/players/LocalPlayer.java @@ -2,85 +2,86 @@ package org.toop.game.players; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.player.AbstractPlayer; -import org.toop.framework.gameFramework.model.player.Player; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; +/** + * Represents a local player who provides moves manually. + * + * @param the type of turn-based game + */ public class LocalPlayer> extends AbstractPlayer { - // Future can be used with event system, IF unsubscribeAfterSuccess works... - // private CompletableFuture LastMove = new CompletableFuture<>(); - private CompletableFuture LastMove; + private CompletableFuture LastMove = new CompletableFuture<>(); + /** + * Creates a new local player with the given name. + * + * @param name the player's name + */ public LocalPlayer(String name) { super(name); } + /** + * Creates a copy of another local player. + * + * @param other the player to copy + */ public LocalPlayer(LocalPlayer other) { super(other); + this.LastMove = other.LastMove; } + /** + * Waits for and returns the player's next legal move. + * + * @param gameCopy a copy of the current game + * @return the chosen move + */ @Override protected long determineMove(T gameCopy) { - return getValidMove(gameCopy); + long legalMoves = gameCopy.getLegalMoves(); + long move; + + do { + move = getLastMove(); + } while ((legalMoves & move) == 0); + + return move; } - public void setMove(long move) { + /** + * Sets the player's last move. + * + * @param move the move to set + */ + public void setLastMove(long move) { LastMove.complete(move); } - // TODO: helper function, would like to replace to get rid of this method - public static boolean contains(int[] array, int value){ - for (int i : array) if (i == value) return true; - return false; - } - - private long getMove2(T gameCopy) { - LastMove = new CompletableFuture<>(); - long move = 0; + /** + * Waits for the next move from the player. + * + * @return the chosen move or 0 if interrupted + */ + private long getLastMove() { + LastMove = new CompletableFuture<>(); // Reset the future try { - move = LastMove.get(); - System.out.println(Long.toBinaryString(move)); - } catch (InterruptedException | ExecutionException e) { - // TODO: Add proper logging. - e.printStackTrace(); + return LastMove.get(); + } catch (ExecutionException | InterruptedException e) { + return 0; } - return move; - } - - protected long getValidMove(T gameCopy){ - // Get this player's valid moves - long validMoves = gameCopy.getLegalMoves(); - // Make sure provided move is valid - // TODO: Limit amount of retries? - // TODO: Stop copying game so many times - long move = getMove2(gameCopy.deepCopy()); - while ((validMoves & move) == 0) { - System.out.println("Not a valid move, try again"); - move = getMove2(gameCopy.deepCopy()); - } - return move; } + /** + * Creates a deep copy of this local player. + * + * @return a copy of this player + */ @Override public LocalPlayer deepCopy() { - return new LocalPlayer(this.getName()); + return new LocalPlayer<>(this); } - - /*public void register() { - // Listening to PlayerAttemptedMove - new EventFlow().listen(GUIEvents.PlayerAttemptedMove.class, event -> { - if (!LastMove.isDone()) { - LastMove.complete(event.move()); // complete the future - } - }, true); // auto-unsubscribe - } - - // This blocks until the next move arrives - public int take() throws ExecutionException, InterruptedException { - int move = LastMove.get(); // blocking - LastMove = new CompletableFuture<>(); // reset for next move - return move; - }*/ } diff --git a/game/src/main/java/org/toop/game/players/OnlinePlayer.java b/game/src/main/java/org/toop/game/players/OnlinePlayer.java index 9f011c0..fe6b19d 100644 --- a/game/src/main/java/org/toop/game/players/OnlinePlayer.java +++ b/game/src/main/java/org/toop/game/players/OnlinePlayer.java @@ -5,30 +5,45 @@ import org.toop.framework.gameFramework.model.player.AbstractPlayer; import org.toop.framework.gameFramework.model.player.Player; /** - * Represents a player controlled remotely or over a network. - *

- * This class extends {@link AbstractPlayer} and can be used to implement game logic - * where moves are provided by an external source (e.g., another user or a server). - * Currently, this class is a placeholder and does not implement move logic. - *

+ * Represents a player that participates online. + * + * @param the type of turn-based game */ public class OnlinePlayer> extends AbstractPlayer { /** - * Constructs a new OnlinePlayer. - *

- * Currently, no additional initialization is performed. Subclasses or - * future implementations should provide mechanisms to receive moves from - * an external source. + * Creates a new online player with the given name. + * + * @param name the name of the player */ public OnlinePlayer(String name) { super(name); } + /** + * Creates a copy of another online player. + * + * @param other the player to copy + */ public OnlinePlayer(OnlinePlayer other) { super(other); } + /** + * {@inheritDoc} + *

+ * This method is not supported for online players. + * + * @throws UnsupportedOperationException always + */ + @Override + protected long determineMove(T gameCopy) { + throw new UnsupportedOperationException("An online player does not support determining move"); + } + + /** + * {@inheritDoc} + */ @Override public Player deepCopy() { return new OnlinePlayer<>(this);