diff --git a/.idea/copilotDiffState.xml b/.idea/copilotDiffState.xml index b5d06bc..e3a4679 100644 --- a/.idea/copilotDiffState.xml +++ b/.idea/copilotDiffState.xml @@ -20,56 +20,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -87,39 +37,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/java/es/iesquevedo/MainGUI.java b/src/main/java/es/iesquevedo/MainGUI.java index b40725d..c5de527 100644 --- a/src/main/java/es/iesquevedo/MainGUI.java +++ b/src/main/java/es/iesquevedo/MainGUI.java @@ -1,8 +1,7 @@ package es.iesquevedo; -import es.iesquevedo.config.AppConfig; -import es.iesquevedo.service.impl.MainServiceImpl; -import es.iesquevedo.ui.MainScreenController; +import es.iesquevedo.controller.LoginController; +import es.iesquevedo.service.auth.AuthServiceMock; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; @@ -18,23 +17,23 @@ public class MainGUI extends Application { @Override public void start(Stage primaryStage) { try { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/MainScreen.fxml")); + // Cargar pantalla de login + FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/Login.fxml")); Parent root = loader.load(); - MainScreenController controller = loader.getController(); - String firebaseUrl = System.getenv("FIREBASE_URL"); - var repository = AppConfig.createMainRepository(firebaseUrl); - var mainService = new MainServiceImpl(repository); - controller.setService(mainService); + // Configurar LoginController + LoginController controller = loader.getController(); + controller.setAuthService(new AuthServiceMock()); - Scene scene = new Scene(root, 700, 500); - primaryStage.setTitle("InazumaGo - Pantalla Principal"); + Scene scene = new Scene(root, 500, 400); + primaryStage.setTitle("InazumaGo - Login"); primaryStage.setScene(scene); primaryStage.show(); - LOGGER.log(Level.INFO, "Aplicación JavaFX iniciada - Pantalla principal cargada"); + LOGGER.log(Level.INFO, "Aplicación JavaFX iniciada - Pantalla de login cargada"); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Error al iniciar la aplicación", e); + e.printStackTrace(); } } diff --git a/src/main/java/es/iesquevedo/controller/GameController.java b/src/main/java/es/iesquevedo/controller/GameController.java index d403057..24b1b19 100644 --- a/src/main/java/es/iesquevedo/controller/GameController.java +++ b/src/main/java/es/iesquevedo/controller/GameController.java @@ -1,5 +1,14 @@ package es.iesquevedo.controller; +import es.iesquevedo.model.Board; +import es.iesquevedo.model.Game; +import es.iesquevedo.model.GameState; +import es.iesquevedo.model.Move; +import es.iesquevedo.model.Player; +import es.iesquevedo.service.impl.InazumaGoMoveValidator; +import es.iesquevedo.exception.InvalidMoveException; +import es.iesquevedo.exception.PlayerNotInTurnException; + import javafx.animation.AnimationTimer; import javafx.fxml.FXML; import javafx.scene.canvas.Canvas; @@ -34,30 +43,32 @@ public class GameController { private String player1Name = "Jugador 1"; private String player2Name = "Jugador 2"; - private int player1Score = 0; - private int player2Score = 0; private long player1TimeMs = 0; private long player2TimeMs = 0; - private boolean isPlayer1Turn = true; - private Stone[][] board; private AnimationTimer gameTimer; private long lastTime = 0; private boolean gameEnded = false; - - enum Stone { - EMPTY, BLACK, WHITE - } + + // Lógica real del juego + private Game game; + private InazumaGoMoveValidator moveValidator; + private boolean moveInProgress = false; @FXML public void initialize() { LOGGER.log(Level.INFO, "GameController inicializado"); - board = new Stone[BOARD_SIZE][BOARD_SIZE]; - for (int i = 0; i < BOARD_SIZE; i++) { - for (int j = 0; j < BOARD_SIZE; j++) { - board[i][j] = Stone.EMPTY; - } - } - + + // Inicializar validador de movimientos + moveValidator = new InazumaGoMoveValidator(); + + // Crear juego con dos jugadores locales + Player player1 = new Player("1", player1Name); + Player player2 = new Player("2", player2Name); + + game = new Game("Local Game", player1); + game.addPlayer(player2); + game.start(); + updatePlayerInfo(); drawBoard(); boardCanvas.setOnMouseClicked(this::onBoardClick); @@ -72,12 +83,30 @@ private void updatePlayerInfo() { } private void updateScores() { - player1ScoreLabel.setText("Puntos: " + player1Score); - player2ScoreLabel.setText("Puntos: " + player2Score); + // Calcular puntuación simple: contar piedras por color + Board board = game.getBoard(); + int blackStones = 0; + int whiteStones = 0; + + for (int r = 0; r < BOARD_SIZE; r++) { + for (int c = 0; c < BOARD_SIZE; c++) { + int cell = board.getCell(r, c); + if (cell == 1) blackStones++; + else if (cell == 2) whiteStones++; + } + } + + // Komi (ventaja blanca): 5.5 + double blackScore = blackStones; + double whiteScore = whiteStones + 5.5; + + player1ScoreLabel.setText(String.format("Puntos: %.1f", blackScore)); + player2ScoreLabel.setText(String.format("Puntos: %.1f", whiteScore)); } private void updateCurrentTurn() { - String turn = isPlayer1Turn ? player1Name : player2Name; + Player currentPlayer = game.getCurrentPlayer(); + String turn = currentPlayer != null ? currentPlayer.getName() : player1Name; currentTurnLabel.setText("Turno: " + turn); } @@ -170,22 +199,26 @@ private void drawBoard() { } } - // Dibujar piedras actuales (usa drawStone) + // Dibujar piedras del tablero real (Board) + Board board = game.getBoard(); for (int r = 0; r < BOARD_SIZE; r++) { for (int c = 0; c < BOARD_SIZE; c++) { - if (board[r][c] != Stone.EMPTY) { - drawStone(gc, r, c, board[r][c]); + int cell = board.getCell(r, c); + if (cell == 1) { // Negro + drawStone(gc, r, c, 1); + } else if (cell == 2) { // Blanco + drawStone(gc, r, c, 2); } } } } - private void drawStone(GraphicsContext gc, int row, int col, Stone stone) { + private void drawStone(GraphicsContext gc, int row, int col, int stoneColor) { double x = getIntersectionX(col); double y = getIntersectionY(row); double radius = CELL_SIZE / 2 - 3; - if (stone == Stone.BLACK) { + if (stoneColor == 1) { // Negro // sombra proyectada gc.setFill(Color.color(0, 0, 0, 0.28)); gc.fillOval(x - radius + 2, y - radius + 3, radius * 1.9, radius * 1.9); @@ -220,7 +253,7 @@ private void drawStone(GraphicsContext gc, int row, int col, Stone stone) { gc.setLineWidth(0.6); gc.strokeOval(x - radius, y - radius, radius * 2, radius * 2); - } else { + } else if (stoneColor == 2) { // Blanco // piedra blanca: sombra más suave gc.setFill(Color.color(0.06, 0.06, 0.06, 0.14)); gc.fillOval(x - radius + 1.8, y - radius + 2.6, radius * 1.95, radius * 1.95); @@ -262,8 +295,7 @@ private void drawStone(GraphicsContext gc, int row, int col, Stone stone) { @FXML private void onBoardClick(MouseEvent event) { - if (gameEnded) { - statusLabel.setText("Partida finalizada"); + if (gameEnded || moveInProgress) { return; } @@ -285,40 +317,119 @@ private boolean isValidPosition(int row, int col) { } private void placeStone(int row, int col) { - if (board[row][col] != Stone.EMPTY) { - statusLabel.setText("Posición ocupada"); + if (moveInProgress || gameEnded) { return; } - - Stone stone = isPlayer1Turn ? Stone.BLACK : Stone.WHITE; - board[row][col] = stone; - - if (isPlayer1Turn) { - player1Score++; - } else { - player2Score++; + + moveInProgress = true; + + try { + Player currentPlayer = game.getCurrentPlayer(); + + if (currentPlayer == null) { + statusLabel.setText("Error: sin jugador actual"); + moveInProgress = false; + return; + } + + // Crear movimiento con playerId + Move move = new Move(currentPlayer.getId(), row, col); + + // Validar movimiento + moveValidator.validateMove(game, move); + + // Guardar estado previo para detectar repetición + Board previousBoardState = game.getBoard().clone(); + + // Ejecutar movimiento en el tablero + Board board = game.getBoard(); + int playerColor = (game.getCurrentPlayerIndex() == 0) ? 1 : 2; // 1=Negro, 2=Blanco + board.placeStone(row, col, playerColor); + + // Capturar grupos enemigos sin libertades + int capturedCount = board.captureGroupsWithoutLiberties(); + + // Registrar movimiento + move.setCapturedCount(capturedCount); + game.getMoves().add(move); + + // Detectar repetición de posición (se ignora la reversión por ahora) + if (game.getLastBoardState() != null && board.equals(game.getLastBoardState())) { + statusLabel.setText("Movimiento rechazado: repetición de posición"); + moveInProgress = false; + return; + } + + game.setLastBoardState(previousBoardState); + + // Movimiento exitoso + statusLabel.setText("Movimiento realizado" + (capturedCount > 0 ? " (" + capturedCount + " piedras capturadas)" : "")); + + // Cambiar turno + game.nextTurn(); + game.resetConsecutivePasses(); + + updatePlayerInfo(); + drawBoard(); + moveInProgress = false; + + LOGGER.log(Level.INFO, "Piedra colocada en [" + row + "," + col + "], capturadas: " + capturedCount); + + } catch (InvalidMoveException | PlayerNotInTurnException ex) { + statusLabel.setText("Movimiento inválido: " + ex.getMessage()); + moveInProgress = false; + LOGGER.log(Level.WARNING, "Error en movimiento: " + ex.getMessage()); + } catch (Exception ex) { + statusLabel.setText("Error: " + ex.getMessage()); + moveInProgress = false; + LOGGER.log(Level.SEVERE, "Error inesperado en movimiento: " + ex.getMessage()); } - - isPlayer1Turn = !isPlayer1Turn; - updateScores(); - updateCurrentTurn(); - drawBoard(); - - statusLabel.setText("Piedra colocada"); - LOGGER.log(Level.INFO, "Piedra colocada en [" + row + "," + col + "]"); } @FXML private void onPassTurn() { - if (gameEnded) { - statusLabel.setText("Partida finalizada"); + if (gameEnded || moveInProgress) { return; } - isPlayer1Turn = !isPlayer1Turn; - updateCurrentTurn(); - statusLabel.setText((isPlayer1Turn ? player1Name : player2Name) + " pasó su turno"); - LOGGER.log(Level.INFO, "Turno pasado"); + moveInProgress = true; + + try { + Player currentPlayer = game.getCurrentPlayer(); + if (currentPlayer == null) { + statusLabel.setText("Error: sin jugador actual"); + moveInProgress = false; + return; + } + + // Crear movimiento de PASE + Move move = new Move(currentPlayer.getId(), true); // PASE + + // Validar (pasadas sin validación especial) + moveValidator.validateMove(game, move); + + // Registrar pase + game.getMoves().add(move); + game.incrementConsecutivePasses(); + + // Cambiar turno + game.nextTurn(); + + statusLabel.setText(currentPlayer.getName() + " pasó su turno. Pases consecutivos: " + game.getConsecutivePasses()); + + // Verificar doble pase = fin de partida + if (game.getConsecutivePasses() >= 2) { + endGame(); + } + + updateCurrentTurn(); + moveInProgress = false; + + LOGGER.log(Level.INFO, "Turno pasado. Pases consecutivos: " + game.getConsecutivePasses()); + } catch (Exception ex) { + statusLabel.setText("Error al pasar: " + ex.getMessage()); + moveInProgress = false; + } } @FXML @@ -330,10 +441,15 @@ private void onUndo() { @FXML private void onSurrender() { gameEnded = true; - String winner = isPlayer1Turn ? player2Name : player1Name; - String loser = isPlayer1Turn ? player1Name : player2Name; - statusLabel.setText(winner + " ganó. " + loser + " se rindió"); - LOGGER.log(Level.INFO, loser + " se rindió. Ganador: " + winner); + game.setState(GameState.FINISHED); + + Player winner = game.getPlayers().get((game.getCurrentPlayerIndex() + 1) % 2); + Player loser = game.getCurrentPlayer(); + + game.setWinnerPlayerId(winner.getId()); + + statusLabel.setText(winner.getName() + " ganó. " + loser.getName() + " se rindió"); + LOGGER.log(Level.INFO, loser.getName() + " se rindió. Ganador: " + winner.getName()); } @FXML @@ -344,6 +460,36 @@ private void onBackToMenu() { javafx.stage.Stage stage = (javafx.stage.Stage) boardCanvas.getScene().getWindow(); stage.close(); } + + private void endGame() { + gameEnded = true; + game.setState(GameState.FINISHED); + + // Calcular puntuación final + Board board = game.getBoard(); + int blackStones = 0; + int whiteStones = 0; + + for (int r = 0; r < BOARD_SIZE; r++) { + for (int c = 0; c < BOARD_SIZE; c++) { + int cell = board.getCell(r, c); + if (cell == 1) blackStones++; + else if (cell == 2) whiteStones++; + } + } + + double blackScore = blackStones; + double whiteScore = whiteStones + 5.5; + + String winner = blackScore > whiteScore ? player1Name : player2Name; + String result = String.format("Partida finalizada. %s gana %.1f - %.1f", + winner, Math.max(blackScore, whiteScore), Math.min(blackScore, whiteScore)); + + statusLabel.setText(result); + LOGGER.log(Level.INFO, result); + + stopGameTimer(); + } private void startGameTimer() { gameTimer = new AnimationTimer() { @@ -358,7 +504,7 @@ public void handle(long now) { lastTime = now; if (!gameEnded) { - if (isPlayer1Turn) { + if (game.getCurrentPlayerIndex() == 0) { player1TimeMs += elapsedNanos / 1_000_000; } else { player2TimeMs += elapsedNanos / 1_000_000; @@ -397,8 +543,7 @@ public void setPlayerNames(String player1, String player2) { } public void setInitialScores(int score1, int score2) { - this.player1Score = score1; - this.player2Score = score2; + // Puntuación inicial se calcula del Board, no se establece manualmente if (player1ScoreLabel != null) { updateScores(); } diff --git a/src/main/java/es/iesquevedo/model/Board.java b/src/main/java/es/iesquevedo/model/Board.java index 218efeb..3a33bde 100644 --- a/src/main/java/es/iesquevedo/model/Board.java +++ b/src/main/java/es/iesquevedo/model/Board.java @@ -220,4 +220,3 @@ public String toString() { return sb.toString(); } } - diff --git a/src/main/java/es/iesquevedo/model/Move.java b/src/main/java/es/iesquevedo/model/Move.java index 3f71917..014b3d7 100644 --- a/src/main/java/es/iesquevedo/model/Move.java +++ b/src/main/java/es/iesquevedo/model/Move.java @@ -74,4 +74,3 @@ public String toString() { return "Move{" + "playerId='" + playerId + '\'' + ", row=" + row + ", col=" + col + ", timestamp=" + timestamp + '}'; } } - diff --git a/src/main/java/es/iesquevedo/service/MoveValidator.java b/src/main/java/es/iesquevedo/service/MoveValidator.java index 390874f..b54c7a3 100644 --- a/src/main/java/es/iesquevedo/service/MoveValidator.java +++ b/src/main/java/es/iesquevedo/service/MoveValidator.java @@ -19,4 +19,3 @@ public interface MoveValidator { */ void validateMove(Game game, Move move) throws InvalidMoveException, PlayerNotInTurnException; } - diff --git a/src/main/java/es/iesquevedo/service/impl/InazumaGoMoveValidator.java b/src/main/java/es/iesquevedo/service/impl/InazumaGoMoveValidator.java index 0a13988..6e663b1 100644 --- a/src/main/java/es/iesquevedo/service/impl/InazumaGoMoveValidator.java +++ b/src/main/java/es/iesquevedo/service/impl/InazumaGoMoveValidator.java @@ -76,4 +76,3 @@ private void validateNotSuicide(Board board, Move move, Game game) throws Invali } } - diff --git a/src/test/java/es/iesquevedo/model/BoardTest.java b/src/test/java/es/iesquevedo/model/BoardTest.java index e7dfc3a..eff7d9e 100644 --- a/src/test/java/es/iesquevedo/model/BoardTest.java +++ b/src/test/java/es/iesquevedo/model/BoardTest.java @@ -160,4 +160,3 @@ void testBoardEquals() { assertTrue(board.equals(board2), "Tableros con mismo estado deben ser iguales"); } } - diff --git a/src/test/java/es/iesquevedo/service/impl/GameServiceImplExecuteMoveTest.java b/src/test/java/es/iesquevedo/service/impl/GameServiceImplExecuteMoveTest.java index 5c57e67..d938676 100644 --- a/src/test/java/es/iesquevedo/service/impl/GameServiceImplExecuteMoveTest.java +++ b/src/test/java/es/iesquevedo/service/impl/GameServiceImplExecuteMoveTest.java @@ -144,4 +144,3 @@ void testPassDoesNotPlaceStone() { } - diff --git a/src/test/java/es/iesquevedo/service/impl/InazumaGoMoveValidatorTest.java b/src/test/java/es/iesquevedo/service/impl/InazumaGoMoveValidatorTest.java index ca8399a..ff70974 100644 --- a/src/test/java/es/iesquevedo/service/impl/InazumaGoMoveValidatorTest.java +++ b/src/test/java/es/iesquevedo/service/impl/InazumaGoMoveValidatorTest.java @@ -109,4 +109,3 @@ void testSecondPlayerTurnAfterFirst() { assertDoesNotThrow(() -> validator.validateMove(game, move)); } } -