ascii_art/SnakeAsciiArt.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);
}
}