#include <FastLED.h>
#include "Snake.h"
#define NUM_LEDS 135
#define LEDS_PER_ROW 15
#define DATA_PIN 6
#define SPEAKER_PIN 8
#define BTN_LEFT 2
#define BTN_RIGHT 4
#define BTN_UP 5
#define BTN_DOWN 3
#define NOTE_G3 196
#define NOTE_C4 262
#define NOTE_C5 523
#define NOTE_E4 330
#define NOTE_G4 392
#define NOTE_G5 784
// Initialize the snake field with x=15, y=9, delay=10 ticks
Snake snakeGame(15, 9, 10);
CRGB leds[NUM_LEDS];
unsigned long timerTone = 0;
unsigned long timerReset = 0;
unsigned long timerRefresh = 0;
unsigned long timerMoveTone = 0;
unsigned long timerSnakeRose = 0;
bool isMoveTone = false;
bool isGameReset = false;
bool isMoveToneStop = false;
bool isSnakeRose = false;
bool isSnakeRoseEnd = false;
int lastSnakeLenght = 0;
uint8_t direction = 0;
uint8_t lastDirection = 0;
void setup() {
pinMode(BTN_LEFT, INPUT_PULLUP);
pinMode(BTN_RIGHT, INPUT_PULLUP);
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
snakeGame.setBodyColor(255, 0, 255); // Optionally set the color of the snakeparts
snakeGame.setFoodColor(0, 60, 125); // Optionally set the color of the food
snakeGame.setHeadColor(225, 20, 60); // Optionally set the color of the snakeparts
lastSnakeLenght = snakeGame.getSnakeLenght();
delay(1000);
FastLED.addLeds<WS2812, DATA_PIN, GRB>(leds, NUM_LEDS);
}
void inputEvent() {
if (digitalRead(BTN_LEFT) == LOW) {
snakeGame.goLeft(); // Snake will go left on the next move
isMoveTone = true;
direction = LEFT;
}
if (digitalRead(BTN_RIGHT) == LOW) {
snakeGame.goRight(); // Snake will go right on the next move
isMoveTone = true;
direction = RIGHT;
}
if (digitalRead(BTN_UP) == LOW) {
snakeGame.goUp(); // Snake will go up on the next move
isMoveTone = true;
direction = UP;
}
if (digitalRead(BTN_DOWN) == LOW) {
snakeGame.goDown(); // Snake will go down on the next move
isMoveTone = true;
direction = DOWN;
}
// Checks whether the snake has changed position
if (isMoveTone && (direction != lastDirection)) {
isMoveTone = false;
isMoveToneStop = true;
lastDirection = direction;
tone(SPEAKER_PIN, NOTE_G4);
timerMoveTone = millis();
}
if ((isMoveToneStop) && ((millis() - timerMoveTone) >= 30)) {
isMoveToneStop = false;
noTone(SPEAKER_PIN);
}
}
byte setPixel(byte x, byte y, byte r, byte g, byte b)
{
byte ledID = NUM_LEDS - (y * LEDS_PER_ROW) - x - 1 ;
leds[ledID].setRGB(r, g, b);
return ledID;
}
void changeRGBtoGBR()
{
for (int whiteLed = 0; whiteLed < NUM_LEDS; whiteLed++)
{
leds[whiteLed].setRGB(leds[whiteLed].g, leds[whiteLed].b, leds[whiteLed].r);
}
}
void clearScreen()
{
for (int whiteLed = 0; whiteLed < NUM_LEDS; whiteLed++)
{
leds[whiteLed].setRGB( 3, 3, 3);
}
}
void loop()
{
inputEvent();
if (!snakeGame.wasGameReset() && (millis() - timerRefresh > 30)) {
timerRefresh = millis();
// This needs to be updated every frame
Snake::pixel* snakeLimbs = snakeGame.getSnakeLimbs();
Snake::pixel* snakeFood = snakeGame.getFoodPositions();
clearScreen();
setPixel(snakeFood[0].posX, 8 - snakeFood[0].posY, snakeFood[0].pixelColor.r, snakeFood[0].pixelColor.g, snakeFood[0].pixelColor.b); // display the food
for (int i = 0; i < snakeGame.getSnakeLenght(); i++)
{
// Display the snake, my setpixel method has x=0, y=0 at the top left, but the library has it at bottom left, so I invert the Y-Axis:
setPixel(snakeLimbs[i].posX, 8 - snakeLimbs[i].posY, snakeLimbs[i].pixelColor.r, snakeLimbs[i].pixelColor.g, snakeLimbs[i].pixelColor.b);
}
if (lastSnakeLenght != snakeGame.getSnakeLenght()) {
lastSnakeLenght = snakeGame.getSnakeLenght();
isSnakeRose = true;
tone(SPEAKER_PIN, NOTE_G4);
timerSnakeRose = millis();
}
FastLED.show();
snakeGame.tick(); // Main loop for the snake library
}
if ((isSnakeRose) && (millis() - timerSnakeRose) >= 30) {
isSnakeRose = false;
tone(SPEAKER_PIN, NOTE_G5);
isSnakeRoseEnd = true;
timerSnakeRose = millis();
}
if ((isSnakeRoseEnd) && (millis() - timerSnakeRose) >= 30) {
isSnakeRoseEnd = false;
noTone(SPEAKER_PIN);
}
// If the snake bit itself or the wall, flash a little
if (snakeGame.wasGameReset()) {
changeRGBtoGBR();
FastLED.show();
isGameReset = true;
tone(SPEAKER_PIN, NOTE_G3);
timerReset = millis();
}
if ((isGameReset) && (millis() - timerReset) >= 200) {
isGameReset = false;
noTone(SPEAKER_PIN);
}
}