diff --git a/.DS_Store b/.DS_Store index d0f4998..46cc050 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/SnakeAsciiArt.java b/SnakeAsciiArt.java new file mode 100644 index 0000000..08bcfc6 --- /dev/null +++ b/SnakeAsciiArt.java @@ -0,0 +1,240 @@ +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); + } +}