#include <Adafruit_SSD1306.h>
#include <LiquidCrystal.h>
//Kijelző define-ok
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
//Joystick define-ok
#define VERTICAL A0
#define HORIZONTAL A1
#define CENTER 12
//Buzzer define-ok
#define BUZZER 13
//Játék define-ok
//Győzelemhez szükséges pont
#define SCORE_TO_WIN 10
//Méret
#define SNAKE_BASE_LENGTH 3
//Irányok
#define LEFT 0
#define RIGHT 1
#define UP 2
#define DOWN 3
//Játék státuszok
#define GAME_RUN 1
#define GAME_OVER 2
#define GAME_WIN 3
//OLED setup
Adafruit_SSD1306 oled(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
//LCD setup
LiquidCrystal lcd(2, 3, 11, 10, 9, 8);
byte* snakeCoordinates = (byte*)malloc((SNAKE_BASE_LENGTH * 2) * sizeof(byte));
byte foodCoordinates[2];
byte optionsCount = 3;
char* options[] = {
"Start",
"Settings",
"Info"
};
byte optionsStartInHover[] = {
50,
40,
53
};
byte settingsCount = 2;
char* settings[] = {
"Size:",
"Velocity:"
};
byte selectedSetting = 0;
byte sizeCount = 2;
byte size[] = {
2, 4
};
byte selectedSize = 0;
byte velocityCount = 4;
int velocity[] = {
100, 140, 180, 220
};
byte selectedVelocity = 0;
byte option = 0;
byte state = 0;
byte length;
byte direction;
int score;
byte gameState;
bool first = false;
bool initialization = true;
void setup() {
Serial.begin(9600);
lcd.begin(16, 2);
if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
while(true);
}
pinMode(VERTICAL, INPUT);
pinMode(HORIZONTAL, INPUT);
pinMode(CENTER, INPUT_PULLUP);
pinMode(BUZZER, OUTPUT);
setupGame();
lcd.clear();
lcd.print("WELCOME TO SNAKE");
}
void loop() {
switch (state) {
//Főmenü
case 0:
selectMenuOption();
drawMenu();
break;
//Játék
case 1:
if (gameState == GAME_RUN) {
initGame(1000);
int x = analogRead(HORIZONTAL);
int y = analogRead(VERTICAL);
setDirection(x, y);
runGame(direction);
} else if (gameState == GAME_OVER) {
overGame();
} else {
winGame();
}
break;
//Beállítások
case 2:
selectSetting();
drawSettings();
break;
//Információ
case 3:
returnFromInformation();
drawInformation();
break;
}
}
//Játék vezérlő függvények
//Játék inicializálása (joystick miatt)
void initGame(int time) {
if (initialization) {
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(WHITE);
oled.setCursor(12, 30);
oled.println("Initialization...");
oled.display();
delay(time);
initialization = false;
lcd.clear();
lcd.print("SCORE: ");
lcd.print(score);
}
}
//Alapbeállítások beállítása, nullázások
void setupGame() {
length = SNAKE_BASE_LENGTH;
score = 0;
direction = UP;
gameState = GAME_RUN;
initialization = true;
for (int i = 0; i < (length * 2); i+=2) {
snakeCoordinates[i] = SCREEN_WIDTH/2;
snakeCoordinates[i+1] = SCREEN_HEIGHT/2 - size[selectedSize]*(length-i/2);
}
generateFood();
}
//Játékmenet
void runGame(byte direction) {
oled.clearDisplay();
if (score >= SCORE_TO_WIN) {
gameState = GAME_WIN;
}
if (!killSnake()) {
drawSnake();
drawFood();
if (hitFood()) {
eatFood();
}
for (int i = length - 1; i >= 1; i--) {
snakeCoordinates[i * 2] = snakeCoordinates[(i-1) * 2];
snakeCoordinates[i * 2 + 1] = snakeCoordinates[(i-1) * 2 + 1];
}
switch (direction) {
case LEFT:
snakeCoordinates[0] -= size[selectedSize];
break;
case RIGHT:
snakeCoordinates[0] += size[selectedSize];
break;
case UP:
snakeCoordinates[1] -= size[selectedSize];
break;
case DOWN:
snakeCoordinates[1] += size[selectedSize];
break;
}
} else {
gameState = GAME_OVER;
}
oled.display();
delay(velocity[selectedVelocity]);
}
//Játék vége
void overGame() {
drawGameOver();
if (digitalRead(CENTER) == LOW) {
state = 0;
first = true;
setupGame();
lcd.clear();
lcd.print("WELCOME TO SNAKE");
}
}
//Játék vége kirajzolása
void drawGameOver() {
oled.clearDisplay();
oled.setTextSize(2.5);
oled.setTextColor(WHITE);
oled.setCursor(10, 15);
oled.println("GAME OVER");
oled.setTextSize(1);
oled.setCursor(1, 45);
oled.println("Press the joystick to");
oled.setCursor(8, 55);
oled.println("return to the menu!");
oled.display();
}
//Játék vége győzelemmel
void winGame() {
drawWinGame();
if (digitalRead(CENTER) == LOW) {
state = 0;
setupGame();
lcd.clear();
lcd.print("WELCOME TO SNAKE");
}
}
//Győzelem kirajzolása
void drawWinGame() {
oled.clearDisplay();
oled.setTextSize(2);
oled.setTextColor(WHITE);
oled.setCursor(28, 15);
oled.println("WINNER");
oled.setTextSize(1);
oled.setCursor(1, 45);
oled.println("Press the joystick to");
oled.setCursor(8, 55);
oled.println("return to the menu!");
oled.display();
}
//Irány beállítása
void setDirection(int x, int y) {
if (x > 612 && direction != RIGHT) {
direction = LEFT;
} else if (x < 412 && direction != LEFT) {
direction = RIGHT;
} else if (y > 612 && direction != DOWN) {
direction = UP;
} else if (y < 412 && direction != UP) {
direction = DOWN;
}
}
//Kígyó kirajzolása
void drawSnake() {
for (int i = 0; i < length; i++) {
drawSnakePixel(snakeCoordinates[i * 2], snakeCoordinates[i * 2 + 1]);
}
}
//Kigyó egy pixelének kirajzolása
void drawSnakePixel(byte x, byte y) {
for (int i = 0; i < size[selectedSize]; i++) {
for (int j = 0; j < size[selectedSize]; j++) {
oled.drawPixel(x + j, y + i, 1);
}
}
}
//Kígyó megölése
bool killSnake() {
if ((getSnakeHeadX() <= 0) || (getSnakeHeadX() >= (SCREEN_WIDTH - size[selectedSize])) || (getSnakeHeadY() <= 0) || (getSnakeHeadY() >= (SCREEN_HEIGHT - size[selectedSize]))) {
return true;
}
for (int i = 1; i < length; i++) {
if (getSnakeHeadX() == snakeCoordinates[i * 2] && getSnakeHeadY() == snakeCoordinates[i * 2 + 1]) {
return true;
}
}
return false;
}
//Snake fejének lekérdezése (könnyebb átláthatóság végett)
byte getSnakeHeadX() {
return snakeCoordinates[0];
}
byte getSnakeHeadY() {
return snakeCoordinates[1];
}
//Food függvények
//Food generálása
void generateFood() {
byte row, column, x, y;
bool out = true;
do {
row = random(0, (SCREEN_WIDTH / size[selectedSize]) - size[selectedSize]);
column = random(0, (SCREEN_HEIGHT / size[selectedSize]) - size[selectedSize]);
x = row * size[selectedSize];
y = column * size[selectedSize];
for (int i = 0; i < length; i++) {
if (x == snakeCoordinates[i * 2] && y == snakeCoordinates[i * 2 + 1]) {
out = false;
}
}
} while (!out);
foodCoordinates[0] = x;
foodCoordinates[1] = y;
}
//Food kirajzolása
void drawFood() {
drawSnakePixel(foodCoordinates[0], foodCoordinates[1]);
}
//Food megevése
void eatFood() {
score++;
length++;
snakeCoordinates = (byte*)realloc(snakeCoordinates, (length * 2) * sizeof(byte));
lcd.clear();
lcd.print("SCORE: ");
lcd.print(score);
generateFood();
}
//Fooddal való érintkezés
bool hitFood() {
if (getSnakeHeadX() == foodCoordinates[0] && getSnakeHeadY() == foodCoordinates[1]) {
tone(BUZZER, 170, 100);
return true;
}
return false;
}
//--Menüpont függvények--//
//Menüpont kiválasztása
void selectMenuOption() {
if (first) {
delay(200);
first = false;
}
int vertical = analogRead(VERTICAL);
if (vertical < 412) {
if (option < (optionsCount - 1)) {
option++;
} else {
option = 0;
}
delay(250);
} else if (vertical > 612) {
if (option > 0) {
option--;
} else {
option = (optionsCount - 1);
}
delay(250);
}
if (digitalRead(CENTER) == LOW) {
first = true;
state = option + 1;
}
}
//Menü kirajzolása
void drawMenu() {
oled.clearDisplay();
oled.setTextColor(WHITE);
oled.setTextSize(1);
oled.setCursor(15, 5);
oled.println("SNAKE Remastered");
oled.setTextSize(1);
for (int i = 0; i < optionsCount; i++) {
if (option == i) {
drawHoveredMenuOption(options[i], optionsStartInHover[i], 25 + (i * 12));
} else {
drawMenuOption(options[i], optionsStartInHover[i], 25 + (i * 12));
}
}
oled.display();
}
//Menüpont kirajzolása (alap, kijelölt)
void drawMenuOption(char* option, byte cursorX, byte cursorY) {
oled.setTextColor(WHITE);
oled.setCursor(cursorX, cursorY);
oled.println(option);
}
void drawHoveredMenuOption(char* option, byte cursorX, byte cursorY) {
for (int i = 0; i < SCREEN_WIDTH/2; i++) {
for (int j = 0; j < 10; j++) {
oled.drawPixel(SCREEN_WIDTH/4 + i, cursorY - 1 + j, WHITE);
}
}
oled.setTextColor(BLACK);
oled.setCursor(cursorX, cursorY);
oled.println(option);
}
//Beállítások
//Beállítások kirajzolása
void drawSettings() {
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(WHITE);
oled.setCursor(40, 5);
oled.println("Settings");
for (int i = 0; i < settingsCount; i++) {
if (selectedSetting == i) {
drawHoveredSetting(i, 5, 20 + (i * 10));
} else {
drawSetting(i, 5, 20 + (i * 10));
}
}
oled.setTextColor(WHITE);
oled.setCursor(5, 43);
oled.println("Press the middle to");
oled.setCursor(45, 53);
oled.println("return");
oled.display();
}
//Beállítás kiválasztása
void selectSetting() {
if (first) {
delay(200);
first = false;
}
int vertical = analogRead(VERTICAL);
if (vertical < 412) {
if (selectedSetting < (settingsCount - 1)) {
selectedSetting++;
} else {
selectedSetting = 0;
}
delay(250);
} else if (vertical > 612) {
if (selectedSetting > 0) {
selectedSetting--;
} else {
selectedSetting = (settingsCount - 1);
}
delay(250);
}
int horizontal = analogRead(HORIZONTAL);
if (horizontal < 412) {
if (selectedSetting == 0) {
if (selectedSize < (sizeCount - 1)) {
selectedSize++;
} else {
selectedSize = 0;
}
} else {
if (selectedVelocity < (velocityCount - 1)) {
selectedVelocity++;
} else {
selectedVelocity = 0;
}
}
delay(250);
} else if (horizontal > 612) {
if (selectedSetting == 0) {
if (selectedSize > 0) {
selectedSize--;
} else {
selectedSize = (sizeCount - 1);
}
} else {
if (selectedVelocity > 0) {
selectedVelocity--;
} else {
selectedVelocity = (velocityCount - 1);
}
}
delay(250);
}
if (digitalRead(CENTER) == LOW) {
setupGame();
state = 0;
first = true;
}
}
//Beállítás kirajzolása (alap, kijelölt)
void drawSetting(byte setting, byte cursorX, byte cursorY) {
oled.setTextColor(WHITE);
oled.setCursor(cursorX, cursorY);
oled.println(settings[setting]);
oled.setCursor(cursorX + 80, cursorY);
if (setting == 0) {
oled.println(size[selectedSize]);
} else {
oled.println(velocity[selectedVelocity]);
}
}
void drawHoveredSetting(byte setting, byte cursorX, byte cursorY) {
for (int i = 0; i < SCREEN_WIDTH; i++) {
for (int j = 0; j < 10; j++) {
oled.drawPixel(i, cursorY - 1 + j, WHITE);
}
}
oled.setTextColor(BLACK);
oled.setCursor(cursorX, cursorY);
oled.println(settings[setting]);
oled.setCursor(cursorX + 80, cursorY);
if (setting == 0) {
oled.println(size[selectedSize]);
} else {
oled.println(velocity[selectedVelocity]);
}
}
//Információ
//Információ kirajzolása
void drawInformation() {
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(WHITE);
oled.setCursor(30, 5);
oled.println("Created by:");
oled.setCursor(30, 15);
oled.println("Balazs Nagy");
oled.setCursor(40, 25);
oled.println("XM6PZH");
oled.setCursor(5, 43);
oled.println("Press the middle to");
oled.setCursor(45, 53);
oled.println("return");
oled.display();
}
//Visszatérés az információból a menübe
void returnFromInformation() {
if (first) {
delay(200);
first = false;
}
if (digitalRead(CENTER) == LOW) {
state = 0;
first = true;
}
}