#include <LedControl.h>
#define LED_CLK 13
#define LED_CS 12
#define LED_DATA 11
#define STATE_START 0
#define STATE_GLOOP 1
#define STATE_GOVER 2
#define JOY_X A0
#define JOY_Y A1
#define JOY_BTN 10
#define MAX_LENGTH 64
int gameState = STATE_START;
int snakeX[MAX_LENGTH];
int snakeY[MAX_LENGTH];
int snakeLength = 3; // starting length
int score = 0;
char score_txt[17] = {0};
int dx = 1; // start moving right
int dy = 0;
int dxr = 0;
int dyr = 1;
int x = 0;
int y = 0;
unsigned long lastMove = 0;
const int speed = 200; // move every 200 ms
int foodX = random(16);
int foodY = random(16);
const int thresholdLow = 400;
const int thresholdHigh = 600;
LedControl lc = LedControl(LED_DATA, LED_CLK, LED_CS, 4);
void setPixel(int x, int y, boolean state) {
int device, localX, localY;
if (x < 8 && y < 8) {
device = 1; // top-left physical
localX = x;
localY = y;
} else if (x >= 8 && y < 8) {
device = 0; // top-right physical
localX = x - 8;
localY = y;
} else if (x < 8 && y >= 8) {
device = 3; // bottom-left physical
localX = x;
localY = y - 8;
} else {
device = 2; // bottom-right physical
localX = x - 8;
localY = y - 8;
}
// Apply mirroring if needed (you might need to test these!)
localX = 7 - localX; // horizontal flip
// localY = 7 - localY; // uncomment if vertical flip needed
lc.setLed(device, localY, localX, state);
}
void handleInput(int xVal, int yVal) {
if (xVal < thresholdLow && dx != -1) {
dxr = 1; dyr = 0;
}
if (xVal > thresholdHigh && dx != 1) {
dxr = -1; dyr = 0;
}
if (yVal < thresholdLow && dy != -1) {
dxr = 0; dyr = 1;
}
if (yVal > thresholdHigh && dy != 1) {
dxr = 0; dyr = -1;
}
}
void showStartScreen() {
static bool toggle = false;
static unsigned long lastBlink = 0;
if (millis() - lastBlink > 500) { // blink every 500 ms
lastBlink = millis();
toggle = !toggle;
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) {
if ((x + y) % 2 == (toggle ? 0 : 1)) {
setPixel(x, y, true);
} else {
setPixel(x, y, false);
}
}
}
}
}
void startGame() {
snakeLength = 3;
snakeX[0] = 8; snakeY[0] = 8;
snakeX[1] = 7; snakeY[1] = 8;
snakeX[2] = 6; snakeY[2] = 8;
dx = 1; dy = 0;
foodX = random(16);
foodY = random(16);
lc.clearDisplay(0);
lc.clearDisplay(1);
lc.clearDisplay(2);
lc.clearDisplay(3);
gameState = STATE_GLOOP;
setPixel(foodX, foodY, true);
}
void updateGame() {
if (millis() - lastMove < speed) {
return; // too soon, wait
}
dx = dxr;
dy = dyr;
lastMove = millis();
bool ateFood = (snakeX[0] + dx == foodX && snakeY[0] + dy == foodY);
// Shift body forward
if (!ateFood) {
// Erase tail on display
setPixel(snakeX[snakeLength - 1], snakeY[snakeLength - 1], false);
for (int i = snakeLength - 1; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
} else {
// Growing: shift and increase length
for (int i = snakeLength; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
++snakeLength;
++score;
if (snakeLength > MAX_LENGTH) snakeLength = MAX_LENGTH;
}
// Add new head position
snakeX[0] += dx;
snakeY[0] += dy;
// Wrap edges
if (snakeX[0] < 0) snakeX[0] = 15;
if (snakeX[0] > 15) snakeX[0] = 0;
if (snakeY[0] < 0) snakeY[0] = 15;
if (snakeY[0] > 15) snakeY[0] = 0;
// Check for self-collision
for (int i = 1; i < snakeLength; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
gameState = STATE_GOVER;
return;
}
}
// Draw new head
setPixel(snakeX[0], snakeY[0], true);
// Check if ate food and place new food
if (ateFood) {
bool valid;
do {
foodX = random(16);
foodY = random(16);
valid = true;
for (int i = 0; i < snakeLength; i++) {
if (snakeX[i] == foodX && snakeY[i] == foodY) {
valid = false;
break;
}
}
} while (!valid);
// Draw food
setPixel(foodX, foodY, true);
}
}
void showGameOverScreen() {
for (int y = 0; y < 16; y++) {
for (int x = 0; x < 16; x++) {
setPixel(x, y, true);
}
}
}
void setup() {
gameState = STATE_START;
pinMode( JOY_BTN, INPUT_PULLUP );
for (int i = 0; i < 4; i++) {
lc.shutdown(i, false); // Wake up MAX7221
lc.setIntensity(i, 8); // Set brightness (0–15)
lc.clearDisplay(i); // Clear display
}
// Initialize snake in center
snakeX[0] = 8; snakeY[0] = 8;
snakeX[1] = 7; snakeY[1] = 8;
snakeX[2] = 6; snakeY[2] = 8;
}
void loop() {
int xVal = analogRead(JOY_X);
int yVal = analogRead(JOY_Y);
bool buttonPressed = (digitalRead(JOY_BTN) == LOW);
switch (gameState) {
case STATE_START:
// Show welcome / ready screen
if (buttonPressed) {
startGame();
} else {
showStartScreen();
}
break;
case STATE_GLOOP:
handleInput(xVal, yVal);
updateGame();
break;
case STATE_GOVER:
// Show game over screen
if (buttonPressed) {
gameState = STATE_START;
} else {
showGameOverScreen();
}
break;
}
}
/*
void loop() {
int xVal = analogRead(JOY_X);
int yVal = analogRead(JOY_Y);
// Horizontal
if (xVal < thresholdLow && dx != -1) {
dx = 1; dy = 0; // Left
}
if (xVal > thresholdHigh && dx != 1) {
dx = -1; dy = 0; // Right
}
// Vertical
if (yVal < thresholdLow && dy != -1) {
dx = 0; dy = 1; // Up
}
if (yVal > thresholdHigh && dy != 1) {
dx = 0; dy = -1; // Down
}
if (millis() - lastMove > speed) {
lastMove = millis();
// Check if eating food
bool ateFood = (snakeX[0] + dx == foodX && snakeY[0] + dy == foodY);
// Shift body (only if not growing)
if (!ateFood) {
setPixel(snakeX[snakeLength - 1], snakeY[snakeLength - 1], false);
for (int i = snakeLength - 1; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
} else {
// Growing → shift all forward and extend length
for (int i = snakeLength; i > 0; i--) {
snakeX[i] = snakeX[i - 1];
snakeY[i] = snakeY[i - 1];
}
snakeLength++;
if (snakeLength >= MAX_LENGTH) snakeLength = MAX_LENGTH;
}
// Add new head
snakeX[0] += dx;
snakeY[0] += dy;
// Wrap edges
if (snakeX[0] < 0) snakeX[0] = 15;
if (snakeX[0] > 15) snakeX[0] = 0;
if (snakeY[0] < 0) snakeY[0] = 15;
if (snakeY[0] > 15) snakeY[0] = 0;
// Check collision (with self)
for (int i = 1; i < snakeLength; i++) {
if (snakeX[0] == snakeX[i] && snakeY[0] == snakeY[i]) {
// Game over → reset
snakeLength = 3;
snakeX[0] = 8; snakeY[0] = 8;
snakeX[1] = 7; snakeY[1] = 8;
snakeX[2] = 6; snakeY[2] = 8;
}
}
// Draw snake
for (int i = 0; i < snakeLength; i++) {
setPixel(snakeX[i], snakeY[i], true);
}
// Draw food
setPixel(foodX, foodY, true);
// Spawn new food if eaten
if (ateFood) {
bool valid;
do {
foodX = random(16);
foodY = random(16);
valid = true;
for (int i = 0; i < snakeLength; i++) {
if (snakeX[i] == foodX && snakeY[i] == foodY) {
valid = false;
break;
}
}
} while (!valid);
}
}
}
*/