241 lines
9.2 KiB
Java
241 lines
9.2 KiB
Java
import java.awt.*;
|
|
import java.util.LinkedList;
|
|
import java.util.Random;
|
|
import javax.swing.*;
|
|
|
|
/**
|
|
* Clase que representa un juego de la serpiente en ASCII-Art utilizando Java Swing.
|
|
*/
|
|
public class SnakeAsciiArt extends JPanel {
|
|
// Tamaño del área de juego en caracteres
|
|
private static final int WIDTH = 40;
|
|
private static final int HEIGHT = 20;
|
|
|
|
// Caracteres utilizados en la representación del juego
|
|
private static final char APPLE_CHAR = '@';
|
|
private static final char EMPTY_CHAR = ' ';
|
|
|
|
private LinkedList<Segment> snake; // Lista de segmentos que representan el cuerpo de la serpiente
|
|
private Point apple; // Posición de la manzana
|
|
private char[][] grid; // Matriz de caracteres que representa el área de juego
|
|
private Timer timer; // Temporizador para actualizar el juego periódicamente
|
|
private String direction; // Dirección actual de la serpiente
|
|
private boolean gameOver; // Estado del juego
|
|
|
|
/**
|
|
* Clase que representa un segmento de la serpiente.
|
|
*/
|
|
private static class Segment {
|
|
Point position;
|
|
String direction;
|
|
|
|
Segment(Point position, String direction) {
|
|
this.position = position;
|
|
this.direction = direction;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Constructor que inicializa el juego.
|
|
*/
|
|
public SnakeAsciiArt() {
|
|
snake = new LinkedList<>();
|
|
snake.add(new Segment(new Point(WIDTH / 2, HEIGHT / 2), "RIGHT")); // Posiciona la serpiente en el centro
|
|
direction = "RIGHT"; // Dirección inicial
|
|
gameOver = false; // El juego comienza en estado activo
|
|
spawnApple(); // Genera la primera manzana
|
|
grid = new char[HEIGHT][WIDTH]; // Inicializa la matriz de juego
|
|
|
|
// Configura el temporizador para actualizar el juego cada 200ms
|
|
timer = new Timer(200, e -> updateGame());
|
|
timer.start();
|
|
}
|
|
|
|
/**
|
|
* Genera una nueva manzana en una posición aleatoria que no coincida con la serpiente.
|
|
*/
|
|
private void spawnApple() {
|
|
Random rand = new Random();
|
|
int x, y;
|
|
do {
|
|
x = rand.nextInt(WIDTH);
|
|
y = rand.nextInt(HEIGHT);
|
|
} while (isSnakeBody(x, y)); // Asegura que la manzana no aparezca dentro de la serpiente
|
|
apple = new Point(x, y);
|
|
}
|
|
|
|
/**
|
|
* Verifica si una coordenada pertenece al cuerpo de la serpiente.
|
|
*/
|
|
private boolean isSnakeBody(int x, int y) {
|
|
for (Segment s : snake) {
|
|
if (s.position.x == x && s.position.y == y) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Actualiza el estado del juego en cada ciclo del temporizador.
|
|
*/
|
|
private void updateGame() {
|
|
if (gameOver) return; // No actualizar si el juego ha terminado
|
|
|
|
Segment head = snake.getFirst(); // Obtiene la cabeza de la serpiente
|
|
Point newHeadPosition = new Point(head.position);
|
|
|
|
// Movimiento automático hacia la manzana
|
|
if (apple.x > head.position.x) {
|
|
newHeadPosition.x++;
|
|
direction = "RIGHT";
|
|
} else if (apple.x < head.position.x) {
|
|
newHeadPosition.x--;
|
|
direction = "LEFT";
|
|
} else if (apple.y > head.position.y) {
|
|
newHeadPosition.y++;
|
|
direction = "DOWN";
|
|
} else if (apple.y < head.position.y) {
|
|
newHeadPosition.y--;
|
|
direction = "UP";
|
|
}
|
|
|
|
// Verifica colisiones con los bordes o con el cuerpo de la serpiente
|
|
if (newHeadPosition.x < 0 || newHeadPosition.x >= WIDTH || newHeadPosition.y < 0 || newHeadPosition.y >= HEIGHT || isSnakeBody(newHeadPosition.x, newHeadPosition.y)) {
|
|
System.out.println("Game Over: La serpiente salió del área de juego o se chocó a sí misma.");
|
|
gameOver = true; // Cambia el estado del juego a terminado
|
|
timer.stop(); // Detiene el juego al colisionar
|
|
repaint(); // Redibuja el juego para mostrar el mensaje de "GAME OVER"
|
|
return;
|
|
}
|
|
|
|
// Verifica si la serpiente ha comido la manzana
|
|
if (newHeadPosition.equals(apple)) {
|
|
snake.addFirst(new Segment(newHeadPosition, direction)); // La serpiente crece
|
|
spawnApple(); // Se genera una nueva manzana
|
|
} else {
|
|
snake.addFirst(new Segment(newHeadPosition, direction));
|
|
snake.removeLast(); // Elimina la última parte de la serpiente para simular el movimiento
|
|
}
|
|
repaint(); // Redibuja el juego
|
|
}
|
|
|
|
/**
|
|
* Llena la matriz de juego con los elementos actuales (serpiente y manzana).
|
|
*/
|
|
private void renderGrid() {
|
|
for (int y = 0; y < HEIGHT; y++) {
|
|
for (int x = 0; x < WIDTH; x++) {
|
|
grid[y][x] = EMPTY_CHAR;
|
|
}
|
|
}
|
|
for (Segment s : snake) {
|
|
char segmentChar;
|
|
switch (s.direction) {
|
|
case "UP":
|
|
segmentChar = '|';
|
|
break;
|
|
case "DOWN":
|
|
segmentChar = '|';
|
|
break;
|
|
case "LEFT":
|
|
segmentChar = '-';
|
|
break;
|
|
case "RIGHT":
|
|
segmentChar = '-';
|
|
break;
|
|
default:
|
|
segmentChar = '=';
|
|
}
|
|
grid[s.position.y][s.position.x] = segmentChar;
|
|
}
|
|
Segment head = snake.getFirst();
|
|
switch (head.direction) {
|
|
case "UP":
|
|
grid[head.position.y][head.position.x] = '^';
|
|
break;
|
|
case "DOWN":
|
|
grid[head.position.y][head.position.x] = 'v';
|
|
break;
|
|
case "LEFT":
|
|
grid[head.position.y][head.position.x] = '<';
|
|
break;
|
|
case "RIGHT":
|
|
grid[head.position.y][head.position.x] = '>';
|
|
break;
|
|
}
|
|
grid[apple.y][apple.x] = APPLE_CHAR;
|
|
}
|
|
|
|
/**
|
|
* Dibuja el contenido de la matriz en el panel.
|
|
*/
|
|
@Override
|
|
protected void paintComponent(Graphics g) {
|
|
super.paintComponent(g);
|
|
g.setFont(new Font("Monospaced", Font.PLAIN, 14));
|
|
|
|
if (gameOver) {
|
|
// Dibujar el mensaje de "GAME OVER" estilizado en ASCII
|
|
String[] gameOverMessage = {
|
|
" _ ____ _____ _ ____ _ ____ ___ _ ",
|
|
"(_) ___|| ____| / \\ / ___| / \\ | __ ) / _ \\| |",
|
|
"| \\___ \\| _| / _ \\| | / _ \\ | _ \\| | | | |",
|
|
"| |___) | |___ / ___ \\ |___ / ___ \\| |_) | |_| |_|",
|
|
"|_|____/|_____| /_/ \\_\\____/_/ \\_\\____/ \\___/(_)"
|
|
};
|
|
|
|
int messageX = (getWidth() - g.getFontMetrics().stringWidth(gameOverMessage[0])) / 2;
|
|
int messageY = getHeight() / 2 - (gameOverMessage.length * 15) / 2;
|
|
|
|
for (int i = 0; i < gameOverMessage.length; i++) {
|
|
g.drawString(gameOverMessage[i], messageX, messageY + (i * 15));
|
|
}
|
|
} else {
|
|
renderGrid();
|
|
|
|
// Dibujar el título "SNAKE" estilizado en ASCII
|
|
String[] title = {
|
|
" ____ _____ ____ ____ ___ _____ _ _ _____ _____ ",
|
|
"/ ___|| ____| _ \\| _ \\_ _| ____| \\ | |_ _| ____|",
|
|
"\\___ \\| _| | |_) | |_) | || _| | \\| | | | | _| ",
|
|
" ___) | |___| _ <| __/| || |___| |\\ | | | | |___ ",
|
|
"|____/|_____|_| \\_\\_| |___|_____|_| \\_| |_| |_____|"
|
|
};
|
|
|
|
int titleX = (getWidth() - g.getFontMetrics().stringWidth(title[0])) / 2;
|
|
int titleY = 30; // Ajusta la posición vertical según sea necesario
|
|
|
|
for (int i = 0; i < title.length; i++) {
|
|
g.drawString(title[i], titleX, titleY + (i * 15));
|
|
}
|
|
|
|
// Dibujar el contorno del área de juego
|
|
int gridX = 10;
|
|
int gridY = (titleY + title.length * 15) + 10;
|
|
int cellSize = 15;
|
|
|
|
g.drawRect(gridX - 1, gridY - 1, WIDTH * cellSize + 1, HEIGHT * cellSize + 1);
|
|
|
|
for (int y = 0; y < HEIGHT; y++) {
|
|
StringBuilder line = new StringBuilder();
|
|
for (int x = 0; x < WIDTH; x++) {
|
|
line.append(grid[y][x]);
|
|
}
|
|
g.drawString(line.toString(), gridX, gridY + (y * cellSize));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Método principal que inicializa la ventana y ejecuta el juego.
|
|
*/
|
|
public static void main(String[] args) {
|
|
JFrame frame = new JFrame("ASCII Snake Animation");
|
|
SnakeAsciiArt panel = new SnakeAsciiArt();
|
|
frame.add(panel);
|
|
frame.setSize(620, 450);
|
|
frame.setLocationRelativeTo(null);
|
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
frame.setVisible(true);
|
|
}
|
|
}
|