#include <math.h>
#include <SPI.h>
#include <time.h>
#include <stdlib.h>
#include "pitches.h"
// MAX7219 Control registers
#define DECODE_MODE 9
#define INTENSITY 0x0A
#define SCAN_LIMIT 0x0B
#define SHUTDOWN 0x0C
#define DISPLAY_TEST 0x0F
#define BUZZER_PIN 9
static const int defaultIntensity = 0x08;
int Buttons[4] = {6, 5, 4, 3};
int save_buttons_values[4] = {0};
int loadPin = 7;
int clockPin = 13;
int dataPin = 11;
int k = 0;
int increase_snake = 0;
int have_food = 0;
typedef struct Head {
int positionX;
int positionY;
int speedX;
int speedY;
int lenght;
} Head;
Head head;
int matrix[8][8] = {0};
void softwareSPI(uint8_t A, uint8_t B) {
digitalWrite(loadPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, A);
shiftOut(dataPin, clockPin, MSBFIRST, B);
digitalWrite(loadPin, HIGH);
}
void setup() {
Serial.begin(115200);
randomSeed(analogRead(0));
pinMode(BUZZER_PIN, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
pinMode(loadPin, OUTPUT);
softwareSPI(DISPLAY_TEST, 0x01); // Run test - All LED segments lit.
delay(1000);
softwareSPI(DISPLAY_TEST, 0x00); // Finish test mode.
softwareSPI(DECODE_MODE, 0x00); // Disable BCD mode.
softwareSPI(INTENSITY, 0x0e); // Use lowest intensity.
softwareSPI(SCAN_LIMIT, 0x0f); // Scan all digits.
softwareSPI(SHUTDOWN, 0x01); // Turn on chip.
initSnake();
}
void initSnake() {
head.positionX = 4;
head.positionY = 4;
head.speedX = 1;
head.speedY = 0;
head.lenght = 4;
for (int i = 0; i < 8; i ++) {
for (int j = 0; j < 8; j++) {
matrix[i][j] = 0;
}
}
matrix[4][4] = 4;
matrix[3][4] = 3;
matrix[2][4] = 2;
matrix[1][4] = 1;
}
void change_speed(int buttonPin) {
switch (buttonPin) {
case 6:
if (head.speedY != -1) {
head.speedY = 1;
head.speedX = 0;
}
break;
case 5:
if (head.speedX != -1) {
head.speedX = 1;
head.speedY = 0;
}
break;
case 4:
if (head.speedY != 1) {
head.speedY = -1;
head.speedX = 0;
}
break;
case 3:
if (head.speedX != 1) {
head.speedX = -1;
head.speedY = 0;
}
break;
default:
break;
}
}
int move() {
head.positionX += head.speedX;
if (head.positionX > 7) {
head.positionX = 0;
}
else if (head.positionX < 0) {
head.positionX = 7;
}
head.positionY += head.speedY;
if (head.positionY > 7) {
head.positionY = 0;
}
else if (head.positionY < 0) {
head.positionY = 7;
}
if (matrix[head.positionY][head.positionX] == -1) {
head.lenght++;
matrix[head.positionY][head.positionX] = head.lenght;
increase_snake = 1;
have_food = 0;
tone(BUZZER_PIN, 40);
delay(30);
noTone(BUZZER_PIN);
}
else if (matrix[head.positionY][head.positionX] > 0) {
return -1;
}
else {
matrix[head.positionY][head.positionX] = head.lenght + 1;
}
for (int i = 0; i < 8; i ++) {
for (int j = 0; j < 8; j ++) {
if (matrix[i][j] > 0) {
if (increase_snake == 1) {
matrix[i][j]++;
}
matrix[i][j]--;
}
}
}
increase_snake = 0;
return 0;
}
void check_buttons() {
for (int i = 0; i < 4; i ++) {
int val = digitalRead(Buttons[i]);
if (val == 1 && save_buttons_values[i] == 0) {
change_speed(Buttons[i]);
}
save_buttons_values[i] = val;
}
}
void food_appearence() {
int x = 0;
int y = 0;
bool run = true;
srand(time(NULL));
while (run) {
x = random(0, 7);
y = random(0, 7);
Serial.println(x);
Serial.println(y);
if (matrix[y][x] == 0) {
matrix[y][x] = -1;
run = false;
have_food = 1;
}
}
}
void printSnake(int flag) {
for (int i = 0; i < 8; i ++) {
int8_t data = 0;
for (int j = 0; j < 8; j ++) {
if (matrix[i][j] != 0) {
data += 1 << j;
}
}
if (flag == 1) softwareSPI(i + 1, data);
else softwareSPI(i + 1, 0);
}
}
int melody[] = {
NOTE_B4, NOTE_B5, NOTE_FS5, NOTE_DS5,
NOTE_B5, NOTE_FS5, NOTE_DS5, NOTE_C5,
NOTE_C6, NOTE_G6, NOTE_E6, NOTE_C6, NOTE_G6, NOTE_E6,
NOTE_B4, NOTE_B5, NOTE_FS5, NOTE_DS5, NOTE_B5,
NOTE_FS5, NOTE_DS5, NOTE_DS5, NOTE_E5, NOTE_F5,
NOTE_F5, NOTE_FS5, NOTE_G5, NOTE_G5, NOTE_GS5, NOTE_A5, NOTE_B5
};
int durations[] = {
16, 16, 16, 16,
32, 16, 8, 16,
16, 16, 16, 32, 16, 8,
16, 16, 16, 16, 32,
16, 8, 32, 32, 32,
32, 32, 32, 32, 32, 16, 8
};
void play_melody() {
int size = sizeof(durations) / sizeof(int);
for (int note = 0; note < size; note++) {
//to calculate the note duration, take one second divided by the note type.
//e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
int duration = 1000 / durations[note];
tone(BUZZER_PIN, melody[note], duration);
//to distinguish the notes, set a minimum time between them.
//the note's duration + 30% seems to work well:
int pauseBetweenNotes = duration * 1.30;
delay(pauseBetweenNotes);
//stop the tone playing:
noTone(BUZZER_PIN);
}
}
void restart() {
play_melody();
initSnake();
have_food = 0;
}
void game_over() {
for (int i = 0; i < 3; i ++) {
printSnake(1);
tone(BUZZER_PIN, 100);
delay(300);
noTone(BUZZER_PIN);
printSnake(0);
delay(300);
}
}
long prevTime = 0;
void loop() {
check_buttons();
if (have_food == 0) {
food_appearence();
}
if(millis() - prevTime > constrain(1000 - (100 * head.lenght), 100, 1000)) {
prevTime = millis();
if (move() == -1) {
game_over();
restart();
}
tone(BUZZER_PIN, 50);
printSnake(1);
} else {
noTone(BUZZER_PIN);
}
}