Proyecto: Juego sencillo en Java
Introducción
Un aspecto fascinante de la programación es la posibilidad de crear juegos, donde podemos aplicar muchos de los conceptos que hemos aprendido hasta ahora en un contexto divertido y creativo. Los juegos constituyen un excelente ejercicio práctico ya que combinan múltiples aspectos de la programación: gestión de estados, interacción con el usuario, lógica condicional y, en algunos casos, representación gráfica. En este artículo, desarrollaremos un juego de "Adivina el número" con una interfaz por consola y luego evolucionaremos hacia una versión gráfica sencilla utilizando JavaFX.
A través de este proyecto práctico, reforzaremos conceptos fundamentales de Java como clases, objetos, control de flujo, manejo de excepciones y entrada/salida, mientras creamos algo entretenido que puede ser expandido y personalizado posteriormente.
Planificación del juego
Antes de empezar a programar, es importante definir qué queremos construir. Para nuestro juego "Adivina el número":
- Objetivo del juego: El programa generará un número aleatorio entre 1 y 100, y el jugador deberá adivinarlo en la menor cantidad de intentos posible.
- Mecánica: El jugador introduce un número y recibe pistas sobre si es mayor o menor que el número a adivinar.
- Final del juego: El juego termina cuando el jugador adivina el número o decide rendirse.
Componentes necesarios
Para desarrollar este juego necesitaremos implementar:
- Generación de números aleatorios
- Gestión de la entrada del usuario
- Lógica del juego (comparaciones, conteo de intentos)
- Interfaz de usuario (primero por consola, luego gráfica)
Versión por consola
Comencemos con la implementación de la versión por consola del juego.
Estructura del proyecto
// AdivinaNumero.java
import java.util.Random;
import java.util.Scanner;
public class AdivinaNumero {
private int numeroSecreto;
private int intentos;
private boolean juegoTerminado;
private final Scanner scanner;
public AdivinaNumero() {
// Inicializamos las variables
Random random = new Random();
this.numeroSecreto = random.nextInt(100) + 1; // Número entre 1 y 100
this.intentos = 0;
this.juegoTerminado = false;
this.scanner = new Scanner(System.in);
}
public void jugar() {
System.out.println("¡Bienvenido a Adivina el Número!");
System.out.println("He pensado un número entre 1 y 100.");
System.out.println("Intenta adivinarlo en la menor cantidad de intentos posible.");
while (!juegoTerminado) {
System.out.print("\nIntroduce un número (o escribe 'rendirse' para terminar): ");
String entrada = scanner.nextLine();
// Comprobar si el jugador se rinde
if (entrada.equalsIgnoreCase("rendirse")) {
System.out.println("Te has rendido. El número era: " + numeroSecreto);
juegoTerminado = true;
continue;
}
// Procesar la entrada numérica
try {
int numeroUsuario = Integer.parseInt(entrada);
procesarIntento(numeroUsuario);
} catch (NumberFormatException e) {
System.out.println("Por favor, introduce un número válido o escribe 'rendirse'.");
}
}
scanner.close();
}
private void procesarIntento(int numeroUsuario) {
intentos++;
if (numeroUsuario < 1 || numeroUsuario > 100) {
System.out.println("El número debe estar entre 1 y 100.");
return;
}
if (numeroUsuario < numeroSecreto) {
System.out.println("El número secreto es MAYOR.");
} else if (numeroUsuario > numeroSecreto) {
System.out.println("El número secreto es MENOR.");
} else {
System.out.println("¡CORRECTO! Has adivinado el número en " + intentos + " intentos.");
juegoTerminado = true;
}
}
public static void main(String[] args) {
AdivinaNumero juego = new AdivinaNumero();
juego.jugar();
}
}
Explicación del código
-
Atributos de la clase:
numeroSecreto
: El número que el jugador debe adivinarintentos
: Contador de intentos realizadosjuegoTerminado
: Estado del juegoscanner
: Objeto para leer la entrada del usuario
-
Constructor:
- Inicializamos el número secreto de forma aleatoria con la clase
Random
- Configuramos los valores iniciales para las demás variables
- Inicializamos el número secreto de forma aleatoria con la clase
-
Método
jugar()
:- Muestra las instrucciones iniciales
- Implementa el bucle principal del juego
- Procesa la entrada del usuario y maneja la opción de rendirse
-
Método
procesarIntento()
:- Incrementa el contador de intentos
- Valida que el número esté dentro del rango permitido
- Compara el número del usuario con el número secreto
- Proporciona pistas o indica si el jugador ha ganado
-
Método
main()
:- Punto de entrada del programa, crea una instancia del juego y lo inicia
Probando el juego
Para probar el juego, simplemente compila y ejecuta la clase AdivinaNumero
:
javac AdivinaNumero.java
java AdivinaNumero
Deberías ver la bienvenida y poder comenzar a introducir números para intentar adivinar. El programa te proporcionará pistas sobre si tu número es mayor o menor que el número secreto.
Mejorando el juego: niveles de dificultad
Añadamos la posibilidad de seleccionar diferentes niveles de dificultad que cambiarán el rango de números.
// AdivinaNumeroMejorado.java
import java.util.Random;
import java.util.Scanner;
public class AdivinaNumeroMejorado {
private int numeroSecreto;
private int intentos;
private boolean juegoTerminado;
private final Scanner scanner;
private int rangoMaximo;
public AdivinaNumeroMejorado() {
this.scanner = new Scanner(System.in);
this.intentos = 0;
this.juegoTerminado = false;
}
public void iniciar() {
System.out.println("¡Bienvenido a Adivina el Número!");
seleccionarDificultad();
// Generar número aleatorio según la dificultad
Random random = new Random();
this.numeroSecreto = random.nextInt(rangoMaximo) + 1;
jugar();
}
private void seleccionarDificultad() {
System.out.println("\nSelecciona el nivel de dificultad:");
System.out.println("1. Fácil (1-50)");
System.out.println("2. Normal (1-100)");
System.out.println("3. Difícil (1-200)");
System.out.print("Introduce tu elección (1-3): ");
try {
int opcion = Integer.parseInt(scanner.nextLine());
switch (opcion) {
case 1:
rangoMaximo = 50;
System.out.println("Has seleccionado nivel Fácil (1-50)");
break;
case 2:
rangoMaximo = 100;
System.out.println("Has seleccionado nivel Normal (1-100)");
break;
case 3:
rangoMaximo = 200;
System.out.println("Has seleccionado nivel Difícil (1-200)");
break;
default:
System.out.println("Opción no válida. Seleccionando nivel Normal por defecto.");
rangoMaximo = 100;
}
} catch (NumberFormatException e) {
System.out.println("Entrada no válida. Seleccionando nivel Normal por defecto.");
rangoMaximo = 100;
}
}
public void jugar() {
System.out.println("\nHe pensado un número entre 1 y " + rangoMaximo + ".");
System.out.println("Intenta adivinarlo en la menor cantidad de intentos posible.");
while (!juegoTerminado) {
System.out.print("\nIntroduce un número (o escribe 'rendirse' para terminar): ");
String entrada = scanner.nextLine();
if (entrada.equalsIgnoreCase("rendirse")) {
System.out.println("Te has rendido. El número era: " + numeroSecreto);
juegoTerminado = true;
continue;
}
try {
int numeroUsuario = Integer.parseInt(entrada);
procesarIntento(numeroUsuario);
} catch (NumberFormatException e) {
System.out.println("Por favor, introduce un número válido o escribe 'rendirse'.");
}
}
preguntarNuevaPartida();
}
private void procesarIntento(int numeroUsuario) {
intentos++;
if (numeroUsuario < 1 || numeroUsuario > rangoMaximo) {
System.out.println("El número debe estar entre 1 y " + rangoMaximo + ".");
return;
}
if (numeroUsuario < numeroSecreto) {
System.out.println("El número secreto es MAYOR.");
} else if (numeroUsuario > numeroSecreto) {
System.out.println("El número secreto es MENOR.");
} else {
System.out.println("¡CORRECTO! Has adivinado el número en " + intentos + " intentos.");
juegoTerminado = true;
}
}
private void preguntarNuevaPartida() {
System.out.print("\n¿Quieres jugar otra partida? (s/n): ");
String respuesta = scanner.nextLine();
if (respuesta.equalsIgnoreCase("s")) {
// Reiniciar el juego
this.intentos = 0;
this.juegoTerminado = false;
iniciar();
} else {
System.out.println("¡Gracias por jugar! ¡Hasta pronto!");
scanner.close();
}
}
public static void main(String[] args) {
AdivinaNumeroMejorado juego = new AdivinaNumeroMejorado();
juego.iniciar();
}
}
Nuevas características
- Niveles de dificultad: Ahora el jugador puede seleccionar entre tres niveles que modifican el rango de números posibles.
- Reinicio del juego: Al finalizar una partida, se ofrece la posibilidad de jugar nuevamente.
- Mejor estructura: Hemos reorganizado el código para separar mejor las diferentes funcionalidades.
Versión con interfaz gráfica
Ahora vamos a evolucionar nuestro juego para crear una versión con interfaz gráfica utilizando JavaFX. Para este ejemplo, asegúrate de tener configurado JavaFX en tu entorno de desarrollo.
// AdivinaNumeroGUI.java
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.util.Random;
public class AdivinaNumeroGUI extends Application {
private int numeroSecreto;
private int intentos;
private int rangoMaximo;
private boolean juegoTerminado;
// Componentes de la interfaz
private TextField campoNumero;
private Button botonAdivinar;
private Button botonReiniciar;
private Text mensajeResultado;
private Text contadorIntentos;
private ComboBox<String> comboNivel;
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Adivina el Número");
// Configuración inicial
rangoMaximo = 100; // Por defecto
juegoTerminado = false;
intentos = 0;
generarNumeroSecreto();
// Crear la interfaz
GridPane grid = crearInterfaz();
// Configurar la escena
Scene scene = new Scene(grid, 400, 350);
primaryStage.setScene(scene);
primaryStage.show();
}
private GridPane crearInterfaz() {
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
// Título
Text titulo = new Text("Adivina el Número");
titulo.setFont(Font.font("Tahoma", FontWeight.BOLD, 20));
grid.add(titulo, 0, 0, 2, 1);
// Selección de nivel
Label nivelLabel = new Label("Nivel de dificultad:");
grid.add(nivelLabel, 0, 1);
comboNivel = new ComboBox<>();
comboNivel.getItems().addAll("Fácil (1-50)", "Normal (1-100)", "Difícil (1-200)");
comboNivel.setValue("Normal (1-100)");
comboNivel.setOnAction(e -> cambiarNivel());
grid.add(comboNivel, 1, 1);
// Instrucciones
Text instrucciones = new Text("He pensado un número entre 1 y " + rangoMaximo +
". ¡Intenta adivinarlo!");
grid.add(instrucciones, 0, 2, 2, 1);
// Campo para introducir número
Label numeroLabel = new Label("Tu número:");
grid.add(numeroLabel, 0, 3);
campoNumero = new TextField();
grid.add(campoNumero, 1, 3);
// Botones
HBox hbBotones = new HBox(10);
botonAdivinar = new Button("Adivinar");
botonAdivinar.setOnAction(e -> procesarIntento());
botonReiniciar = new Button("Nueva partida");
botonReiniciar.setOnAction(e -> reiniciarJuego());
hbBotones.getChildren().addAll(botonAdivinar, botonReiniciar);
hbBotones.setAlignment(Pos.CENTER);
grid.add(hbBotones, 0, 4, 2, 1);
// Mensaje de resultado
mensajeResultado = new Text();
mensajeResultado.setFont(Font.font("Tahoma", FontWeight.NORMAL, 14));
grid.add(mensajeResultado, 0, 5, 2, 1);
// Contador de intentos
contadorIntentos = new Text("Intentos: 0");
contadorIntentos.setFont(Font.font("Tahoma", FontWeight.NORMAL, 14));
grid.add(contadorIntentos, 0, 6, 2, 1);
return grid;
}
private void generarNumeroSecreto() {
Random random = new Random();
numeroSecreto = random.nextInt(rangoMaximo) + 1;
}
private void cambiarNivel() {
String nivelSeleccionado = comboNivel.getValue();
if (nivelSeleccionado.startsWith("Fácil")) {
rangoMaximo = 50;
} else if (nivelSeleccionado.startsWith("Normal")) {
rangoMaximo = 100;
} else {
rangoMaximo = 200;
}
reiniciarJuego();
}
private void procesarIntento() {
if (juegoTerminado) {
mensajeResultado.setText("El juego ha terminado. Inicia una nueva partida.");
mensajeResultado.setFill(Color.BLUE);
return;
}
try {
int numeroUsuario = Integer.parseInt(campoNumero.getText());
intentos++;
contadorIntentos.setText("Intentos: " + intentos);
if (numeroUsuario < 1 || numeroUsuario > rangoMaximo) {
mensajeResultado.setText("El número debe estar entre 1 y " + rangoMaximo);
mensajeResultado.setFill(Color.ORANGE);
return;
}
if (numeroUsuario < numeroSecreto) {
mensajeResultado.setText("El número secreto es MAYOR");
mensajeResultado.setFill(Color.RED);
} else if (numeroUsuario > numeroSecreto) {
mensajeResultado.setText("El número secreto es MENOR");
mensajeResultado.setFill(Color.RED);
} else {
mensajeResultado.setText("¡CORRECTO! Has adivinado en " + intentos + " intentos");
mensajeResultado.setFill(Color.GREEN);
juegoTerminado = true;
}
} catch (NumberFormatException e) {
mensajeResultado.setText("Por favor, introduce un número válido");
mensajeResultado.setFill(Color.ORANGE);
}
campoNumero.clear();
campoNumero.requestFocus();
}
private void reiniciarJuego() {
juegoTerminado = false;
intentos = 0;
generarNumeroSecreto();
campoNumero.clear();
mensajeResultado.setText("");
contadorIntentos.setText("Intentos: 0");
campoNumero.setDisable(false);
botonAdivinar.setDisable(false);
}
public static void main(String[] args) {
launch(args);
}
}
Explicación de la versión gráfica
-
Componentes de JavaFX:
GridPane
: Como contenedor principal para organizar los elementosTextField
: Para que el usuario introduzca su númeroButton
: Para las acciones "Adivinar" y "Nueva partida"ComboBox
: Para seleccionar el nivel de dificultadText
: Para mostrar mensajes y resultados
-
Estructura del código:
start()
: Método principal que configura la aplicacióncrearInterfaz()
: Construye la interfaz gráficaprocesarIntento()
: Maneja la lógica del juegocambiarNivel()
yreiniciarJuego()
: Gestionan el cambio de configuración
-
Características adicionales:
- Retroalimentación visual con colores según el resultado
- Interfaz intuitiva con botones y campos de texto
- Mayor interactividad con selección de nivel mediante menú desplegable
Ejecución de la versión gráfica
Para ejecutar la versión gráfica, necesitas tener JavaFX configurado en tu entorno. La forma de ejecutar dependerá de tu configuración:
javac --module-path /ruta/a/javafx-sdk/lib --add-modules javafx.controls,javafx.fxml AdivinaNumeroGUI.java
java --module-path /ruta/a/javafx-sdk/lib --add-modules javafx.controls,javafx.fxml AdivinaNumeroGUI
Ampliaciones posibles
Una vez que tienes la base del juego funcionando, puedes considerar estas mejoras:
- Sistema de puntuación: Almacenar las mejores puntuaciones (menos intentos) en un archivo.
- Mejoras visuales: Añadir animaciones, efectos de sonido, o imágenes.
- Diferentes modos de juego: Por ejemplo, con límite de tiempo o número máximo de intentos.
- Multijugador: Permitir que dos jugadores compitan por turnos.
Ejemplo: Añadir un sistema de puntuación
// Fragmento para guardar puntuaciones
private void guardarPuntuacion(String nombre, int intentos, String nivel) {
try (FileWriter fw = new FileWriter("puntuaciones.txt", true);
BufferedWriter bw = new BufferedWriter(fw);
PrintWriter out = new PrintWriter(bw)) {
out.println(nombre + "," + intentos + "," + nivel + "," + new Date());
} catch (IOException e) {
System.err.println("Error al guardar la puntuación: " + e.getMessage());
}
}
// Fragmento para cargar puntuaciones
private List<String[]> cargarPuntuaciones() {
List<String[]> puntuaciones = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader("puntuaciones.txt"))) {
String linea;
while ((linea = br.readLine()) != null) {
puntuaciones.add(linea.split(","));
}
} catch (IOException e) {
System.err.println("Error al cargar las puntuaciones: " + e.getMessage());
}
return puntuaciones;
}
Resumen
En este proyecto, hemos creado un juego "Adivina el número" en Java, primero con una interfaz de consola y luego con una interfaz gráfica utilizando JavaFX. A lo largo del desarrollo, hemos aplicado numerosos conceptos importantes de Java:
- Programación orientada a objetos: creando clases con atributos y métodos
- Manejo de entrada/salida: tanto por consola como con interfaz gráfica
- Control de flujo: con condicionales para la lógica del juego
- Gestión de excepciones: para manejar entradas incorrectas
- Interfaz de usuario: desarrollo de una UI interactiva con JavaFX
Este proyecto sirve como excelente punto de partida para explorar el desarrollo de juegos en Java. Puedes expandirlo con las sugerencias mencionadas o incluso aventurarte a crear juegos más complejos utilizando los mismos principios fundamentales. ¡La programación de juegos es una forma divertida y efectiva de mejorar tus habilidades de programación en Java!