#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
#include <Wire.h>
#include <Bounce2.h>
#define BUZZER_PIN 10
#define SNAKE_PART 3
#define NUMBUTTONS 4
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SNAKE_SIZE_I 5
#define MAP_X 20
#define MAP_Y 20
#define BUTTOM_DEBOUNCE_I 15
#define TAM_MAX_SNAKE 100
#define DELAY_SNAKE 10
const byte buttons[] = {2, 3, 5, 4};
uint8_t fruit[2];
uint8_t snake[TAM_MAX_SNAKE][2];
uint8_t snake_length;
Bounce* bounce_buttons = new Bounce[NUMBUTTONS];
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);
typedef enum{
MAIN_MENU,
SETTINGS,
PLAYING,
GAMEOVER
} State;
typedef enum{
RIGHT,
LEFT,
TOP,
BOTTOM
} Direction;
State app_state = MAIN_MENU;
Direction direc;
Direction newDirec;
void setup(){
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.display();
delay(1000);
display.clearDisplay();
for (int i = 0; i < NUMBUTTONS; i++){
bounce_buttons[i].attach(buttons[i], INPUT_PULLUP);
bounce_buttons[i].interval(BUTTOM_DEBOUNCE_I);
}
randomSeed(analogRead(0));
}
void text(const char *text, int x, int y, float tam,
uint16_t textColor = SSD1306_WHITE){
display.setTextSize(tam);
display.setTextColor(textColor);
display.setCursor(x, y);
display.print(text);
}
uint8_t cursor = 20;
uint16_t score = 0;
void main_menu(){
text("Menu Principal", 25, 1, 1);
text("Play", 53, 20, 0.5);
text("Settings", 40, 35, 0.5);
text("Exit", 53, 50, 0.5);
if (cursor > 50) cursor = 20;
else if (cursor < 20) cursor = 50;
text(">", 30, cursor, 0.3);
}
void reset(){
snake_length = SNAKE_SIZE_I;
direc = RIGHT;
for (int i = 0; i < snake_length; i++) {
snake[i][0] = MAP_X / 2 - i;
snake[i][1] = MAP_Y / 2;
}
};
void exec_main(){
int8_t aux = (cursor - 20) / 2;
switch (aux) {
case 0: app_state = PLAYING; reset(); break;
case 7: app_state = SETTINGS; break;
case 15: display.ssd1306_command(SSD1306_DISPLAYOFF); break;
}
}
void settings_menu(){
}
void generateFruit(){
bool b = false;
do {
b = false;
fruit[0] = random(0, MAP_X);
fruit[1] = random(0, MAP_Y);
for (int i = 0; i < snake_length; i++) {
if (fruit[0] == snake[i][0] && fruit[1] == snake[i][1]) {
b = true;
continue;
}
}
} while (b);
}
void moveSnake(){
int8_t x = snake[0][0];
int8_t y = snake[0][1];
switch (direc){
case TOP:
y--;
break;
case BOTTOM:
y++;
break;
case LEFT:
x--;
break;
case RIGHT:
x++;
break;
}
for (int i = snake_length - 1; i > 0; i--){
snake[i][0] = snake[i - 1][0];
snake[i][1] = snake[i - 1][1];
}
if (collisionCheck(x, y)){
app_state = GAMEOVER;
}
snake[0][0] = x;
snake[0][1] = y;
colide_apple();
}
bool collisionCheck(int8_t x, int8_t y){
for (int i = 1; i < snake_length; i++){
if (x == snake[i][0] && y == snake[i][1]) return true;
}
if (x < 0 || y < 0 || x >= MAP_X || y >= MAP_Y) return true;
return false;
}
void colide_apple(){
if (snake[0][0] == fruit[0] && snake[0][1] == fruit[1]) {
tone(BUZZER_PIN, 500, 100);
generateFruit();
snake_length++;
score++;
}
}
void drawMap(){
const int offsetMapX = SCREEN_WIDTH - SNAKE_PART * MAP_X - 2;
const int offsetMapY = 2;
display.drawRect(fruit[0] * SNAKE_PART + offsetMapX, fruit[1] * SNAKE_PART + offsetMapY, SNAKE_PART, SNAKE_PART, SSD1306_INVERSE);
display.drawRect(offsetMapX - 2, 0, SNAKE_PART * MAP_X + 4, SNAKE_PART * MAP_Y + 4, SSD1306_WHITE);
for (int i = 0; i < snake_length; i++) {
display.fillRect(snake[i][0] * SNAKE_PART + offsetMapX, snake[i][1] * SNAKE_PART + offsetMapY, SNAKE_PART, SNAKE_PART, SSD1306_WHITE);
}
}
uint8_t moveTime = 0;
void loop() {
display.clearDisplay();
for (int i = 0; i < NUMBUTTONS; i++) {
bounce_buttons[i].update();
if (bounce_buttons[i].fell()) {
if (app_state == MAIN_MENU) {
if (i == 0){
cursor -= 15;
}
if (i == 1){
exec_main();
}
if (i == 2){
cursor += 15;
}
if (i == 3){}
}
else if (app_state == PLAYING){
if (i == 0 && direc != BOTTOM){
direc = TOP;
}
else if (i == 1 && direc != LEFT){
direc = RIGHT;
}
else if (i == 2 && direc != TOP){
direc = BOTTOM;
}
else if (i == 3 && direc != RIGHT){
direc = LEFT;
}
}
}
}
switch (app_state){
case MAIN_MENU:
main_menu();
break;
case SETTINGS:
settings_menu();
break;
case PLAYING:
moveTime++;
if (moveTime >= DELAY_SNAKE){
moveSnake();
moveTime = 0;
}
drawMap();
String S_score = "Score: " + String(score);
const char* score_char = S_score.c_str();
text(score_char, 0, 10, 0.2);
break;
case GAMEOVER:
break;
}
display.display();
}