#include <Arduino.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "pitches.h" // Asegúrate de tener este archivo con las definiciones de notas
// Configuración de la pantalla OLED
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Configuración del joystick
int joystick_x = 13;
int joystick_y = 12;
int joystick_select = 4;
// Declaramos el pin del zumbador
int pinZumbador = 14; // Cambia este pin si es necesario
// Tamaño del juego
#define WIDTH 128
#define HEIGHT 64
// Definición de la serpiente
struct Snake {
int x, y;
Snake* next;
};
Snake* head = nullptr;
int food_x, food_y;
int score = 0;
int direction = 0; // 0: derecha, 1: abajo, 2: izquierda, 3: arriba
// Variables para controlar la velocidad
int delayTime = 100; // Tiempo de delay inicial
int minDelay = 20; // Tiempo de delay mínimo
// Definición de tonos para el juego
#define TONE_E4 NOTE_E4
#define TONE_G4 NOTE_G4
#define TONE_C5 NOTE_C5
#define TONE_D5 NOTE_D5
#define TONE_GAME_OVER NOTE_DS5
#define TONE_FRUIT NOTE_B4 // Tono agudo para recolectar fruta
void setup() {
// Inicializar la pantalla OLED
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.display();
// Inicializar el joystick
pinMode(joystick_x, INPUT);
pinMode(joystick_y, INPUT);
pinMode(joystick_select, INPUT_PULLUP);
// Inicializar el pin del buzzer
pinMode(pinZumbador, OUTPUT);
// Inicializar la serpiente y la comida
head = new Snake;
head->x = WIDTH / 2;
head->y = HEIGHT / 2;
head->next = nullptr;
// Generar comida asegurando que no aparezca en la parte superior
food_x = random(1, WIDTH - 1);
food_y = random(11, HEIGHT - 1);
}
void loop() {
// Leer la posición del joystick
int x_value = analogRead(joystick_x);
int y_value = analogRead(joystick_y);
// Cambiar dirección según el joystick (invertir la lógica y evitar cambios de dirección opuestos)
if (x_value < 1000 && direction != 2) {
direction = 0; // derecha
playShortTone(TONE_E4); // Sonido corto al mover a la derecha
} else if (x_value > 3000 && direction != 0) {
direction = 2; // izquierda
playShortTone(TONE_E4); // Sonido corto al mover a la izquierda
} else if (y_value < 1000 && direction != 3) {
direction = 1; // abajo
playShortTone(TONE_E4); // Sonido corto al mover hacia abajo
} else if (y_value > 3000 && direction != 1) {
direction = 3; // arriba
playShortTone(TONE_E4); // Sonido corto al mover hacia arriba
}
// Mover la serpiente...
Snake* current = head;
while (current->next != nullptr) {
current->x = current->next->x;
current->y = current->next->y;
current = current->next;
}
switch (direction) {
case 0: current->x++; break; // derecha
case 1: current->y++; break; // abajo
case 2: current->x--; break; // izquierda
case 3: current->y--; break; // arriba
}
// Comprobar si la serpiente ha comido la comida...
if (current->x == food_x && current->y == food_y) {
score++;
Snake* new_segment = new Snake;
new_segment->x = current->x;
new_segment->y = current->y;
new_segment->next = nullptr;
current->next = new_segment;
// Generar nueva comida asegurando que no aparezca en la parte superior
food_x = random(1, WIDTH - 1);
food_y = random(11, HEIGHT - 1);
// Sonido al comer comida (agudo y prolongado)
playCollectFruitSound();
// Aumentar la velocidad (reducir delay)
if (delayTime > minDelay) {
delayTime -= 10; // Disminuye el delay en 10 ms
}
}
// Comprobar colisiones con los muros y consigo misma...
bool game_over = false;
if (current->x < 0 || current->x >= WIDTH || current->y < 10 || current->y >= HEIGHT) {
game_over = true;
} else {
Snake* temp = head;
while (temp != current) {
if (temp->x == current->x && temp->y == current->y) {
game_over = true;
break;
}
temp = temp->next;
}
}
// Dibujar la pantalla...
display.clearDisplay();
// Dibujar los muros...
display.fillRect(0,10,WIDTH,1,WHITE);
display.fillRect(0,HEIGHT-1,WIDTH,1,WHITE);
display.fillRect(0,0,1,HEIGHT,WHITE);
display.fillRect(WIDTH-1,0,1,HEIGHT,WHITE);
// Mostrar el puntaje...
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(2,2);
display.print("Score: ");
display.println(score);
// Dibujar la serpiente...
Snake* current_segment = head;
while (current_segment != nullptr) {
display.fillRect(current_segment->x,current_segment->y,1,1,WHITE);
current_segment=current_segment->next;
}
// Dibujar la comida...
display.fillRect(food_x,food_y,1,1,WHITE);
if (game_over) {
playGameOverSound();
display.setTextSize(1);
display.setCursor(20,20);
display.println("Game Over");
display.display();
while (true){
if(!digitalRead(joystick_select)) {
resetGame();
break;
}
};
}
display.display();
delay(delayTime);
}
// Función para reproducir un tono específico en el buzzer.
void playTone(int frequency) {
tone(pinZumbador, frequency);
delay(100);
}
// Función para reproducir un tono corto que se apaga inmediatamente.
void playShortTone(int frequency) {
tone(pinZumbador, frequency);
delay(50);
noTone(pinZumbador); // Apagar el tono inmediatamente después de un breve retraso.
}
// Función para reproducir un sonido al recolectar fruta.
void playCollectFruitSound() {
tone(pinZumbador, TONE_FRUIT); // Sonido agudo al recolectar fruta.
delay(200); // Mantener el sonido por un momento.
noTone(pinZumbador); // Apagar el sonido después de un momento.
}
// Función para reproducir el sonido de Game Over que va de agudo a grave.
void playGameOverSound() {
for (int freq = NOTE_A5; freq >= NOTE_C4; freq -= 50) {
tone(pinZumbador, freq);
delay(100);
}
noTone(pinZumbador);
}
// Función para reiniciar el juego.
void resetGame() {
head->x = WIDTH /2;
head->y = HEIGHT /2;
head->next=nullptr;
food_x=random(1,WIDTH-1);
food_y=random(11,HEIGHT-1);
score=0;
}