From 14115fab5ae0db1533ca47879f7fe79edca7fa28 Mon Sep 17 00:00:00 2001 From: SophiaForeroedu Date: Tue, 12 May 2026 17:20:40 +0200 Subject: [PATCH 1/7] mejorar tablero y validar email en login --- TEST_GAME.md | 6 +- pom.xml | 10 +- .../iesquevedo/controller/GameController.java | 269 ++++++++++++++---- .../controller/LoginController.java | 10 + .../java/es/iesquevedo/util/EmailUtils.java | 18 ++ src/main/resources/fxml/Game.fxml | 13 +- .../LoginControllerEmailValidationTest.java | 27 ++ 7 files changed, 286 insertions(+), 67 deletions(-) create mode 100644 src/main/java/es/iesquevedo/util/EmailUtils.java create mode 100644 src/test/java/es/iesquevedo/controller/LoginControllerEmailValidationTest.java diff --git a/TEST_GAME.md b/TEST_GAME.md index 7ed8818..ae87c6c 100644 --- a/TEST_GAME.md +++ b/TEST_GAME.md @@ -150,9 +150,9 @@ Tras login exitoso, verás la nueva vista de partida con: ## Notas Técnicas -- **Framework**: JavaFX 12.0.1 -- **Compilador**: Eclipse Compiler (ecj) para Java 8 -- **Versión Java**: 8 (compilado y compatible) +- **Framework**: JavaFX 21.0.2 +- **Compilador**: Eclipse Compiler (ecj) para Java 21 +- **Versión Java**: 21 (LTS) - **Patrón de Diseño**: MVC (Model-View-Controller) - **Threading**: AnimationTimer para actualización de UI en tiempo real diff --git a/pom.xml b/pom.xml index be25a8d..b147e61 100644 --- a/pom.xml +++ b/pom.xml @@ -9,11 +9,11 @@ 1.0-SNAPSHOT - 8 - 8 + 21 + 21 UTF-8 0.0.6 - 12.0.1 + 21.0.2 windows-x86_64 @@ -76,8 +76,8 @@ maven-compiler-plugin 3.8.1 - 8 - 8 + 21 + 21 eclipse diff --git a/src/main/java/es/iesquevedo/controller/GameController.java b/src/main/java/es/iesquevedo/controller/GameController.java index 1782603..d403057 100644 --- a/src/main/java/es/iesquevedo/controller/GameController.java +++ b/src/main/java/es/iesquevedo/controller/GameController.java @@ -6,7 +6,13 @@ import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.Label; import javafx.scene.paint.Color; +import javafx.scene.paint.LinearGradient; +import javafx.scene.paint.RadialGradient; +import javafx.scene.paint.CycleMethod; +import javafx.scene.paint.Stop; import javafx.scene.input.MouseEvent; +import javafx.scene.text.Font; +import javafx.scene.text.TextAlignment; import java.util.logging.Level; import java.util.logging.Logger; @@ -77,74 +83,180 @@ private void updateCurrentTurn() { private void drawBoard() { GraphicsContext gc = boardCanvas.getGraphicsContext2D(); - gc.setFill(Color.WHITE); + double boardStartX = getBoardStartX(); + double boardStartY = getBoardStartY(); + double boardEndX = getBoardEndX(); + double boardEndY = getBoardEndY(); + double boardSpan = (BOARD_SIZE - 1) * CELL_SIZE; + + // Fondo de madera con degradado ligero (más realista) + LinearGradient wood = new LinearGradient( + 0, 0, 1, 1, true, CycleMethod.NO_CYCLE, + new Stop(0, Color.web("#D2A679")), + new Stop(0.5, Color.web("#C37E3A")), + new Stop(1, Color.web("#B06A2E")) + ); + gc.setFill(wood); gc.fillRect(0, 0, boardCanvas.getWidth(), boardCanvas.getHeight()); - gc.setStroke(Color.BLACK); - gc.setLineWidth(1); + // Granulado / vetas finas + gc.setStroke(Color.color(0.15, 0.07, 0.03, 0.06)); + gc.setLineWidth(0.8); + for (int i = 0; i < (int) boardCanvas.getWidth(); i += 6) { + double offset = (i % 24) * 0.3; + gc.beginPath(); + gc.moveTo(0, (i + offset) % boardCanvas.getHeight()); + gc.bezierCurveTo(boardCanvas.getWidth() * 0.25, (i + offset + 8) % boardCanvas.getHeight(), + boardCanvas.getWidth() * 0.75, (i + offset - 12 + boardCanvas.getHeight()) % boardCanvas.getHeight(), + boardCanvas.getWidth(), (i + offset) % boardCanvas.getHeight()); + gc.stroke(); + } + // Marco exterior con esquinas redondeadas perceptuales + gc.setStroke(Color.color(0.12, 0.06, 0.03, 1.0)); + gc.setLineWidth(5); + double outerX = boardStartX - 4; + double outerY = boardStartY - 4; + double outerW = boardSpan + 8; + double outerH = boardSpan + 8; + gc.strokeRoundRect(outerX, outerY, outerW, outerH, 12, 12); + + // Marco interior menos intenso + gc.setStroke(Color.color(0.36, 0.18, 0.08, 0.7)); + gc.setLineWidth(1.5); + gc.strokeRoundRect(boardStartX - 1, boardStartY - 1, + boardSpan + 2, boardSpan + 2, 8, 8); + + // Líneas del tablero, ligeramente suavizadas con doble trazo (sombra + línea) for (int i = 0; i < BOARD_SIZE; i++) { - gc.strokeLine(CELL_SIZE / 2, CELL_SIZE / 2 + i * CELL_SIZE, - CELL_SIZE / 2 + (BOARD_SIZE - 1) * CELL_SIZE, CELL_SIZE / 2 + i * CELL_SIZE); - gc.strokeLine(CELL_SIZE / 2 + i * CELL_SIZE, CELL_SIZE / 2, - CELL_SIZE / 2 + i * CELL_SIZE, CELL_SIZE / 2 + (BOARD_SIZE - 1) * CELL_SIZE); + double y = getIntersectionY(i); + double x = getIntersectionX(i); + + // sombra de la línea horizontal + gc.setStroke(Color.color(0, 0, 0, 0.12)); + gc.setLineWidth(2.0); + gc.strokeLine(boardStartX + 0.8, y + 0.8, boardEndX + 0.8, y + 0.8); + + // línea principal + gc.setStroke(Color.color(0.06, 0.03, 0.01, 1.0)); + gc.setLineWidth(1.2); + gc.strokeLine(boardStartX, y, boardEndX, y); + + // sombra vertical + gc.setStroke(Color.color(0, 0, 0, 0.12)); + gc.setLineWidth(2.0); + gc.strokeLine(x + 0.8, boardStartY + 0.8, x + 0.8, boardEndY + 0.8); + + // línea vertical principal + gc.setStroke(Color.color(0.06, 0.03, 0.01, 1.0)); + gc.setLineWidth(1.2); + gc.strokeLine(x, boardStartY, x, boardEndY); } - for (int i = 0; i < BOARD_SIZE; i++) { - for (int j = 0; j < BOARD_SIZE; j++) { - if (board[i][j] != Stone.EMPTY) { - drawStone(gc, i, j, board[i][j]); + // Coordenadas alrededor del tablero + drawCoordinates(gc, boardStartX, boardStartY, boardEndX, boardEndY); + + // Puntos hoshizashi mejor integrados + gc.setFill(Color.color(0.04, 0.02, 0.01, 1.0)); + int[] stars = {2, 4, 6}; + for (int i : stars) { + for (int j : stars) { + double px = getIntersectionX(i); + double py = getIntersectionY(j); + gc.fillOval(px - 3, py - 3, 6, 6); + gc.setStroke(Color.color(0, 0, 0, 0.18)); + gc.setLineWidth(0.6); + gc.strokeOval(px - 4, py - 4, 8, 8); + } + } + + // Dibujar piedras actuales (usa drawStone) + 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]); } } } } private void drawStone(GraphicsContext gc, int row, int col, Stone stone) { - double x = CELL_SIZE / 2 + col * CELL_SIZE; - double y = CELL_SIZE / 2 + row * CELL_SIZE; - double radius = CELL_SIZE / 2 - 2; - + double x = getIntersectionX(col); + double y = getIntersectionY(row); + double radius = CELL_SIZE / 2 - 3; + if (stone == Stone.BLACK) { - // Sombra - gc.setFill(Color.color(0, 0, 0, 0.3)); - gc.fillOval(x - radius + 2, y - radius + 3, radius * 2, radius * 2); - - // Base oscura - gc.setFill(Color.color(0.2, 0.2, 0.2, 1.0)); + // 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); + + // gradiente radial para dar volumen + RadialGradient blackGrad = new RadialGradient( + 45, 0.1, + x - radius * 0.15, y - radius * 0.2, + radius * 1.05, false, CycleMethod.NO_CYCLE, + new Stop(0.0, Color.web("#6e6e6e")), + new Stop(0.4, Color.web("#222222")), + new Stop(1.0, Color.web("#000000")) + ); + gc.setFill(blackGrad); gc.fillOval(x - radius, y - radius, radius * 2, radius * 2); - - // Superficie negra principal - gc.setFill(Color.BLACK); - gc.fillOval(x - radius + 1, y - radius + 1, radius * 2 - 2, radius * 2 - 2); - - // Destello/brillo - double shine_x = x - radius + radius * 0.3; - double shine_y = y - radius + radius * 0.3; - gc.setFill(Color.color(0.3, 0.3, 0.3, 0.4)); - gc.fillOval(shine_x, shine_y, radius * 0.8, radius * 0.8); + + // highlight suave (brillo) + RadialGradient shine = new RadialGradient( + 45, 0.2, + x - radius * 0.25, y - radius * 0.3, + radius * 0.6, false, CycleMethod.NO_CYCLE, + new Stop(0.0, Color.color(1, 1, 1, 0.9)), + new Stop(0.4, Color.color(1, 1, 1, 0.25)), + new Stop(1.0, Color.color(1, 1, 1, 0.0)) + ); + gc.setGlobalAlpha(1.0); + gc.setFill(shine); + gc.fillOval(x - radius * 0.2, y - radius * 0.25, radius * 1.2, radius * 1.2); + + // borde ligero para definición + gc.setStroke(Color.color(0, 0, 0, 0.45)); + gc.setLineWidth(0.6); + gc.strokeOval(x - radius, y - radius, radius * 2, radius * 2); + } else { - // Sombra para piedra blanca - gc.setFill(Color.color(0, 0, 0, 0.2)); - gc.fillOval(x - radius + 2, y - radius + 3, radius * 2, radius * 2); - - // Base gris claro - gc.setFill(Color.color(0.95, 0.95, 0.95, 1.0)); + // 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); + + // base con degradado radial cálido + RadialGradient whiteGrad = new RadialGradient( + 45, 0.12, + x - radius * 0.12, y - radius * 0.18, + radius * 1.05, false, CycleMethod.NO_CYCLE, + new Stop(0.0, Color.web("#ffffff")), + new Stop(0.6, Color.web("#f2f2f2")), + new Stop(1.0, Color.web("#d1d1d1")) + ); + gc.setFill(whiteGrad); gc.fillOval(x - radius, y - radius, radius * 2, radius * 2); - - // Superficie blanca - gc.setFill(Color.WHITE); - gc.fillOval(x - radius + 1, y - radius + 1, radius * 2 - 2, radius * 2 - 2); - - // Borde gris oscuro - gc.setStroke(Color.color(0.7, 0.7, 0.7, 1.0)); - gc.setLineWidth(1.5); - gc.strokeOval(x - radius + 1, y - radius + 1, radius * 2 - 2, radius * 2 - 2); - - // Destello/brillo en la piedra blanca - double shine_x = x - radius + radius * 0.35; - double shine_y = y - radius + radius * 0.35; - gc.setFill(Color.color(1.0, 1.0, 1.0, 0.6)); - gc.fillOval(shine_x, shine_y, radius * 0.7, radius * 0.7); + + // borde suave gris + gc.setStroke(Color.color(0.6, 0.6, 0.6, 0.6)); + gc.setLineWidth(0.9); + gc.strokeOval(x - radius + 0.4, y - radius + 0.4, radius * 2 - 0.8, radius * 2 - 0.8); + + // highlights blancos + RadialGradient whiteShine = new RadialGradient( + 45, 0.2, + x - radius * 0.2, y - radius * 0.25, + radius * 0.5, false, CycleMethod.NO_CYCLE, + new Stop(0.0, Color.color(1, 1, 1, 0.95)), + new Stop(0.6, Color.color(1, 1, 1, 0.45)), + new Stop(1.0, Color.color(1, 1, 1, 0.0)) + ); + gc.setFill(whiteShine); + gc.fillOval(x - radius * 0.15, y - radius * 0.18, radius * 0.95, radius * 0.95); + + // textura sutil + gc.setFill(Color.color(0.8, 0.8, 0.8, 0.05)); + gc.fillOval(x - radius + 2, y - radius + 2, radius * 1.4, radius * 1.4); } } @@ -157,9 +269,11 @@ private void onBoardClick(MouseEvent event) { double x = event.getX(); double y = event.getY(); + double boardStartX = getBoardStartX(); + double boardStartY = getBoardStartY(); - int col = (int) Math.round((x - CELL_SIZE / 2) / CELL_SIZE); - int row = (int) Math.round((y - CELL_SIZE / 2) / CELL_SIZE); + int col = (int) Math.round((x - boardStartX) / CELL_SIZE); + int row = (int) Math.round((y - boardStartY) / CELL_SIZE); if (isValidPosition(row, col)) { placeStone(row, col); @@ -289,6 +403,53 @@ public void setInitialScores(int score1, int score2) { updateScores(); } } + + private double getBoardStartX() { + return (boardCanvas.getWidth() - ((BOARD_SIZE - 1) * CELL_SIZE)) / 2.0; + } + + private double getBoardStartY() { + return (boardCanvas.getHeight() - ((BOARD_SIZE - 1) * CELL_SIZE)) / 2.0; + } + + private double getBoardEndX() { + return getBoardStartX() + ((BOARD_SIZE - 1) * CELL_SIZE); + } + + private double getBoardEndY() { + return getBoardStartY() + ((BOARD_SIZE - 1) * CELL_SIZE); + } + + private double getIntersectionX(int col) { + return getBoardStartX() + col * CELL_SIZE; + } + + private double getIntersectionY(int row) { + return getBoardStartY() + row * CELL_SIZE; + } + + private void drawCoordinates(GraphicsContext gc, double boardStartX, double boardStartY, double boardEndX, double boardEndY) { + String[] letters = {"A", "B", "C", "D", "E", "F", "G", "H", "J"}; + gc.setFill(Color.color(0.22, 0.12, 0.05, 0.9)); + gc.setFont(Font.font("System", 13)); + gc.setTextAlign(TextAlignment.CENTER); + gc.setTextBaseline(javafx.geometry.VPos.CENTER); + + double offset = 18; + for (int i = 0; i < BOARD_SIZE; i++) { + double x = getIntersectionX(i); + double y = getIntersectionY(i); + + // Letras arriba y abajo + gc.fillText(letters[i], x, boardStartY - offset); + gc.fillText(letters[i], x, boardEndY + offset); + + // Números a izquierda y derecha + String number = String.valueOf(BOARD_SIZE - i); + gc.fillText(number, boardStartX - offset, y); + gc.fillText(number, boardEndX + offset, y); + } + } } diff --git a/src/main/java/es/iesquevedo/controller/LoginController.java b/src/main/java/es/iesquevedo/controller/LoginController.java index f04d5f5..307a469 100644 --- a/src/main/java/es/iesquevedo/controller/LoginController.java +++ b/src/main/java/es/iesquevedo/controller/LoginController.java @@ -13,6 +13,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import es.iesquevedo.util.EmailUtils; /** * Controlador para la pantalla de login. @@ -69,6 +70,13 @@ public void onLoginClicked() { return; } + // Validar formato de email (asegura que contenga '@' y estructura básica) + if (!EmailUtils.isValidEmail(email)) { + updateStatus("El email no tiene un formato válido. Ej: usuario@ejemplo.com", "error"); + LOGGER.log(Level.WARNING, "Intento de login con email inválido: " + email); + return; + } + if (password == null || password.trim().isEmpty()) { updateStatus("La contraseña no puede estar vacía", "error"); LOGGER.log(Level.WARNING, "Intento de login sin contraseña"); @@ -162,5 +170,7 @@ private void updateStatus(String message, String type) { statusLabel.setTextFill(Color.BLACK); } } + + // ...existing code... } diff --git a/src/main/java/es/iesquevedo/util/EmailUtils.java b/src/main/java/es/iesquevedo/util/EmailUtils.java new file mode 100644 index 0000000..4bb3726 --- /dev/null +++ b/src/main/java/es/iesquevedo/util/EmailUtils.java @@ -0,0 +1,18 @@ +package es.iesquevedo.util; + +public final class EmailUtils { + private EmailUtils() {} + + /** + * Valida el formato básico del email (asegura presencia de '@' y dominio con TLD). + * No pretende ser una validación RFC completa, sino una comprobación práctica. + */ + public static boolean isValidEmail(String email) { + if (email == null) return false; + email = email.trim(); + // Regex simple y efectivo que exige: usuario@dominio.tld (TLD >= 2 caracteres) + String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"; + return email.matches(emailRegex); + } +} + diff --git a/src/main/resources/fxml/Game.fxml b/src/main/resources/fxml/Game.fxml index a5bd715..2bcf68a 100644 --- a/src/main/resources/fxml/Game.fxml +++ b/src/main/resources/fxml/Game.fxml @@ -4,6 +4,7 @@ + @@ -54,15 +55,17 @@
- + - - + + - + style="-fx-border-color: #000000; -fx-border-width: 1;" + StackPane.alignment="CENTER"/> +
diff --git a/src/test/java/es/iesquevedo/controller/LoginControllerEmailValidationTest.java b/src/test/java/es/iesquevedo/controller/LoginControllerEmailValidationTest.java new file mode 100644 index 0000000..a8e3a63 --- /dev/null +++ b/src/test/java/es/iesquevedo/controller/LoginControllerEmailValidationTest.java @@ -0,0 +1,27 @@ +package es.iesquevedo.controller; + +import es.iesquevedo.util.EmailUtils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class LoginControllerEmailValidationTest { + + @Test + public void testValidEmails() { + assertTrue(EmailUtils.isValidEmail("usuario@ejemplo.com")); + assertTrue(EmailUtils.isValidEmail("u.ser+tag-123@sub.dominio.org")); + assertTrue(EmailUtils.isValidEmail("nombre.apellido@dominio.co")); + } + + @Test + public void testInvalidEmails() { + assertFalse(EmailUtils.isValidEmail("sinArroba.ejemplo.com")); + assertFalse(EmailUtils.isValidEmail("mal@dominio")); // sin TLD + assertFalse(EmailUtils.isValidEmail("mal@.com")); + assertFalse(EmailUtils.isValidEmail(" ")); + assertFalse(EmailUtils.isValidEmail(null)); + } +} + + From 6295e7c05e4ebcd1305d445575e63d6d67a2bbe4 Mon Sep 17 00:00:00 2001 From: SophiaForeroedu Date: Tue, 12 May 2026 17:20:40 +0200 Subject: [PATCH 2/7] mejorar tablero y validar email en login --- TEST_GAME.md | 6 +- pom.xml | 10 +- .../iesquevedo/controller/GameController.java | 269 ++++++++++++++---- .../controller/LoginController.java | 10 + .../java/es/iesquevedo/util/EmailUtils.java | 18 ++ src/main/resources/fxml/Game.fxml | 13 +- .../LoginControllerEmailValidationTest.java | 27 ++ 7 files changed, 286 insertions(+), 67 deletions(-) create mode 100644 src/main/java/es/iesquevedo/util/EmailUtils.java create mode 100644 src/test/java/es/iesquevedo/controller/LoginControllerEmailValidationTest.java diff --git a/TEST_GAME.md b/TEST_GAME.md index 7ed8818..ae87c6c 100644 --- a/TEST_GAME.md +++ b/TEST_GAME.md @@ -150,9 +150,9 @@ Tras login exitoso, verás la nueva vista de partida con: ## Notas Técnicas -- **Framework**: JavaFX 12.0.1 -- **Compilador**: Eclipse Compiler (ecj) para Java 8 -- **Versión Java**: 8 (compilado y compatible) +- **Framework**: JavaFX 21.0.2 +- **Compilador**: Eclipse Compiler (ecj) para Java 21 +- **Versión Java**: 21 (LTS) - **Patrón de Diseño**: MVC (Model-View-Controller) - **Threading**: AnimationTimer para actualización de UI en tiempo real diff --git a/pom.xml b/pom.xml index be25a8d..b147e61 100644 --- a/pom.xml +++ b/pom.xml @@ -9,11 +9,11 @@ 1.0-SNAPSHOT - 8 - 8 + 21 + 21 UTF-8 0.0.6 - 12.0.1 + 21.0.2 windows-x86_64 @@ -76,8 +76,8 @@ maven-compiler-plugin 3.8.1 - 8 - 8 + 21 + 21 eclipse diff --git a/src/main/java/es/iesquevedo/controller/GameController.java b/src/main/java/es/iesquevedo/controller/GameController.java index 1782603..d403057 100644 --- a/src/main/java/es/iesquevedo/controller/GameController.java +++ b/src/main/java/es/iesquevedo/controller/GameController.java @@ -6,7 +6,13 @@ import javafx.scene.canvas.GraphicsContext; import javafx.scene.control.Label; import javafx.scene.paint.Color; +import javafx.scene.paint.LinearGradient; +import javafx.scene.paint.RadialGradient; +import javafx.scene.paint.CycleMethod; +import javafx.scene.paint.Stop; import javafx.scene.input.MouseEvent; +import javafx.scene.text.Font; +import javafx.scene.text.TextAlignment; import java.util.logging.Level; import java.util.logging.Logger; @@ -77,74 +83,180 @@ private void updateCurrentTurn() { private void drawBoard() { GraphicsContext gc = boardCanvas.getGraphicsContext2D(); - gc.setFill(Color.WHITE); + double boardStartX = getBoardStartX(); + double boardStartY = getBoardStartY(); + double boardEndX = getBoardEndX(); + double boardEndY = getBoardEndY(); + double boardSpan = (BOARD_SIZE - 1) * CELL_SIZE; + + // Fondo de madera con degradado ligero (más realista) + LinearGradient wood = new LinearGradient( + 0, 0, 1, 1, true, CycleMethod.NO_CYCLE, + new Stop(0, Color.web("#D2A679")), + new Stop(0.5, Color.web("#C37E3A")), + new Stop(1, Color.web("#B06A2E")) + ); + gc.setFill(wood); gc.fillRect(0, 0, boardCanvas.getWidth(), boardCanvas.getHeight()); - gc.setStroke(Color.BLACK); - gc.setLineWidth(1); + // Granulado / vetas finas + gc.setStroke(Color.color(0.15, 0.07, 0.03, 0.06)); + gc.setLineWidth(0.8); + for (int i = 0; i < (int) boardCanvas.getWidth(); i += 6) { + double offset = (i % 24) * 0.3; + gc.beginPath(); + gc.moveTo(0, (i + offset) % boardCanvas.getHeight()); + gc.bezierCurveTo(boardCanvas.getWidth() * 0.25, (i + offset + 8) % boardCanvas.getHeight(), + boardCanvas.getWidth() * 0.75, (i + offset - 12 + boardCanvas.getHeight()) % boardCanvas.getHeight(), + boardCanvas.getWidth(), (i + offset) % boardCanvas.getHeight()); + gc.stroke(); + } + // Marco exterior con esquinas redondeadas perceptuales + gc.setStroke(Color.color(0.12, 0.06, 0.03, 1.0)); + gc.setLineWidth(5); + double outerX = boardStartX - 4; + double outerY = boardStartY - 4; + double outerW = boardSpan + 8; + double outerH = boardSpan + 8; + gc.strokeRoundRect(outerX, outerY, outerW, outerH, 12, 12); + + // Marco interior menos intenso + gc.setStroke(Color.color(0.36, 0.18, 0.08, 0.7)); + gc.setLineWidth(1.5); + gc.strokeRoundRect(boardStartX - 1, boardStartY - 1, + boardSpan + 2, boardSpan + 2, 8, 8); + + // Líneas del tablero, ligeramente suavizadas con doble trazo (sombra + línea) for (int i = 0; i < BOARD_SIZE; i++) { - gc.strokeLine(CELL_SIZE / 2, CELL_SIZE / 2 + i * CELL_SIZE, - CELL_SIZE / 2 + (BOARD_SIZE - 1) * CELL_SIZE, CELL_SIZE / 2 + i * CELL_SIZE); - gc.strokeLine(CELL_SIZE / 2 + i * CELL_SIZE, CELL_SIZE / 2, - CELL_SIZE / 2 + i * CELL_SIZE, CELL_SIZE / 2 + (BOARD_SIZE - 1) * CELL_SIZE); + double y = getIntersectionY(i); + double x = getIntersectionX(i); + + // sombra de la línea horizontal + gc.setStroke(Color.color(0, 0, 0, 0.12)); + gc.setLineWidth(2.0); + gc.strokeLine(boardStartX + 0.8, y + 0.8, boardEndX + 0.8, y + 0.8); + + // línea principal + gc.setStroke(Color.color(0.06, 0.03, 0.01, 1.0)); + gc.setLineWidth(1.2); + gc.strokeLine(boardStartX, y, boardEndX, y); + + // sombra vertical + gc.setStroke(Color.color(0, 0, 0, 0.12)); + gc.setLineWidth(2.0); + gc.strokeLine(x + 0.8, boardStartY + 0.8, x + 0.8, boardEndY + 0.8); + + // línea vertical principal + gc.setStroke(Color.color(0.06, 0.03, 0.01, 1.0)); + gc.setLineWidth(1.2); + gc.strokeLine(x, boardStartY, x, boardEndY); } - for (int i = 0; i < BOARD_SIZE; i++) { - for (int j = 0; j < BOARD_SIZE; j++) { - if (board[i][j] != Stone.EMPTY) { - drawStone(gc, i, j, board[i][j]); + // Coordenadas alrededor del tablero + drawCoordinates(gc, boardStartX, boardStartY, boardEndX, boardEndY); + + // Puntos hoshizashi mejor integrados + gc.setFill(Color.color(0.04, 0.02, 0.01, 1.0)); + int[] stars = {2, 4, 6}; + for (int i : stars) { + for (int j : stars) { + double px = getIntersectionX(i); + double py = getIntersectionY(j); + gc.fillOval(px - 3, py - 3, 6, 6); + gc.setStroke(Color.color(0, 0, 0, 0.18)); + gc.setLineWidth(0.6); + gc.strokeOval(px - 4, py - 4, 8, 8); + } + } + + // Dibujar piedras actuales (usa drawStone) + 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]); } } } } private void drawStone(GraphicsContext gc, int row, int col, Stone stone) { - double x = CELL_SIZE / 2 + col * CELL_SIZE; - double y = CELL_SIZE / 2 + row * CELL_SIZE; - double radius = CELL_SIZE / 2 - 2; - + double x = getIntersectionX(col); + double y = getIntersectionY(row); + double radius = CELL_SIZE / 2 - 3; + if (stone == Stone.BLACK) { - // Sombra - gc.setFill(Color.color(0, 0, 0, 0.3)); - gc.fillOval(x - radius + 2, y - radius + 3, radius * 2, radius * 2); - - // Base oscura - gc.setFill(Color.color(0.2, 0.2, 0.2, 1.0)); + // 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); + + // gradiente radial para dar volumen + RadialGradient blackGrad = new RadialGradient( + 45, 0.1, + x - radius * 0.15, y - radius * 0.2, + radius * 1.05, false, CycleMethod.NO_CYCLE, + new Stop(0.0, Color.web("#6e6e6e")), + new Stop(0.4, Color.web("#222222")), + new Stop(1.0, Color.web("#000000")) + ); + gc.setFill(blackGrad); gc.fillOval(x - radius, y - radius, radius * 2, radius * 2); - - // Superficie negra principal - gc.setFill(Color.BLACK); - gc.fillOval(x - radius + 1, y - radius + 1, radius * 2 - 2, radius * 2 - 2); - - // Destello/brillo - double shine_x = x - radius + radius * 0.3; - double shine_y = y - radius + radius * 0.3; - gc.setFill(Color.color(0.3, 0.3, 0.3, 0.4)); - gc.fillOval(shine_x, shine_y, radius * 0.8, radius * 0.8); + + // highlight suave (brillo) + RadialGradient shine = new RadialGradient( + 45, 0.2, + x - radius * 0.25, y - radius * 0.3, + radius * 0.6, false, CycleMethod.NO_CYCLE, + new Stop(0.0, Color.color(1, 1, 1, 0.9)), + new Stop(0.4, Color.color(1, 1, 1, 0.25)), + new Stop(1.0, Color.color(1, 1, 1, 0.0)) + ); + gc.setGlobalAlpha(1.0); + gc.setFill(shine); + gc.fillOval(x - radius * 0.2, y - radius * 0.25, radius * 1.2, radius * 1.2); + + // borde ligero para definición + gc.setStroke(Color.color(0, 0, 0, 0.45)); + gc.setLineWidth(0.6); + gc.strokeOval(x - radius, y - radius, radius * 2, radius * 2); + } else { - // Sombra para piedra blanca - gc.setFill(Color.color(0, 0, 0, 0.2)); - gc.fillOval(x - radius + 2, y - radius + 3, radius * 2, radius * 2); - - // Base gris claro - gc.setFill(Color.color(0.95, 0.95, 0.95, 1.0)); + // 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); + + // base con degradado radial cálido + RadialGradient whiteGrad = new RadialGradient( + 45, 0.12, + x - radius * 0.12, y - radius * 0.18, + radius * 1.05, false, CycleMethod.NO_CYCLE, + new Stop(0.0, Color.web("#ffffff")), + new Stop(0.6, Color.web("#f2f2f2")), + new Stop(1.0, Color.web("#d1d1d1")) + ); + gc.setFill(whiteGrad); gc.fillOval(x - radius, y - radius, radius * 2, radius * 2); - - // Superficie blanca - gc.setFill(Color.WHITE); - gc.fillOval(x - radius + 1, y - radius + 1, radius * 2 - 2, radius * 2 - 2); - - // Borde gris oscuro - gc.setStroke(Color.color(0.7, 0.7, 0.7, 1.0)); - gc.setLineWidth(1.5); - gc.strokeOval(x - radius + 1, y - radius + 1, radius * 2 - 2, radius * 2 - 2); - - // Destello/brillo en la piedra blanca - double shine_x = x - radius + radius * 0.35; - double shine_y = y - radius + radius * 0.35; - gc.setFill(Color.color(1.0, 1.0, 1.0, 0.6)); - gc.fillOval(shine_x, shine_y, radius * 0.7, radius * 0.7); + + // borde suave gris + gc.setStroke(Color.color(0.6, 0.6, 0.6, 0.6)); + gc.setLineWidth(0.9); + gc.strokeOval(x - radius + 0.4, y - radius + 0.4, radius * 2 - 0.8, radius * 2 - 0.8); + + // highlights blancos + RadialGradient whiteShine = new RadialGradient( + 45, 0.2, + x - radius * 0.2, y - radius * 0.25, + radius * 0.5, false, CycleMethod.NO_CYCLE, + new Stop(0.0, Color.color(1, 1, 1, 0.95)), + new Stop(0.6, Color.color(1, 1, 1, 0.45)), + new Stop(1.0, Color.color(1, 1, 1, 0.0)) + ); + gc.setFill(whiteShine); + gc.fillOval(x - radius * 0.15, y - radius * 0.18, radius * 0.95, radius * 0.95); + + // textura sutil + gc.setFill(Color.color(0.8, 0.8, 0.8, 0.05)); + gc.fillOval(x - radius + 2, y - radius + 2, radius * 1.4, radius * 1.4); } } @@ -157,9 +269,11 @@ private void onBoardClick(MouseEvent event) { double x = event.getX(); double y = event.getY(); + double boardStartX = getBoardStartX(); + double boardStartY = getBoardStartY(); - int col = (int) Math.round((x - CELL_SIZE / 2) / CELL_SIZE); - int row = (int) Math.round((y - CELL_SIZE / 2) / CELL_SIZE); + int col = (int) Math.round((x - boardStartX) / CELL_SIZE); + int row = (int) Math.round((y - boardStartY) / CELL_SIZE); if (isValidPosition(row, col)) { placeStone(row, col); @@ -289,6 +403,53 @@ public void setInitialScores(int score1, int score2) { updateScores(); } } + + private double getBoardStartX() { + return (boardCanvas.getWidth() - ((BOARD_SIZE - 1) * CELL_SIZE)) / 2.0; + } + + private double getBoardStartY() { + return (boardCanvas.getHeight() - ((BOARD_SIZE - 1) * CELL_SIZE)) / 2.0; + } + + private double getBoardEndX() { + return getBoardStartX() + ((BOARD_SIZE - 1) * CELL_SIZE); + } + + private double getBoardEndY() { + return getBoardStartY() + ((BOARD_SIZE - 1) * CELL_SIZE); + } + + private double getIntersectionX(int col) { + return getBoardStartX() + col * CELL_SIZE; + } + + private double getIntersectionY(int row) { + return getBoardStartY() + row * CELL_SIZE; + } + + private void drawCoordinates(GraphicsContext gc, double boardStartX, double boardStartY, double boardEndX, double boardEndY) { + String[] letters = {"A", "B", "C", "D", "E", "F", "G", "H", "J"}; + gc.setFill(Color.color(0.22, 0.12, 0.05, 0.9)); + gc.setFont(Font.font("System", 13)); + gc.setTextAlign(TextAlignment.CENTER); + gc.setTextBaseline(javafx.geometry.VPos.CENTER); + + double offset = 18; + for (int i = 0; i < BOARD_SIZE; i++) { + double x = getIntersectionX(i); + double y = getIntersectionY(i); + + // Letras arriba y abajo + gc.fillText(letters[i], x, boardStartY - offset); + gc.fillText(letters[i], x, boardEndY + offset); + + // Números a izquierda y derecha + String number = String.valueOf(BOARD_SIZE - i); + gc.fillText(number, boardStartX - offset, y); + gc.fillText(number, boardEndX + offset, y); + } + } } diff --git a/src/main/java/es/iesquevedo/controller/LoginController.java b/src/main/java/es/iesquevedo/controller/LoginController.java index f04d5f5..307a469 100644 --- a/src/main/java/es/iesquevedo/controller/LoginController.java +++ b/src/main/java/es/iesquevedo/controller/LoginController.java @@ -13,6 +13,7 @@ import java.util.logging.Level; import java.util.logging.Logger; +import es.iesquevedo.util.EmailUtils; /** * Controlador para la pantalla de login. @@ -69,6 +70,13 @@ public void onLoginClicked() { return; } + // Validar formato de email (asegura que contenga '@' y estructura básica) + if (!EmailUtils.isValidEmail(email)) { + updateStatus("El email no tiene un formato válido. Ej: usuario@ejemplo.com", "error"); + LOGGER.log(Level.WARNING, "Intento de login con email inválido: " + email); + return; + } + if (password == null || password.trim().isEmpty()) { updateStatus("La contraseña no puede estar vacía", "error"); LOGGER.log(Level.WARNING, "Intento de login sin contraseña"); @@ -162,5 +170,7 @@ private void updateStatus(String message, String type) { statusLabel.setTextFill(Color.BLACK); } } + + // ...existing code... } diff --git a/src/main/java/es/iesquevedo/util/EmailUtils.java b/src/main/java/es/iesquevedo/util/EmailUtils.java new file mode 100644 index 0000000..4bb3726 --- /dev/null +++ b/src/main/java/es/iesquevedo/util/EmailUtils.java @@ -0,0 +1,18 @@ +package es.iesquevedo.util; + +public final class EmailUtils { + private EmailUtils() {} + + /** + * Valida el formato básico del email (asegura presencia de '@' y dominio con TLD). + * No pretende ser una validación RFC completa, sino una comprobación práctica. + */ + public static boolean isValidEmail(String email) { + if (email == null) return false; + email = email.trim(); + // Regex simple y efectivo que exige: usuario@dominio.tld (TLD >= 2 caracteres) + String emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"; + return email.matches(emailRegex); + } +} + diff --git a/src/main/resources/fxml/Game.fxml b/src/main/resources/fxml/Game.fxml index a5bd715..2bcf68a 100644 --- a/src/main/resources/fxml/Game.fxml +++ b/src/main/resources/fxml/Game.fxml @@ -4,6 +4,7 @@ + @@ -54,15 +55,17 @@
- + - - + + - + style="-fx-border-color: #000000; -fx-border-width: 1;" + StackPane.alignment="CENTER"/> +
diff --git a/src/test/java/es/iesquevedo/controller/LoginControllerEmailValidationTest.java b/src/test/java/es/iesquevedo/controller/LoginControllerEmailValidationTest.java new file mode 100644 index 0000000..a8e3a63 --- /dev/null +++ b/src/test/java/es/iesquevedo/controller/LoginControllerEmailValidationTest.java @@ -0,0 +1,27 @@ +package es.iesquevedo.controller; + +import es.iesquevedo.util.EmailUtils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class LoginControllerEmailValidationTest { + + @Test + public void testValidEmails() { + assertTrue(EmailUtils.isValidEmail("usuario@ejemplo.com")); + assertTrue(EmailUtils.isValidEmail("u.ser+tag-123@sub.dominio.org")); + assertTrue(EmailUtils.isValidEmail("nombre.apellido@dominio.co")); + } + + @Test + public void testInvalidEmails() { + assertFalse(EmailUtils.isValidEmail("sinArroba.ejemplo.com")); + assertFalse(EmailUtils.isValidEmail("mal@dominio")); // sin TLD + assertFalse(EmailUtils.isValidEmail("mal@.com")); + assertFalse(EmailUtils.isValidEmail(" ")); + assertFalse(EmailUtils.isValidEmail(null)); + } +} + + From 87c0b4ef824e9fb92cbed3938c9fcf24cc284957 Mon Sep 17 00:00:00 2001 From: SophiaForeroedu Date: Tue, 12 May 2026 17:51:46 +0200 Subject: [PATCH 3/7] mejorar tablero y validar email en login --- ci/pipeline.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/pipeline.yml b/ci/pipeline.yml index 65feacb..abe7186 100644 --- a/ci/pipeline.yml +++ b/ci/pipeline.yml @@ -13,11 +13,12 @@ jobs: image: eclipse-temurin:21 steps: - checkout + - run: java -version - restore_cache: key: maven-cache paths: - ~/.m2/repository - - run: ./mvnw -B -DskipTests=false test + - run: ./mvnw -B clean test - save_cache: key: maven-cache paths: From f28b14044605aae6b5e48359df1266affac42b97 Mon Sep 17 00:00:00 2001 From: SophiaForeroedu Date: Wed, 13 May 2026 12:49:13 +0200 Subject: [PATCH 4/7] arreglo error, mejorar tablero y validar email en login --- .gitignore | 1 - .idea/compiler.xml | 14 ++++++++++++++ .idea/copilotDiffState.xml | 18 ++++++++++++++++++ TEST_GAME.md | 2 +- pom.xml | 16 +++------------- 5 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/copilotDiffState.xml diff --git a/.gitignore b/.gitignore index 6e637a6..be95506 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ target/ ### IntelliJ IDEA ### .idea/modules.xml .idea/jarRepositories.xml -.idea/compiler.xml .idea/libraries/ *.iws *.iml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..377d957 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilotDiffState.xml b/.idea/copilotDiffState.xml new file mode 100644 index 0000000..9e9dc17 --- /dev/null +++ b/.idea/copilotDiffState.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/TEST_GAME.md b/TEST_GAME.md index ae87c6c..733ec62 100644 --- a/TEST_GAME.md +++ b/TEST_GAME.md @@ -151,7 +151,7 @@ Tras login exitoso, verás la nueva vista de partida con: ## Notas Técnicas - **Framework**: JavaFX 21.0.2 -- **Compilador**: Eclipse Compiler (ecj) para Java 21 +- **Compilador**: Maven Compiler Plugin 3.13.0 con `javac` - **Versión Java**: 21 (LTS) - **Patrón de Diseño**: MVC (Model-View-Controller) - **Threading**: AnimationTimer para actualización de UI en tiempo real diff --git a/pom.xml b/pom.xml index b147e61..e3ef351 100644 --- a/pom.xml +++ b/pom.xml @@ -9,8 +9,7 @@ 1.0-SNAPSHOT - 21 - 21 + 21 UTF-8 0.0.6 21.0.2 @@ -74,19 +73,10 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.13.0 - 21 - 21 - eclipse + ${maven.compiler.release} - - - org.codehaus.plexus - plexus-compiler-eclipse - 2.8.8 - - org.apache.maven.plugins From 449626e87a63f71125f9c6eb194a4237d9f3b486 Mon Sep 17 00:00:00 2001 From: SophiaForeroedu Date: Wed, 13 May 2026 12:49:13 +0200 Subject: [PATCH 5/7] arreglo error, mejorar tablero y validar email en login --- .gitignore | 1 - .idea/compiler.xml | 14 ++++++++++++++ .idea/copilotDiffState.xml | 18 ++++++++++++++++++ TEST_GAME.md | 2 +- pom.xml | 16 +++------------- 5 files changed, 36 insertions(+), 15 deletions(-) create mode 100644 .idea/compiler.xml create mode 100644 .idea/copilotDiffState.xml diff --git a/.gitignore b/.gitignore index 6e637a6..be95506 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,6 @@ target/ ### IntelliJ IDEA ### .idea/modules.xml .idea/jarRepositories.xml -.idea/compiler.xml .idea/libraries/ *.iws *.iml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..377d957 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,14 @@ + + + + + \ No newline at end of file diff --git a/.idea/copilotDiffState.xml b/.idea/copilotDiffState.xml new file mode 100644 index 0000000..9e9dc17 --- /dev/null +++ b/.idea/copilotDiffState.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/TEST_GAME.md b/TEST_GAME.md index ae87c6c..733ec62 100644 --- a/TEST_GAME.md +++ b/TEST_GAME.md @@ -151,7 +151,7 @@ Tras login exitoso, verás la nueva vista de partida con: ## Notas Técnicas - **Framework**: JavaFX 21.0.2 -- **Compilador**: Eclipse Compiler (ecj) para Java 21 +- **Compilador**: Maven Compiler Plugin 3.13.0 con `javac` - **Versión Java**: 21 (LTS) - **Patrón de Diseño**: MVC (Model-View-Controller) - **Threading**: AnimationTimer para actualización de UI en tiempo real diff --git a/pom.xml b/pom.xml index b147e61..e3ef351 100644 --- a/pom.xml +++ b/pom.xml @@ -9,8 +9,7 @@ 1.0-SNAPSHOT - 21 - 21 + 21 UTF-8 0.0.6 21.0.2 @@ -74,19 +73,10 @@ org.apache.maven.plugins maven-compiler-plugin - 3.8.1 + 3.13.0 - 21 - 21 - eclipse + ${maven.compiler.release} - - - org.codehaus.plexus - plexus-compiler-eclipse - 2.8.8 - - org.apache.maven.plugins From f1632e2492846a7374faf7178c34e87ec063e21a Mon Sep 17 00:00:00 2001 From: SophiaForeroedu Date: Wed, 13 May 2026 13:49:38 +0200 Subject: [PATCH 6/7] arreglo error, mejorar tablero y validar email en login --- src/main/java/es/iesquevedo/MainApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/es/iesquevedo/MainApp.java b/src/main/java/es/iesquevedo/MainApp.java index ca48305..090641f 100644 --- a/src/main/java/es/iesquevedo/MainApp.java +++ b/src/main/java/es/iesquevedo/MainApp.java @@ -43,7 +43,7 @@ private static void runConsoleMode() { MainServiceImpl service = new MainServiceImpl(repository); MainController mainController = new MainController(); mainController.setService(service); - +//error, eliminar comentario // Adaptadores UI UIAdapter ui = new UIAdapter(mainController); HealthController healthController = new HealthController(); From e84b40a706084d406c3b2f1868780ed9c65949a6 Mon Sep 17 00:00:00 2001 From: SophiaForeroedu Date: Wed, 13 May 2026 13:50:59 +0200 Subject: [PATCH 7/7] arreglo error, mejorar tablero y validar email en login --- src/main/java/es/iesquevedo/MainApp.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/es/iesquevedo/MainApp.java b/src/main/java/es/iesquevedo/MainApp.java index 090641f..307f4aa 100644 --- a/src/main/java/es/iesquevedo/MainApp.java +++ b/src/main/java/es/iesquevedo/MainApp.java @@ -43,7 +43,7 @@ private static void runConsoleMode() { MainServiceImpl service = new MainServiceImpl(repository); MainController mainController = new MainController(); mainController.setService(service); -//error, eliminar comentario +//error, eliminar este comentario // Adaptadores UI UIAdapter ui = new UIAdapter(mainController); HealthController healthController = new HealthController();