0% found this document useful (0 votes)
27 views7 pages

LCD Snake Game

This code defines a snake game with the following functionality: - It initializes the game with a snake of 4 blocks and an apple at random positions on a 16x4 pixel grid. - It uses button input and debouncing to control snake direction and initiate a new game. - Each loop iteration, it calculates snake and apple movement/collision, then redraws to the LCD display. If the snake hits a wall or itself, the game ends.

Uploaded by

anhnamzip20
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
27 views7 pages

LCD Snake Game

This code defines a snake game with the following functionality: - It initializes the game with a snake of 4 blocks and an apple at random positions on a 16x4 pixel grid. - It uses button input and debouncing to control snake direction and initiate a new game. - Each loop iteration, it calculates snake and apple movement/collision, then redraws to the LCD display. If the snake hits a wall or itself, the game ends.

Uploaded by

anhnamzip20
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

//MADE BY HOX DIPAN NEW

// VISIT YouTube.com/HoXDipannew
enum DisplayItem {GRAPHIC_ITEM_NONE, GRAPHIC_ITEM_A, GRAPHIC_ITEM_B,
GRAPHIC_ITEM_NUM};
#include <LiquidCrystal_I2C.h> //Install LiquidCrystal_I2C.h
#include <stdlib.h> //Install stdlib.h
#include <limits.h> //Install limits.h
#define GRAPHIC_WIDTH 16
#define GRAPHIC_HEIGHT 4
LiquidCrystal_I2C lcd(0x27,16,2);

