8 years, 7 months ago.

Snake Game, The Snake Tail not working as intended.

I'm trying to make a simple snake game, on FRDM-k64F. Everything works fine, the snake moves, it grows if a food's eaten or i press a button (to simulate food eaten, for debugging). Except, all the snake tail section changes direction everytime I change the snake head direction. I know that's my code's problem, I identified it, but I couldn't get a way to write my code correctly. Any suggestions?

SnakeGame

#include "mbed.h"
#include "N5110.h"
#include "SNAKELIB.h"
//         VCC,    SCE,   RST,   D/C,   MOSI,  SCLK,   LED
N5110 lcd (PTE26 , PTA0 , PTC4 , PTD0 , PTD2 , PTD1 , PTC3);
Ticker SnakeSpeed;
//Variables
int FoodPattern[3][3] = {{1,0,1},{0,1,0},{1,0,1}};
int SnakeHead[4][3] = {{1,1,1},{1,1,1},{1,1,1},{0,1,1}}; //Snake head template
int SnakeTail[4][3] = {{0,0,1},{0,1,1},{1,1,1},{0,1,1}}; //Snake tail template
int RandX, RandY; //Random food coordinate
int x, y; //The SnakeHead 1st pixel, max (19,10) because it's 4 blocks(and 2 blank pixel space)
int tailX[100], tailY[100], SnakeLength; //The SnakeTail pixel, max (19,10) (same above)
int prevX, prevY; //Store Previous Snake Tail X,Y status
int prev2X, prev2Y; //Store Previous Previous Snake Tail X,Y
//Functions
void RandFoodCoordinate(); //Randomize the food coordinate
void SetFoodCoordinate(); //Set the food coordinate 
void SetSnakeHead(); 
void SetSnakeTail();
void SnakeMoving();
void DrawBorders(); //Draw the outline border.
void MainGame(); //This is the game function which's called every ticker time
void InitSnakeTail(); 
//Declarations
void RandFoodCoordinate()
{
    RandX = rand()%19; //randomize a number between 0-18, i.e. max x coordinate = 76
    RandY = rand()%10; //randomize a number between 0-9, i.e. max y coordinate = 40
}

void SetFoodCoordinate()
{
    for (int a = 0; a < 3; a++){
        for (int b = 0; b < 3; b++){
            if (FoodPattern[a][b] == 1){
                //The +2 is to make sure the food would not spawn in the border. Maximum (x,y)of food is (78,42)
                //and the last pixel of food would be (80,44) [[maybe i could make it 83,47 later. need testing]] 
                lcd.setPixel(4*RandX+a+2,4*RandY+b+2); 
            }
        }
    }
}

void SetSnakeHead()
{  
    if (snakedirection == right){
        for (int c = 0; c < 4; c++){
            for (int d = 0; d < 3; d++){
                if (SnakeHead[c][d] == 1){
                    lcd.setPixel(4*x+c+1,4*y+d+2); //The +1, +2 is just to align the pixel with the food correctly
                }
            }
        }
    }
    else if (snakedirection == left){
        for (int c = 0; c < 4; c++){
            for (int d = 0; d < 3; d++){
                if (SnakeHead[c][d] == 1){
                    lcd.setPixel(4*x-c+5,4*y+d+2); //The +5, +2 is to align the pixel with food correctly
                }
            }
        }
    }
    else if (snakedirection == up){
        for (int c = 0; c < 4; c++){
            for (int d = 0; d < 3; d++){
                if (SnakeHead[c][d] == 1){
                    lcd.setPixel(4*x+d+2,4*y-c+6); //+2, +6 is to align pixel with food
                }
            }
        }
    }
    else if (snakedirection == down){
        for (int c = 0; c < 4; c++){
            for (int d = 0; d < 3; d++){
                if (SnakeHead[c][d] == 1){
                    lcd.setPixel(4*x+d+2,4*y+c+2); //+2 +2 for pixel alignment
                }
            }
        }for (int c = 0; c < 4; c++){
            for (int d = 0; d < 3; d++){
                if (SnakeHead[c][d] == 1){
                    lcd.setPixel(4*x+c+1,4*y+d+2); //The +1, +2 is just to align the pixel with the food correctly
                }
            }
        }
    }
    else{
for (int c = 0; c < 4; c++){
            for (int d = 0; d < 3; d++){
                if (SnakeHead[c][d] == 1){
                    lcd.setPixel(4*x+c+1,4*y+d+2); //The +1, +2 is just to align the pixel with the food correctly
                }
            }
        }
    }
}

void SetSnakeTail() //This is the function which consists of problem.
{
    prevX = tailX[0]; //Stores the value of SnakeHead(x,y) so that the tail[i] can move to tail[i-1] (which is 0 in this case] 
    prevY = tailY[0]; //
    tailX[0] = x; //tailX[0] is actually the x coordinate of SnakeHead. This makes sure the tail is following head.
    tailY[0] = y; //tailY[0] is actually the y coordinate of SnakeHead. This makes sure the tail is following head.
    for (int i = 1; i<SnakeLength; i++){
        prev2X = tailX[i]; //Stores (x,y) of tailX[i], e.g. tailX[1]
        prev2Y = tailY[i]; //
        tailX[i] = prevX; //tailX[i] moves to tailX[i-1], e.g. tailX[1] to tailX[0] i.e. x (head position). Remember head already moved.
        tailY[i] = prevY; //tailY[i] moves to tailY[i-1].
        prevX = prev2X; //prevX becomes prev2X. So that in the next for loop tailX[i] would update to the next element. 
        prevY = prev2Y;
        if (snakedirection == right){ 
            for (int e = 0; e < 4; e++){
                for (int f = 0; f < 3; f++){
                    if (SnakeTail[e][f] == 1){
                        lcd.setPixel(4*tailX[i]+e+1,4*tailY[i]+f+2); //move right
                    }
                }
            }
        }
        else if (snakedirection == left){
            for (int e = 0; e < 4; e++){
                for (int f = 0; f < 3; f++){
                    if (SnakeTail[e][f] == 1){
                        lcd.setPixel(4*tailX[i]-e+5,4*tailY[i]+f+2); //move left
                    }
                }
            }
        }
        else if (snakedirection == up){
            for (int e = 0; e < 4; e++){
                for (int f = 0; f < 3; f++){
                    if (SnakeTail[e][f] == 1){
                        lcd.setPixel(4*tailX[i]+f+2,4*tailY[i]-e+6); //move up
                    }
                }
            }
        }
        else if (snakedirection == down){
            for (int e = 0; e < 4; e++){
                for (int f = 0; f < 3; f++){
                    if (SnakeTail[e][f] == 1){
                        lcd.setPixel(4*tailX[i]+f+2,4*tailY[i]+e+2); //move down
                    }
                }
            }
        }
    }
}
        
void SnakeMoving() //karnaugh warping of pixels.
{
    readJoystick();
    if (snakedirection == right){
        x++;
        if (x > 19){
        x = 0;}}
    else if (snakedirection == left){
        x--;
        if (x < 0){
        x = 19;}}
    else if (snakedirection == up){
        y--;
        if (y < 0){
        y = 10;}}
    else if (snakedirection == down){
        y++;
        if (y > 10){
        y = 1;}}
    else {}
}

void DrawBorders() //function name says it all
{
    lcd.drawRect(0,0,83,47,0);
    lcd.refresh();
}

void MainGame()
{
    lcd.clear();
    SetSnakeHead();
    if (button == 1 || (x == RandX && y == RandY)){
        SnakeLength++;
        RandFoodCoordinate();
        SetFoodCoordinate();
    }
    else{
        SetFoodCoordinate();
    }
    SetSnakeTail();
    SnakeMoving();
    DrawBorders();
}
//MAIN CODE
int main()
{
    //Calibrate Joystick First.
    calibrateJoystick();  // get centred values of joystick
    //
    button.mode(PullDown);
    srand(time(0)); //what srand do, is to tell the program make a new set of random numbers every second the program is running
    //Start the lcd initialisation
    lcd.init();
    RandFoodCoordinate();
    SetFoodCoordinate();
    x = 10; y = 5;
    SnakeLength = 2;
    SetSnakeHead();
    InitSnakeTail();
    DrawBorders();
    readJoystick();
    snakedirection = centre;
    //And then start calling the ticker function of game
    while (snakedirection == centre){
        updateJoystick();
        readJoystick();
        if (snakedirection != centre)
        break;
    }
    pollJoystick.attach(&updateJoystick,1.0/30.0);  // read joystick 10 times per second
    SnakeSpeed.attach(&MainGame,0.2); //address of the function to be attached (Main Game) and speed (0.2)
    while(1)
    {}
}

void InitSnakeTail()
{
    tailX[0] = x;
    tailY[0] = y;
    for (int i = 1; i < SnakeLength; i++){
        tailX[i] = x-i;
        tailY[i] = y;
        for (int e = 0; e < 4; e++){
            for (int f = 0; f < 3; f++){
                if (SnakeTail[e][f] == 1){
                    lcd.setPixel(4*tailX[i]+e+1,4*tailY[i]+f+2); //That +2 is to match the snake placement.
                }
            }
        }
    }
}

This is the link to a video showing how my code doesn't work. https://www.youtube.com/watch?v=sk5fL_qBxTo

1 Answer

8 years, 7 months ago.

OK so you're using full redraw, Personally I prefer selective erase it might give less flicker if the display doesn't have a double buffer.

Looking at the "draw tail" loop it seems to use the same "snakedirection" variable for the whole tail. I think you have two options:

Either store the tail direction in an array the way you store the tail positions... or ...Determine the direction of a tail section based on the position of the previous section.

something like

        if (tailX[i] > prevX) { 
            for (int e = 0; e < 4; e++){
                for (int f = 0; f < 3; f++){
                    if (SnakeTail[e][f] == 1){
                        lcd.setPixel(4*tailX[i]+e+1,4*tailY[i]+f+2); //move right
                    }
                }
            }
        }
        else if (tailX[i] < prevX) {
            for (int e = 0; e < 4; e++){
                for (int f = 0; f < 3; f++){
                    if (SnakeTail[e][f] == 1){
                        lcd.setPixel(4*tailX[i]-e+5,4*tailY[i]+f+2); //move left
                    }
                }
            }
        }
        else if (tailY[i] < prevY) {
            for (int e = 0; e < 4; e++){
                for (int f = 0; f < 3; f++){
                    if (SnakeTail[e][f] == 1){
                        lcd.setPixel(4*tailX[i]+f+2,4*tailY[i]-e+6); //move up
                    }
                }
            }
        }
        else if (tailY[i] > prevY) ){
            for (int e = 0; e < 4; e++){
                for (int f = 0; f < 3; f++){
                    if (SnakeTail[e][f] == 1){
                        lcd.setPixel(4*tailX[i]+f+2,4*tailY[i]+e+2); //move down
                    }
                }
            }
        }

I might have got left/right flipped but I think it shows how it might be fixed