#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// Настройки дисплея
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// Пины кнопок
const int upButton = 2;
const int rightButton = 3;
const int downButton = 4;
const int leftButton = 5;
// Настройки игры
const int pixelSize = 4; // Размер сегмента змейки в пикселях
const int gridWidth = SCREEN_WIDTH / pixelSize;
const int gridHeight = SCREEN_HEIGHT / pixelSize;
struct Point {
int x, y;
};
Point snake[256]; // Массив сегментов змейки (максимум 256 сегментов)
int snakeLength = 3;
Point food;
int dx = 1, dy = 0; // Направление движения (по умолчанию — вправо)
bool gameOver = false;
unsigned long lastMoveTime = 0;
const long moveInterval = 150; // Интервал движения в мс
void setup() {
Serial.begin(9600);
// Инициализация дисплея
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Бесконечный цикл при ошибке
}
display.display();
delay(2000);
display.clearDisplay();
// Настройка пинов кнопок
pinMode(upButton, INPUT_PULLUP);
pinMode(rightButton, INPUT_PULLUP);
pinMode(downButton, INPUT_PULLUP);
pinMode(leftButton, INPUT_PULLUP);
initializeGame();
}
void initializeGame() {
// Начальная позиция змейки
snake[0] = {gridWidth / 2, gridHeight / 2};
for (int i = 1; i < snakeLength; i++) {
snake[i] = {snake[i - 1].x - 1, snake[i - 1].y};
}
generateFood();
gameOver = false;
dx = 1; dy = 0;
lastMoveTime = millis();
}
void generateFood() {
food.x = random(0, gridWidth);
food.y = random(0, gridHeight);
// Проверка, что еда не на змейке
for (int i = 0; i < snakeLength; i++) {
if (food.x == snake[i].x && food.y == snake[i].y) {
generateFood(); // Рекурсивный вызов при наложении
return;
}
}
}
void readInput() {
if (digitalRead(upButton) == LOW && dy != 1) { // Нельзя двигаться вниз, если движемся вверх
dx = 0; dy = -1;
} else if (digitalRead(rightButton) == LOW && dx != -1) {
dx = 1; dy = 0;
} else if (digitalRead(downButton) == LOW && dy != -1) {
dx = 0; dy = 1;
} else if (digitalRead(leftButton) == LOW && dx != 1) {
dx = -1; dy = 0;
}
}
void updateGame() {
// Движение змейки: каждый сегмент следует за предыдущим
for (int i = snakeLength - 1; i > 0; i--) {
snake[i] = snake[i - 1];
}
// Новая позиция головы
snake[0].x += dx;
snake[0].y += dy;
// Проверка границ (телепортация на противоположную сторону)
if (snake[0].x < 0) snake[0].x = gridWidth - 1;
if (snake[0].x >= gridWidth) snake[0].x = 0;
if (snake[0].y < 0) snake[0].y = gridHeight - 1;
if (snake[0].y >= gridHeight) snake[0].y = 0;
// Проверка столкновения с собой
for (int i = 1; i < snakeLength; i++) {
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
gameOver = true;
return;
}
}
// Проверка поедания еды
if (snake[0].x == food.x && snake[0].y == food.y) {
snakeLength++;
generateFood();
}
}
void drawGame() {
display.clearDisplay();
// Отрисовка змейки
for (int i = 0; i < snakeLength; i++) {
display.fillRect(snake[i].x * pixelSize, snake[i].y * pixelSize,
pixelSize, pixelSize, SSD1306_WHITE);
}
// Отрисовка еды
display.fillRect(food.x * pixelSize, food.y * pixelSize,
pixelSize, pixelSize, SSD1306_WHITE);
// Отображение счёта
display.setTextSize(1);
display.setCursor(0, 0);
display.print("Score: ");
display.print(snakeLength - 3);
if (gameOver) {
display.setTextSize(1);
display.setCursor(30, 30);
display.print("GAME OVER");
}
display.display();
}
void loop() {
unsigned long currentTime = millis();
readInput();
if (!gameOver && currentTime - lastMoveTime >= moveInterval) {
updateGame();
lastMoveTime = currentTime;
}
drawGame();
// Перезапуск при проигрыше по нажатию любой кнопки
if (gameOver &&
(digitalRead(upButton) == LOW ||
digitalRead(rightButton) == LOW ||
digitalRead(downButton) == LOW ||
digitalRead(leftButton) == LOW)) {
initializeGame();
}
delay(10); // Небольшая задержка для стабильности
}