Skip to content

Instantly share code, notes, and snippets.

@HorlogeSkynet
Last active October 23, 2023 10:16
Show Gist options
  • Save HorlogeSkynet/d7ec59050c5e8041a7bb to your computer and use it in GitHub Desktop.
Save HorlogeSkynet/d7ec59050c5e8041a7bb to your computer and use it in GitHub Desktop.
Simple Snake for LCD4884 screen on Arduino
//!\\ Visit <https://samuel.forestier.app/blog/hacking/snake-with-lcd4884-shield> //!\\
#include "LCD4884.h"
///////////User's Parameters
#define USE_PUSHBUTTONS 1
#define USE_BUZZER 1
//PIN parameters
#define LEFT_BUTTON 13
#define UP_BUTTON 12
#define DOWN_BUTTON 11
#define RIGHT_BUTTON 10
#define BUZZER 8
#define VOIDANALOGIC A1 //<-- An useless analogic pin to set the random seed
//Global parameters
#define MOVESNEWAPPLE 15
#define DELAY_MS 400
//Snake initial position
#define LENGHTATSTART 3
#define H_X_START 1
#define H_Y_START 5
#define B_X_START 1
#define B_Y_START 4
#define T_X_START 1
#define T_Y_START 3
////////////////////////////
/////Programmer's Parameters
#define DEBUG 0
//Screen options
#define SIZE_X 6
#define SIZE_Y 14
#define MIN_X 0
#define MIN_Y 0
////////////////////////////
typedef enum
{
LEFT,
DOWN,
RIGHT,
UP
}DIRECTION;
typedef enum
{
MINUS,
PIPE
}TYPE;
typedef struct
{
short int posX;
short int posY;
TYPE whatChar;
}POSITION;
typedef struct
{
POSITION *snake;
short int snakeLenght;
DIRECTION lastDirection;
}PLAYER;
typedef struct
{
POSITION coordonates;
short int movingCount;
bool present;
}APPLE;
//////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
lcd.init();
lcd.clear();
lcd.turnBacklightOn(true);
randomSeed(analogRead(VOIDANALOGIC));
if(USE_PUSHBUTTONS)
{
pinMode(LEFT_BUTTON, INPUT);
pinMode(UP_BUTTON, INPUT);
pinMode(DOWN_BUTTON, INPUT);
pinMode(RIGHT_BUTTON, INPUT);
}
if(USE_BUZZER)
{
pinMode(BUZZER, OUTPUT);
}
if(DEBUG)
{
Serial.begin(9600);
Serial.println("Screen, OUTPUT & INPUT pins, and random seed initialised.");
}
}
void loop()
{
APPLE apple;
PLAYER player;
start(&player, &apple);
play(&player, &apple);
end(&player, &apple);
}
//////////////////////////////////////////////////////////////////////////////////////////
void start(PLAYER *player, APPLE *apple)
{
player->snakeLenght = LENGHTATSTART;
player->snake = (POSITION*)malloc(player->snakeLenght * sizeof(*player->snake));
if(player->snake == NULL)
{
if(DEBUG)
{
Serial.println("Error during memory allocation.");
}
loop();
}
apple->coordonates.posX = 0;
apple->coordonates.posY = 0;
apple->movingCount = 0;
apple->present = false;
player->snake[0].posX = H_X_START;
player->snake[0].posY = H_Y_START;
player->snake[0].whatChar = MINUS;
player->snake[1].posX = B_X_START;
player->snake[1].posY = B_Y_START;
player->snake[1].whatChar = MINUS;
player->snake[2].posX = T_X_START;
player->snake[2].posY = T_Y_START;
player->snake[2].whatChar = MINUS;
player->lastDirection = RIGHT;
if(USE_BUZZER)
{
tone(BUZZER, 10000, 10);
}
lcd.writeString(CENTER("SNAKE"), 2, "SNAKE", MENU_NORMAL);
lcd.writeString(CENTER("START !"), 5, "START !", MENU_HIGHLIGHT);
while(noButtonPressed());
promptMap(player, apple);
delay(1000);
while(noButtonPressed());
}
void play(PLAYER *player, APPLE *apple)
{
bool useLastDirection = false;
short int _lastPosX, _lastPosY;
TYPE _lastType;
for(short int k; ; apple->movingCount++, delay(DELAY_MS))
{
_lastPosX = player->snake[0].posX;
_lastPosY = player->snake[0].posY;
_lastType = player->snake[0].whatChar;
if(USE_PUSHBUTTONS)
{
if(digitalRead(LEFT_BUTTON))
{
goLeft(player);
}
else
{
if(digitalRead(UP_BUTTON))
{
goUp(player);
}
else
{
if(digitalRead(DOWN_BUTTON))
{
goDown(player);
}
else
{
if(digitalRead(RIGHT_BUTTON))
{
goRight(player);
}
}
}
}
}
else
{
static short int _joystick;
_joystick = int(analogRead(0));
Serial.println(_joystick);
if(_joystick < 1000)
{
if(_joystick < 700)
{
if(_joystick < 450)
{
if(_joystick < 200)
{
goDown(player);
}
else
{
goRight(player);
}
}
else
{
goUp(player);
}
}
else
{
goLeft(player);
}
}
}
if(player->snake[0].posX == _lastPosX && player->snake[0].posY == _lastPosY)
{
switch(player->lastDirection)
{
case LEFT:
goLeft(player);
break;
case UP:
goUp(player);
break;
case DOWN:
goDown(player);
break;
case RIGHT:
goRight(player);
break;
default:
if(DEBUG)
{
Serial.println("Error Switch lastDirection.\n\n");
}
break;
}
}
if(isSnakePresent(player))
{
break;
}
else
{
appleTest(player, apple);
//Head
if(player->lastDirection == LEFT || player->lastDirection == RIGHT)
{
player->snake[0].whatChar = MINUS;
}
else
{
player->snake[0].whatChar = PIPE;
}
//Tail
for(k = player->snakeLenght - 1; k > 1; k--)
{
player->snake[k].posX = player->snake[k - 1].posX;
player->snake[k].posY = player->snake[k - 1].posY;
player->snake[k].whatChar = player->snake[k - 1].whatChar;
}
//Body
player->snake[1].posX = _lastPosX;
player->snake[1].posY = _lastPosY;
player->snake[1].whatChar = _lastType;
if(!apple->present)
{
appleAppearing(player, apple);
}
promptMap(player, apple);
}
}
}
void goUp(PLAYER *player)
{
if(player->lastDirection != DOWN)
{
player->snake[0].posX--;
if(player->snake[0].posX < MIN_X)
{
player->snake[0].posX = SIZE_X - 1;
}
player->lastDirection = UP;
}
}
void goLeft(PLAYER *player)
{
if(player->lastDirection != RIGHT)
{
player->snake[0].posY--;
if(player->snake[0].posY < MIN_Y)
{
player->snake[0].posY = SIZE_Y - 1;
}
player->lastDirection = LEFT;
}
}
void goRight(PLAYER *player)
{
if(player->lastDirection != LEFT)
{
player->snake[0].posY++;
if(player->snake[0].posY > SIZE_Y - 1)
{
player->snake[0].posY = MIN_Y;
}
player->lastDirection = RIGHT;
}
}
void goDown(PLAYER *player)
{
if(player->lastDirection != UP)
{
player->snake[0].posX++;
if(player->snake[0].posX > SIZE_X - 1)
{
player->snake[0].posX = MIN_X;
}
player->lastDirection = DOWN;
}
}
void newSnake(PLAYER *player)
{
POSITION *_snakeTemp;
player->snakeLenght++;
_snakeTemp = (POSITION*)malloc(player->snakeLenght * sizeof(*_snakeTemp));
if(_snakeTemp == NULL)
{
if(DEBUG)
{
Serial.println("Error during memory allocation.");
}
loop();
}
//The last value of new array takes the last value of the old array
_snakeTemp[player->snakeLenght - 1].posX = player->snake[player->snakeLenght - 2].posX;
_snakeTemp[player->snakeLenght - 1].posY = player->snake[player->snakeLenght - 2].posY;
for(short int i = 0; i < player->snakeLenght - 1; i++)
{
_snakeTemp[i].posX = player->snake[i].posX;
_snakeTemp[i].posY = player->snake[i].posY;
_snakeTemp[i].whatChar = player->snake[i].whatChar;
}
if(player->snake != NULL)
{
free(player->snake);
player->snake = NULL;
}
player->snake = _snakeTemp;
_snakeTemp = NULL;
}
void promptMap(PLAYER *player, APPLE *apple)
{
lcd.clear();
for(short int k = 0; k < player->snakeLenght; k++)
{
if(player->snake[k].whatChar == MINUS)
{
lcd.writeString(player->snake[k].posY * NB_PIX_X, player->snake[k].posX, "-", MENU_NORMAL);
}
else
{
lcd.writeString(player->snake[k].posY * NB_PIX_X, player->snake[k].posX, "I", MENU_NORMAL);
}
}
if(apple->present)
{
lcd.writeString(apple->coordonates.posY * NB_PIX_X, apple->coordonates.posX, "*", MENU_HIGHLIGHT);
}
}
void appleAppearing(PLAYER *player, APPLE *apple)
{
short int i, j;
long _randy;
do
{
_randy = random(0, SIZE_X * SIZE_Y);
j = _randy / SIZE_X;
i = _randy - (j * SIZE_X);
} while(!testCase(player, i, j));
apple->coordonates.posX = i;
apple->coordonates.posY = j;
apple->present = true;
}
bool isSnakePresent(PLAYER *player)
{
for(short int k = 1; k < player->snakeLenght; k++)
{
if(player->snake[0].posX == player->snake[k].posX && player->snake[0].posY == player->snake[k].posY)
{
return true;
}
}
return false;
}
bool testCase(PLAYER *player, short int caseX, short int caseY)
{
for(short int k = 0; k < player->snakeLenght; k++)
{
if(player->snake[k].posX == caseX && player->snake[k].posY == caseY)
{
return false;
}
}
return true;
}
void appleTest(PLAYER *player, APPLE *apple)
{
if(player->snake[0].posX == apple->coordonates.posX && player->snake[0].posY == apple->coordonates.posY)
{
if(USE_BUZZER)
{
tone(BUZZER, 10000, 5);
}
newSnake(player);
apple->movingCount = 0;
apple->present = false;
}
else
{
if(apple->movingCount > MOVESNEWAPPLE)
{
apple->movingCount = 0;
apple->present = false;
}
}
}
bool noButtonPressed()
{
if(USE_PUSHBUTTONS)
{
if(digitalRead(LEFT_BUTTON) != 0 || digitalRead(UP_BUTTON) != 0 || digitalRead(DOWN_BUTTON) != 0 || digitalRead(RIGHT_BUTTON) != 0)
{
return false;
}
}
else
{
if(analogRead(0) < 1000)
{
return false;
}
}
return true;
}
void end(PLAYER *player, APPLE *apple)
{
if(player->snake != NULL)
{
free(player->snake);
player->snake = NULL;
}
lcd.clear();
lcd.turnBacklightOn(false);
if(USE_BUZZER)
{
tone(BUZZER, 10000, 10);
}
if(player->snakeLenght < SIZE_X * SIZE_Y)
{
lcd.writeString(CENTER("Defeat !"), 1, "Defeat !", MENU_NORMAL);
}
else
{
lcd.writeString(CENTER("Victory !"), 1, "Victory !", MENU_NORMAL);
}
lcd.writeString(0, 3, "Score:", MENU_NORMAL);
char _score[3] = {0};
sprintf(_score, "%hd", player->snakeLenght - LENGHTATSTART);
lcd.writeString(8 * NB_PIX_X, 3, _score, MENU_NORMAL);
lcd.writeString(CENTER("REPLAY ?"), 5, "REPLAY ?", MENU_HIGHLIGHT);
delay(1000);
while(noButtonPressed());
delay(1000);
lcd.clear();
lcd.turnBacklightOn(true);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment