#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Definição dos pinos
#define JOYSTICK_X A1
#define JOYSTICK_Y A0
#define BUTTON_PIN 8
#define BUZZER_PIN 3
// Definições do LCD
#define LCD_ADDR 0x27
#define LCD_COLS 16
#define LCD_ROWS 2
// Configurações do jogo
#define INITIAL_SNAKE_SIZE 2
#define MOVE_INTERVAL 500 // Intervalo de movimento da cobra em milissegundos
// Enumeração para as direções da cobra
enum Direction {
UP,
DOWN,
LEFT,
RIGHT
};
Direction direction = RIGHT; // Direção inicial da cobra
int snakeX[32]; // Array para armazenar as posições X da cobra
int snakeY[32]; // Array para armazenar as posições Y da cobra
int foodX, foodY; // Posições da comida
int snakeSize = INITIAL_SNAKE_SIZE; // Tamanho inicial da cobra
bool gameover = false; // Indicador de fim de jogo
bool gameStarted = false; // Indicador de início de jogo
unsigned long startTime; // Tempo de início do jogo
unsigned long gameTime; // Duração do jogo
LiquidCrystal_I2C lcd(LCD_ADDR, LCD_COLS, LCD_ROWS); // Instância do LCD
void setup() {
Serial.begin(9600);
pinMode(JOYSTICK_X, INPUT);
pinMode(JOYSTICK_Y, INPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);
// Inicialização do LCD
lcd.begin(LCD_COLS, LCD_ROWS);
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Press Button");
lcd.setCursor(0, 1);
lcd.print("to Start");
// Inicialização do gerador de números aleatórios
randomSeed(analogRead(0));
}
void loop() {
if (!gameStarted) {
// Espera pelo início do jogo
if (digitalRead(BUTTON_PIN) == LOW) {
delay(50); // Debouncing
if (digitalRead(BUTTON_PIN) == LOW) {
gameStarted = true;
startTime = millis(); // Registrar o tempo de início do jogo
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Game Started");
delay(1000);
lcd.clear();
initializeGame(); // Inicializar o jogo
}
}
return;
}
if (!gameover) {
// Atualizar e desenhar o jogo enquanto não houver game over
updateJoystick();
moveSnake();
checkCollision();
drawSnake();
delay(MOVE_INTERVAL);
} else {
// Mostrar mensagem de game over e o tempo de jogo
gameTime = (millis() - startTime) / 1000; // Calcular o tempo de jogo em segundos
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Game Over");
lcd.setCursor(0, 1);
lcd.print("Time: ");
lcd.print(gameTime);
lcd.print(" s");
tone(BUZZER_PIN, 1000, 500); // Emite um som de game over
while (true); // Parar o loop
}
}
void initializeGame() {
// Definir direção inicial da cobra
snakeSize = INITIAL_SNAKE_SIZE; // Definir tamanho inicial da cobra
gameover = false; // Resetar estado de game over
// Posição inicial da cobra
for (int i = 0; i < snakeSize; i++) {
snakeX[i] = INITIAL_SNAKE_SIZE - 1;
snakeY[i] = 0;
}
spawnFood(); // Gerar comida numa posição aleatória
}
void updateJoystick() {
int xVal = analogRead(JOYSTICK_X);
int yVal = analogRead(JOYSTICK_Y);
// Atualizar a direção da cobra com base no joystick
/*
0 a 1023 Na hora de testar utilizar o valor minimo e maximo, o motivo pelo qual tenho que estar durante um tempo na telca arrow do teclado
*/
if (xVal > 800 && direction != LEFT && direction != RIGHT) {
direction = RIGHT;
} else if (xVal < 300 && direction != RIGHT && direction != LEFT) {
direction = LEFT;
} else if (yVal > 800 && direction != UP && direction != DOWN) {
direction = DOWN;
} else if (yVal < 200 && direction != DOWN && direction != UP) {
direction = UP;
}
}
void moveSnake() {
// Move o corpo da cobra
for (int i = snakeSize - 1; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
// Move a cabeça da cobra
switch (direction) {
case UP:
snakeY[0]--;
break;
case DOWN:
snakeY[0]++;
break;
case LEFT:
snakeX[0]--;
break;
case RIGHT:
snakeX[0]++;
break;
}
}
void checkCollision() {
// Verifica colisão com as bordas
if (snakeX[0] < 0 || snakeX[0] >= LCD_COLS || snakeY[0] < 0 || snakeY[0] >= LCD_ROWS) {
gameover = true;
return;
}
// Verifica colisão com o próprio corpo
for (int i = 1; i < snakeSize; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
gameover = true;
return;
}
}
// Verifica colisão com a comida
if (snakeX[0] == foodX && snakeY[0] == foodY) {
spawnFood();
growSnake();
}
}
void drawSnake() {
// Limpar a tela
lcd.clear();
// Desenhar a cabeça da cobra
lcd.setCursor(snakeX[0], snakeY[0]);
lcd.print("O");
// Desenhar o corpo da cobra
for (int i = 1; i < snakeSize; i++) {
lcd.setCursor(snakeX[i], snakeY[i]);
lcd.print("*");
}
// Desenhar a comida
lcd.setCursor(foodX, foodY);
lcd.print("X");
}
void spawnFood() {
bool onSnake;
do {
onSnake = false;
// Gerar posição aleatória para a comida
foodX = random(0, LCD_COLS);
foodY = random(0, LCD_ROWS);
// Verificar se a comida não está sobre a cobra
for (int i = 0; i < snakeSize; i++) {
if (snakeX[i] == foodX && snakeY[i] == foodY) {
onSnake = true;
break;
}
}
} while (onSnake); // Repetir se a comida estiver sobre a cobra
}
void growSnake() {
// Adiciona um segmento ao final da cobra
if (snakeSize < 32) { // Limite do tamanho do array
snakeX[snakeSize] = snakeX[snakeSize - 1];
snakeY[snakeSize] = snakeY[snakeSize - 1];
snakeSize++;
} else {
gameWin(); // Se a cobra atingir o tamanho máximo, o jogador ganha
}
}
void gameWin() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Game Won!");
lcd.setCursor(0, 1);
lcd.print("You win!");
while (true); // Parar o loop
}