#pragma once
int dataPin = 12;
int clockPin = 11;
int csPin = 10;
typedef enum MaxRegister {
scanLimit = 0x0B,
intensity = 0x0A,
decode = 0x09,
shotDown = 0x0c
} MaxRegister;
void softwareSPI(uint8_t A, uint8_t B) {
digitalWrite(csPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, A);
shiftOut(dataPin, clockPin, MSBFIRST, B);
digitalWrite(csPin, HIGH);
}
void displayClear() {
for (int i = 0; i < 8; i++) {
softwareSPI(i, 0);
}
}
void displayInit() {
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(csPin, OUTPUT);
digitalWrite(csPin, HIGH);
// Выставляем scanLimit на максимум (таблица 8) - это количество колонок матрицы по умолчанию горела бы только одна колонка
softwareSPI(MaxRegister::scanLimit, 0x07);
// Выставляем intensity согласно (таблице 7)
softwareSPI(MaxRegister::intensity, 0x0F);
// отключаем decode-mode, если он вдруг включен (таблица 4)
softwareSPI(MaxRegister::decode, 0x00);
// Очищаем дисплей. Документация про это умалчивает, но в SRAM при запуске лежит мусор
displayClear();
// теперь выходим в рабочий режим (таблица 3) и рисуем что хотим.
softwareSPI(MaxRegister::shotDown, 0x01);
}
byte leds[8] = {0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00010000, 0b00100000, 0b01000000, 0b10000000};
byte strings[8] = {0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000};
struct Button {
int current = 0;
int previous = 0;
};
struct Button buttons[4];
struct BodyPart {
int x;
int y;
};
void moveBodyPart(struct BodyPart *bodyPart, int dx, int dy) {
bodyPart->x = bodyPart->x + dx;
bodyPart->y = bodyPart->y + dy;
if (bodyPart->x == 0) {
bodyPart->x = 8;
}
if (bodyPart->x == 9) {
bodyPart->x = 1;
}
if (bodyPart->y == 0) {
bodyPart->y = 8;
}
if (bodyPart->y == 9) {
bodyPart->y = 1;
}
}
struct Snake {
struct BodyPart body[64];
int size = 0;
};
void moveSnake(struct Snake *snake, int direction) {
for (int i = snake->size - 1; i >= 1; i--) {
snake->body[i].x = snake->body[i - 1].x;
snake->body[i].y = snake->body[i - 1].y;
}
switch (direction) {
case 1:
moveBodyPart(&snake->body[0], 0, -1);
break;
case 2:
moveBodyPart(&snake->body[0], 0, 1);
break;
case 3:
moveBodyPart(&snake->body[0], -1, 0);
break;
case 4:
moveBodyPart(&snake->body[0], 1, 0);
break;
}
}
void drawSnake(struct Snake snake) {
for (int i = 0; i < snake.size; i++) {
high(snake.body[i].x, snake.body[i].y);
}
}
boolean loseChecker(struct Snake snake, boolean *gameOverStatus){
for (int i = 1; i < snake.size; i++) {
if (snake.body[0].x == snake.body[i].x && snake.body[0].y == snake.body[i].y) {
*gameOverStatus = true;
break;
}
}
}
void generateFood(struct BodyPart *food, struct Snake snake) {
while (true) {
food->x = (rand() % 8) + 1;
food->y = (rand() % 8) + 1;
boolean foodStatus = true;
for (int i = 0; i < snake.size; i++) {
if (food->x == snake.body[i].x && food->y == snake.body[i].y) {
foodStatus = false;
break;
}
}
if (foodStatus == true) {
break;
}
}
}
void foodHandler(struct Snake *snake, struct BodyPart *food) {
if (snake->body[0].x == food->x && snake->body[0].y == food->y) {
snake->body[snake->size] = {-1, -1};
snake->size = snake->size + 1;
generateFood(food, *snake);
}
}
void drawFood(struct BodyPart food) {
high(food.x, food.y);
}
void gameOver() {
for (int i = 1; i <= 8; i++) {
for (int j = 1; j <= 8; j++) {
high(i, j);
}
}
while (true) {
tone(9, 100, 109);
}
}
void myClearDisplay() {
for (int i = 0; i < 8; i++) {
softwareSPI(i, 0);
strings[i] = strings[i] & 0b00000000;
}
softwareSPI(8, 0);
}
void controller(int *direction) {
buttons[0].current = digitalRead(3);
if (buttons[0].current && buttons[0].previous == 0) {
if (*direction != 2) {
*direction = 1;
}
}
buttons[1].current = digitalRead(4);
if (buttons[1].current && buttons[1].previous == 0) {
if (*direction != 1) {
*direction = 2;
}
}
buttons[2].current = digitalRead(2);
if (buttons[2].current && buttons[2].previous == 0) {
if (*direction != 4) {
*direction = 3;
}
}
buttons[3].current = digitalRead(5);
if (buttons[3].current && buttons[3].previous == 0) {
if (*direction != 3) {
*direction = 4;
}
}
buttons[0].previous = buttons[0].current;
buttons[1].previous = buttons[1].current;
buttons[2].previous = buttons[2].current;
buttons[3].previous = buttons[3].current;
}
void high(int row, int col) {
strings[row - 1] = strings[row - 1] | leds[col - 1];
softwareSPI(row, strings[row - 1]);
}
struct Snake snake;
struct BodyPart food;
int direction = 1;
boolean gameOverStatus = false;
int frequency = 200;
unsigned long prevTime = 0;
void setup() {
displayInit();
pinMode(2, INPUT); // up button
pinMode(3, INPUT); // left button
pinMode(4, INPUT); // right button
pinMode(5, INPUT); // down button
pinMode(9, OUTPUT); // buzzer
snake.body[0] = {4, 4};
snake.size = 1;
food = {0, 0};
generateFood(&food, snake);
}
void loop() {
controller(&direction);
if (millis() - prevTime > frequency) {
prevTime = millis();
if (gameOverStatus) {
gameOver();
}
foodHandler(&snake, &food);
moveSnake(&snake, direction);
loseChecker(snake, &gameOverStatus);
myClearDisplay();
drawFood(food);
drawSnake(snake);
}
}