byte block[3] = {
B01110,
B01110,
B01110,
};
byte apple[3] = {
B00100,
B01010,
B00100,
};
#define DEBOUNCE_DURATION 20
//Return true if the actual output value is true
bool debounce_activate(unsigned long* debounceStart)
{
if(*debounceStart == 0)
*debounceStart = millis();
else if(millis()-*debounceStart>DEBOUNCE_DURATION)
return true;
return false;
}
//Return true if it's rising edge/falling edge
bool debounce_activate_edge(unsigned long* debounceStart)
{

if(*debounceStart == ULONG_MAX){
return false;
}else if(*debounceStart == 0){
*debounceStart = millis();
}else if(millis()-*debounceStart>DEBOUNCE_DURATION){
*debounceStart = ULONG_MAX;
return true;
}
return false;
}
void debounce_deactivate(unsigned long* debounceStart){
*debounceStart = 0;
}
#define BUTTON_LEFT 6 //LEFT BUTTON
#define BUTTON_RIGHT 7 // RIGHT BUTTON
unsigned long debounceCounterButtonLeft = 0;
unsigned long debounceCounterButtonRight = 0;
struct Pos {
uint8_t x=0, y=0;
};
struct Pos snakePosHistory[GRAPHIC_HEIGHT*GRAPHIC_WIDTH];
//first element is the head.
size_t snakeLength = 0;
enum {SNAKE_LEFT,SNAKE_UP,SNAKE_RIGHT,SNAKE_DOWN} snakeDirection;
struct Pos applePos;
unsigned long lastGameUpdateTick = 0;
unsigned long gameUpdateInterval = 1000;
bool thisFrameControlUpdated = false;
enum {GAME_MENU, GAME_PLAY, GAME_LOSE, GAME_WIN} gameState;
uint8_t graphicRam[GRAPHIC_WIDTH*2/8][GRAPHIC_HEIGHT];
void graphic_generate_characters()
{
/*
space: 0 0
0: 0 A

1: 0 B
2: A 0
3: A A
4: A B
5: B 0
6: B A
7: B B
*/
for (size_t i=0; i<8; i++) {
byte glyph[8];
int upperIcon = (i+1)/3;
int lowerIcon = (i+1)%3;
memset(glyph, 0, sizeof(glyph));
if(upperIcon==1)
memcpy(&glyph[0], &block[0], 3);
else if(upperIcon==2)
memcpy(&glyph[0], &apple[0], 3);
if(lowerIcon==1)
memcpy(&glyph[4], &block[0], 3);
else if(lowerIcon==2)
memcpy(&glyph[4], &apple[0], 3);
lcd.createChar(i, glyph);
}
delay(1);
//Wait for the CGRAM to be written
}
void graphic_clear(){
memset(graphicRam, 0, sizeof(graphicRam));
}
void graphic_add_item(uint8_t x, uint8_t y, enum DisplayItem item) {
graphicRam[x/(8/2)][y] |= (uint8_t)item*(1<<((x%(8/2))*2));
}
void graphic_flush(){
lcd.clear();
for(size_t x=0; x<16; x++){
for(size_t y=0; y<2; y++){
enum DisplayItem upperItem =
(DisplayItem)((graphicRam[x/(8/2)][y*2]>>((x%(8/2))*2))&0x3);
enum DisplayItem lowerItem =
(DisplayItem)((graphicRam[x/(8/2)][y*2+1]>>((x%(8/2))*2))&0x3);
if(upperItem>=GRAPHIC_ITEM_NUM)
upperItem = GRAPHIC_ITEM_B;

if(lowerItem>=GRAPHIC_ITEM_NUM)
lowerItem = GRAPHIC_ITEM_B;
lcd.setCursor(x, y);
if(upperItem==0 && lowerItem==0)
lcd.write(' ');
else
lcd.write(byte((uint8_t)upperItem*3+(uint8_t)lowerItem-1));
}
}
}
void game_new_apple_pos()
{
bool validApple = true;
do{
applePos.x = rand()%GRAPHIC_WIDTH;
applePos.y = rand()%GRAPHIC_HEIGHT;
validApple = true;
for(size_t i=0; i<snakeLength; i++)
{
if(applePos.x==snakePosHistory[i].x && applePos.y==snakePosHistory[i].y){
validApple = false;
break;
}
}
} while(!validApple);
}
void game_init(){
srand(micros());
gameUpdateInterval = 1000;
gameState = GAME_PLAY;
snakePosHistory[0].x=3; snakePosHistory[0].y=1;
snakePosHistory[1].x=2; snakePosHistory[1].y=1;
snakePosHistory[2].x=1; snakePosHistory[2].y=1;
snakePosHistory[3].x=0; snakePosHistory[3].y=1;
snakeLength = 4;
snakeDirection = SNAKE_RIGHT;
game_new_apple_pos();
thisFrameControlUpdated = false;
}
void game_calculate_logic() {

if(gameState!=GAME_PLAY)
return;
//Calculate the movement of the tail
for(size_t i=snakeLength; i>=1; i--){
snakePosHistory[i] = snakePosHistory[i-1];
}
//Calculate the head movement
snakePosHistory[0]=snakePosHistory[1];
switch(snakeDirection){
case SNAKE_LEFT: snakePosHistory[0].x--; break;
case SNAKE_UP: snakePosHistory[0].y--; break;
case SNAKE_RIGHT: snakePosHistory[0].x++; break;
case SNAKE_DOWN: snakePosHistory[0].y++; break;
}
//Look for wall collision
if(snakePosHistory[0].x<0||snakePosHistory[0].x>=GRAPHIC_WIDTH||snakePosHistory[0].y
<0||snakePosHistory[0].y>=GRAPHIC_HEIGHT){
gameState = GAME_LOSE;
return;
}
//Look for self collision
for(size_t i=1; i<snakeLength; i++){
if(snakePosHistory[0].x==snakePosHistory[i].x &&
snakePosHistory[0].y==snakePosHistory[i].y){
gameState = GAME_LOSE;
return;
}
}
if(snakePosHistory[0].x==applePos.x && snakePosHistory[0].y==applePos.y){
snakeLength++;
gameUpdateInterval = gameUpdateInterval*9/10;
if(snakeLength>=sizeof(snakePosHistory)/sizeof(*snakePosHistory))
gameState = GAME_WIN;
else
game_new_apple_pos();
}
}
void game_calculate_display() {

graphic_clear();
switch(gameState){
case GAME_LOSE:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" You lose!");
lcd.setCursor(0, 1);
lcd.print("Length: ");
lcd.setCursor(8, 1);
lcd.print(snakeLength);
break;
case GAME_WIN:
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("You won. Congratz!");
lcd.setCursor(0, 1);
lcd.print("Length: ");
lcd.setCursor(8, 1);
lcd.print(snakeLength);
break;
case GAME_PLAY:
for(size_t i=0; i<snakeLength; i++)
graphic_add_item(snakePosHistory[i].x, snakePosHistory[i].y, GRAPHIC_ITEM_A);
graphic_add_item(applePos.x, applePos.y, GRAPHIC_ITEM_B);
graphic_flush();
break;
case GAME_MENU:
//Do nothing
break;
}
}
void setup() {

lcd.init();
lcd.backlight();
lcd.print(" Snake Game");
lcd.setCursor(0, 1);
// lcd.print("- Sadale.net");
pinMode(6, INPUT);
pinMode(7, INPUT);
graphic_generate_characters();
gameState = GAME_MENU;
}
void loop() {

lcd.setCursor(0, 0);
if(digitalRead(BUTTON_LEFT)==HIGH){
if(debounce_activate_edge(&debounceCounterButtonLeft)&&!thisFrameControlUpdated){
switch(gameState){
case GAME_PLAY:
switch(snakeDirection){
case SNAKE_LEFT: snakeDirection=SNAKE_DOWN; break;
case SNAKE_UP: snakeDirection=SNAKE_LEFT; break;
case SNAKE_RIGHT: snakeDirection=SNAKE_UP; break;
case SNAKE_DOWN: snakeDirection=SNAKE_RIGHT; break;
}
thisFrameControlUpdated = true;
break;
case GAME_MENU:
game_init();
break;
}
}
}else{
debounce_deactivate(&debounceCounterButtonLeft);
}
lcd.setCursor(8, 0);
if(digitalRead(BUTTON_RIGHT)==HIGH){
if(debounce_activate_edge(&debounceCounterButtonRight)&&!thisFrameControlUpdated){
switch(gameState){
case GAME_PLAY:
switch(snakeDirection){
case SNAKE_LEFT: snakeDirection=SNAKE_UP; break;
case SNAKE_UP: snakeDirection=SNAKE_RIGHT; break;
case SNAKE_RIGHT: snakeDirection=SNAKE_DOWN; break;
case SNAKE_DOWN: snakeDirection=SNAKE_LEFT; break;
}
thisFrameControlUpdated = true;
break;
case GAME_MENU:
game_init();
break;
}
}
}else{
debounce_deactivate(&debounceCounterButtonRight);
}
if(millis()-lastGameUpdateTick>gameUpdateInterval){
game_calculate_logic();
game_calculate_display();
lastGameUpdateTick = millis();
thisFrameControlUpdated = false;
}
}

You might also like