Sign in to follow this  

How do I handle turning in a snake game?

This topic is 4717 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I have got my snake game to the stage where you can go around, eat apples and your snake grows. Problem is I don't know how to make the pieces after the first one turn at the right place. I'm using linked lists with the idea being that it can grow infinately. I'll post the code so far incase it helps. Thanks
[source lang=c]
#include <SDL/SDL.h>
#include <stdio.h>
#include <stdlib.h>

#define MAPWIDTH 40
#define MAPHEIGHT 40

/* Define Booleans */
typedef enum { false, true } bool;
bool one = false;

struct item
{
    int x_pos;
    int y_pos;
};    
struct item apple;

struct part
{
    int x_pos, y_pos;
    int x_vol, y_vol;
    enum Estate {RIGHT, LEFT, UP, DOWN} state;
    struct part *next_part;  /* Pointer to next part */
};    
struct part *first_part = NULL;
struct part *last_part = NULL;

int map[MAPWIDTH][MAPHEIGHT];

/* function predeclarations */
int main(int argc, char *argv[]);
bool game_init();
void game_loop();
void get_input();
void add_apple();
void move_snake(float time_passed);
void move_part(float time_passed, struct part *current_part);
void edge_collision_detection(int *x, int *y);
bool apple_collision_detection(int x, int y);
void draw_apple();
void draw_snake();
void draw_part(struct part *current_part);
void draw_screen();
void game_close();

bool game_running;
float current_time; 
SDL_Surface *screen, *sprite_image;

int main(int argc, char *argv[])
{
    atexit(game_close);
    
    if (game_init() == false)
        exit(1);
    
    while (game_running == true)
        game_loop();
}

bool game_init()
{
    int x, y;
    SDL_Surface *temp;
    
    if (SDL_Init(SDL_INIT_VIDEO) != 0)
    {
        printf("Unable to initialize SDL\n");
        return false;
    }
    
    screen = SDL_SetVideoMode(400, 400, 16, SDL_DOUBLEBUF);
    if (screen == NULL)
    {
        printf("Unable to set video mode\n");
        return false;
    }
    
    SDL_WM_SetCaption("Snake Attempt", NULL);   
    
    temp = SDL_LoadBMP("sprites.bmp");
    if (temp == NULL)
    {
        printf("Unable to load picture\n");
        return false;
    }
    SDL_SetColorKey(temp, SDL_SRCCOLORKEY | SDL_RLEACCEL, (Uint16) SDL_MapRGB(temp->format, 0, 0, 0));     
    sprite_image = SDL_DisplayFormat(temp);
    
    SDL_FreeSurface(temp);
    
    current_time = SDL_GetTicks();
    
    first_part = malloc(sizeof(struct part));
    first_part->next_part = NULL;
    first_part->x_pos = 200;
    first_part->y_pos = 200;
    first_part->x_vol = 1;
    first_part->y_vol = 0;
    first_part->state = RIGHT;
    last_part = first_part;
    
    for (x = 0; x < MAPWIDTH; x++)
        for (y = 0; y < MAPHEIGHT; y++)
            map[x][y] = 0;
    
    add_apple();
    
    game_running = true;
    return true;
}    

void game_loop()
{
    float time_passed, last_time;

    last_time = current_time;
    current_time = SDL_GetTicks();
    time_passed = current_time - last_time;
    
    get_input();
    move_snake(time_passed);
    
    draw_screen();
    draw_snake();
    draw_apple();
    
    SDL_Flip( screen );
}    

void get_input()
{
    SDL_Event event;
    SDL_keysym keysym;
    
    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
                game_running = false;
                break;
                
            case SDL_KEYDOWN:
                keysym = event.key.keysym;
				switch(keysym.sym) 
                {
                    case SDLK_ESCAPE:
                        game_running = false;
                        break;
                        
                    case SDLK_LEFT:
                        first_part->x_vol = -1;
                        first_part->y_vol = 0;
                        first_part->state = LEFT;
                        break;
                    
                    case SDLK_RIGHT:
                        first_part->x_vol = 1;
                        first_part->y_vol = 0;
                        first_part->state = RIGHT;
                        break;
                    
                    case SDLK_UP:
                        first_part->x_vol = 0;
                        first_part->y_vol = -1;
                        first_part->state = UP;
                        break;
                        
                    case SDLK_DOWN:
                        first_part->x_vol = 0;
                        first_part->y_vol = 1;
                        first_part->state = DOWN;
                        break;
                }
                break;
        }
        break;
    }
}  

void add_apple()
{
    int x, y;

    x = rand() % 400;
    y = rand() % 400;
    
    apple.x_pos = x;
    apple.y_pos = y;
}    
    
void add_part()
{
    struct part *new_part;
    
    /* Allocates memory for new part */
    new_part = malloc(sizeof(struct part));
    
    /* Sets the data of new part */
    new_part->x_vol = last_part->x_vol;
    new_part->y_vol = last_part->y_vol;
    new_part->x_pos = last_part->x_pos + (-10 * last_part->x_vol);
    new_part->y_pos = last_part->y_pos + (-10 * last_part->y_vol);
    new_part->state = last_part->state;
    
    /* Point the old last part's next_part to the new part */
    last_part->next_part = new_part;
    
    /* Makes the new part the last part */
    last_part = new_part;
}    
    
void move_snake(float time_passed)
{
    time_passed = time_passed / 10;
    
    struct part *current_part;
    
    current_part = first_part;
    
    while ( current_part != NULL )
    {
        move_part(time_passed, current_part);
        
        /*current_part->next_part->state = current_part->state;
        current_part->next_part->x_vol = current_part->x_vol;
        current_part->next_part->y_vol = current_part->y_vol;*/
        
        current_part = current_part->next_part;
    }    
}    

void move_part(float time_passed, struct part *current_part)
{
    int x, y;
    
    x = current_part->x_pos + (current_part->x_vol * time_passed);
    y = current_part->y_pos + (current_part->y_vol * time_passed);

    edge_collision_detection(&x, &y);
    if ( apple_collision_detection(x, y) )
    {
        add_apple();
        add_part();
    }    
    
    current_part->x_pos = x;
    current_part->y_pos = y;
}    

void edge_collision_detection(int *x, int *y)
{
    if (*x > 390)
        *x = 0;
    if (*x < 0)
        *x = 390;
    
    if (*y > 390)
        *y = 0;
    if (*y < 0)
        *y = 390;
}    

bool apple_collision_detection(int x, int y)
{
    if ( (x + 10 >= apple.x_pos && x <= apple.x_pos + 10) &&
         (y + 10 >= apple.y_pos && y <= apple.y_pos + 10) )
        return true;
    else
        return false;
}    

void draw_apple()
{
    SDL_Rect src, dest;
    
    src.x = 10;
    src.y = 0;
    src.w = 10;        
    src.h = 10;
    dest.x = apple.x_pos;
    dest.y = apple.y_pos;
    dest.w = 10;        
    dest.h = 10;

    SDL_BlitSurface( sprite_image, &src, screen, &dest );
}
    
void draw_snake()
{
    struct part *current_part;
    
    current_part = first_part;
    
    while ( current_part != NULL )
    {
        draw_part(current_part);
        
        current_part = current_part->next_part;
    }   
}    

void draw_part(struct part *current_part)
{
    SDL_Rect src, dest;
    
    src.x = 0;
    src.y = 0;
    src.w = 10;        
    src.h = 10;
    dest.x = current_part->x_pos;
    dest.y = current_part->y_pos;
    dest.w = 10;        
    dest.h = 10;

    SDL_BlitSurface( sprite_image, &src, screen, &dest );
}    

void draw_screen()
{
    int x, y;
    SDL_Rect src, dest;
    
    
    for (x = 0; x < MAPWIDTH; x++)
        for (y = 0; y < MAPHEIGHT; y++)
        {
            src.x = 30;
            src.y = 0;
            src.w = 10;        
            src.h = 10;
            dest.x = x * 10;
            dest.y = y * 10;
            dest.w = 10;        
            dest.h = 10;
            
            SDL_BlitSurface( sprite_image, &src, screen, &dest );
        }    

    
}    

void game_close()
{
    SDL_FreeSurface(sprite_image);
    SDL_Quit();
}    

Share this post


Link to post
Share on other sites
I haven't looked at your code, but you should mark the location that the head node was at when the user issued the turn command, as well as the direction it turns. This info should be stored in a queue. Then you check every node of the snake against each node in the turn queue. If they are at the position, then they should turn, and then have a way of locally marking that they have completed that queued command. When the last node of the snake touches this data ,it should be released from the queue. If this is a tilebased game then this should be easy, if not then it will require a little tweaking (float inaccuracies and such).

Hope this helps.

Share this post


Link to post
Share on other sites
I know you kind of said how to do it if its not tilebased but I have been thinking a bit of how to implement this and I'm not sure how it would work. The problem is that the movement is dependent on how long its been since the last frame so I think the different parts of the snake might not move in exactly the same position?

Share this post


Link to post
Share on other sites
I wrote a snake game with very similar code to what you have a few years ago for a neural nets project at uni. The thing with Snake is that there's only ever one piece moving - all you need to do to move the snake is remove the tail part from the list, put it where the head will be next turn, and insert it back into the list as the head. The other parts can stay where they are.

If the snake eats something and grows, just don't remove tail parts until it's the correct length.

Share this post


Link to post
Share on other sites
Thats really clever! Also it meens I dont have to use my hack of adding nodes to the end of the list, I can put another one on the frount of the list like my book says to. Thanks :)

Share this post


Link to post
Share on other sites
Okaj, i wrote this some two years ago in Turbo C++ inspired by my old Nokia 3210 snake game :)

So it's text based but may give a hint on how to move the snake.

It's poorly commented, but the code should be quite straightforward though it tends to rotten with time it should be buildable in VC++ or other alike but some code needs to replaced or commented out.

I think you can use this in win32 instead of the dos gotoxy()

void gotoxy(int x, int y)
{
COORD pos;
HANDLE screen = GetStdHandle(STD_OUTPUT_HANDLE);

pos.X = x;
pos.Y = y;

SetConsoleCursorPosition(screen, pos);
}



//------------------
// INCLUDE FILES
//------------------

#include <iostream.h>
#include <fstream.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <dos.h>
#include <stdio.h>


//------------------
// GLOBAL CONSTANTS
//------------------

#define TEXT_MODE 0x0003
#define MAX_ANTAL 150
#define STILL_PLAYING 1
#define QUIT 0

const int MOVE_LEFT = 75;
const int MOVE_RIGHT = 77;
const int MOVE_UP = 72;
const int MOVE_DOWN = 80;

const int UP_WALL = 6;
const int DOWN_WALL = 20;
const int LEFT_WALL = 5;
const int RIGHT_WALL = 75;


//------------------
// ENUMERATED TYPES
//------------------

enum BOOL
{
FALSE,
TRUE
};

enum DIRECTION
{
DOWN,
UP,
LEFT,
RIGHT
};

//------------------
// STRUCTURES
//------------------

struct tGame
{
int highScore;
int score;
int level;
};

struct tCoordinate
{
int x;
int y;
};

struct tSnake
{
int size;
tCoordinate body[MAX_ANTAL];
};

struct tFood
{
char food;
tCoordinate pos;
};


//-----------------------
// FUNCTION DECLARATIONS
//-----------------------

void setTextMode();
void init(tGame&, tSnake&, tFood&);
int gameLoop(tGame&, tSnake&, tFood&);
void updateSnake(tSnake&);
void addSegment(tSnake&);
void removeSegment(tSnake&);
void displaySnake(tSnake&);
void processInput();
BOOL wallCollision(tSnake&);
BOOL foodCollision(tSnake&, tFood&);
BOOL snakeCollision(tSnake);
void updateScore(tGame&);
void printScore(tGame);
void increaseSnake(tSnake&);
void updateFood(tFood&, tSnake);
void displayFood(tFood);
BOOL playAgain();
void saveHighscore(tGame&);
void setDirection();
void pause();
char menu();
void gameMain();
char menuInput();
void showInstr();
void displayTitle();


//------------------
// GLOBAL VARIABLES
//------------------

DIRECTION MOVE_DIRECTION;
DIRECTION NEW_DIRECTION;
BOOL NEW_HIGHSCORE;
BOOL GAME_STATUS;


//----------------
// MAIN
//----------------

void main()
{
setTextMode();
do
{
GAME_STATUS = TRUE;
switch (menu())
{
case '1' : gameMain(); break;
case '2' : showInstr(); break;
case '3' : GAME_STATUS = FALSE; break;
}
} while (GAME_STATUS);
}

void setTextMode()
{
_AX = TEXT_MODE;
geninterrupt(0x10);
}

void gameMain()
{
tGame game;
tSnake snake;
tFood food;

do
{
clrscr();
init(game, snake, food);

while (gameLoop(game, snake, food));

if (NEW_HIGHSCORE) // temp
saveHighscore(game);
gotoxy(35,24);
cprintf("GAME OVER");
} while (playAgain());
}

void displayTitle()
{
char ch;
ifstream fin("title.txt", ios::in);
if (!fin)
cerr <<"File error";
gotoxy(1,2);
cout << fin.rdbuf();
fin.close();
}

char menu()
{
clrscr();
textcolor(2);

for (int i = 1; i < 80; i++)
{
gotoxy(i,1);
cprintf("=");
}
displayTitle();

cprintf(" ver.004");
for (i = 1; i < 80; i++)
{
gotoxy(i,10);
cprintf("=");
}

cout <<"\n\n\n";
textcolor(15);
cprintf("Pick a choice");
cout <<"\n\n";
printf("1. Play Game\n");
printf("2. Game Info\n");
printf("3. Exit");
cout << endl << endl;
return menuInput();
}

char menuInput()
{
char input;
do
{
cprintf("Input : ");
cin >> input;
} while (input != '1' && input != '2' && input != '3');
return input;
}

void showInstr()
{
clrscr();
cprintf("GAME PLAY INFO");
cout <<"\n\n\n";
printf("'P' \t\t: Pause the game\n");
printf("UP ARROW \t: Move snake up\n");
printf("DOWN ARROW \t: Move snake down\n");
printf("LEFT ARROW \t: Move snake left\n");
printf("RIGHT ARROW \t: Move snake right\n");
cout <<"\n\n";
printf("PRESS ANY KEY TO RETURN");
getch();
}

BOOL playAgain()
{
char input;
gotoxy(50,24);
cprintf("PLAY AGAIN ? (y/n) : ");
cin >> input;
if (input == 'y' || input == 'Y')
return TRUE;
else
return FALSE;
}

void pause()
{
gotoxy(35,24);
cprintf("PAUSE");
getch();
gotoxy(35,24);
cprintf(" ");
}

void init(tGame & game, tSnake & snake, tFood & food)
{
textbackground(0);
clrscr();
textcolor(15);
gotoxy(35,2);
cprintf("SNAKE");
cout <<"\t\t\t";
cprintf("ver.004");
textcolor(2);
for (int i = 1; i < 80; i++)
{
gotoxy(i,1);
cprintf("=");
}
for (i = 0; i < 80; i++)
{
gotoxy(i,3);
cprintf("=");
}
for (i = 0; i < 80; i++)
{
gotoxy(i,23);
cprintf("=");
}

textcolor(1);
for (i = 5; i < 22; i++)
{
gotoxy(4,i);
cprintf("#");
}
for (i = 5; i < 22; i++)
{
gotoxy(76,i);
cprintf("#");
}
for (i = 4; i < 77; i++)
{
gotoxy(i,5);
cprintf("#");
}
for (i = 4; i < 77; i++)
{
gotoxy(i,21);
cprintf("#");
}
textcolor(15);

ifstream fin("data.dat", ios::in);
fin >> game.highScore;
fin.close();

game.score = 0;
game.level = 9;

snake.size = 15;
snake.body[0].x = 10;
snake.body[0].y = 10;

for (i = 0; i < snake.size; i++)
{
snake.body[i].x = snake.body[0].x + i;
snake.body[i].y = snake.body[0].y;
}

MOVE_DIRECTION = NEW_DIRECTION = RIGHT;
NEW_HIGHSCORE = FALSE;

updateFood(food, snake);
printScore(game);
displaySnake(snake);
gotoxy(35,24);
cprintf("PRESS A KEY TO BEGIN");
getch();
processInput(); // temp
setDirection(); // temp
gotoxy(35,24);
cprintf(" ");
}

int gameLoop(tGame & game, tSnake & snake, tFood & food)
{
char input;

if (kbhit())
{
input = getch();
switch (input)
{
case 0 : processInput(); break;

case 'p' : pause(); processInput(); break;

case char(27) : return QUIT; break;
}
setDirection();
}

updateSnake(snake);

if (wallCollision(snake))
return QUIT;

if (snakeCollision(snake))
return QUIT;

if (foodCollision(snake, food))
{
updateScore(game);
increaseSnake(snake);
addSegment(snake);
increaseSnake(snake);
addSegment(snake);
updateFood(food, snake);
}

return STILL_PLAYING;
}

void updateSnake(tSnake & snake)
{
addSegment(snake);
removeSegment(snake);
if (MOVE_DIRECTION == UP || MOVE_DIRECTION == DOWN)
{
delay(120);
displaySnake(snake);
}
else
{
delay(50);
displaySnake(snake);
}
}

void processInput()
{
char input;
input = getch();

if (int(input) == MOVE_RIGHT)
NEW_DIRECTION = RIGHT;

if (int(input) == MOVE_LEFT)
NEW_DIRECTION = LEFT;

if (int(input) == MOVE_UP)
NEW_DIRECTION = UP;

if (int(input) == MOVE_DOWN)
NEW_DIRECTION = DOWN;
}

void increaseSnake(tSnake & snake)
{
snake.size++;
}

void updateScore(tGame & game)
{
game.score += game.level;
if (game.score >= game.highScore)
{
game.highScore = game.score;
NEW_HIGHSCORE = TRUE;
}
printScore(game);
}

void printScore(tGame game)
{
gotoxy(1,2);
cprintf(" ");
gotoxy(1,2);
cprintf("Highscore: %d", game.highScore);

gotoxy(2,24);
cprintf(" ");
gotoxy(2,24);
cprintf("Score: %d", game.score);
}

void updateFood(tFood & food, tSnake snake)
{
BOOL badPlace = FALSE;

do
{
food.pos.x = (7 + (rand() % 66));
food.pos.y = (7 + (rand() % 13));

for (int i = 0; i < snake.size; i++)
{
if ((food.pos.x == snake.body[i].x) && (food.pos.y == snake.body[i].y))
{
badPlace = TRUE;
break;
}
else
badPlace = FALSE;
}

} while (badPlace);

displayFood(food);
}

void displayFood(tFood food)
{
gotoxy(food.pos.x, food.pos.y);
cprintf("*");
}

void addSegment(tSnake & snake)
{
switch (MOVE_DIRECTION)
{
case LEFT :
snake.body[snake.size].y = snake.body[snake.size-1].y;
snake.body[snake.size].x = snake.body[snake.size-1].x-1; break;
case RIGHT :
snake.body[snake.size].y = snake.body[snake.size-1].y;
snake.body[snake.size].x = snake.body[snake.size-1].x+1; break;
case UP :
snake.body[snake.size].y = snake.body[snake.size-1].y-1;
snake.body[snake.size].x = snake.body[snake.size-1].x; break;
case DOWN :
snake.body[snake.size].y = snake.body[snake.size-1].y+1;
snake.body[snake.size].x = snake.body[snake.size-1].x; break;
}
}

void removeSegment(tSnake & snake)
{
gotoxy(snake.body[0].x, snake.body[0].y);
cprintf(" ");
gotoxy(1,1);

for (int i = 1; i <= snake.size; i++)
snake.body[i-1] = snake.body[i];
}

void displaySnake(tSnake & snake)
{
textcolor(2);
for (int i = 0; i < snake.size; i++)
{
gotoxy(snake.body[i].x, snake.body[i].y);
if (i == (snake.size-1))
textcolor(14);
cprintf("O");
}
textcolor(15);
}

BOOL wallCollision(tSnake & snake)
{
if (snake.body[snake.size].x == LEFT_WALL
|| snake.body[snake.size].x == RIGHT_WALL)
return TRUE;

if (snake.body[snake.size].y == UP_WALL
|| snake.body[snake.size].y == DOWN_WALL)
return TRUE;

return FALSE;
}

BOOL foodCollision(tSnake & snake, tFood & food)
{
if ((snake.body[snake.size].x == food.pos.x) &&
(snake.body[snake.size].y == food.pos.y))
return TRUE;

return FALSE;
}

BOOL snakeCollision(tSnake snake)
{
for (int i = 0; i < (snake.size - 1); i++)
{
if ((snake.body[snake.size].x == snake.body[i].x) &&
(snake.body[snake.size].y == snake.body[i].y))
return TRUE;
}

return FALSE;
}

void saveHighscore(tGame & game)
{
ofstream fout("data.dat", ios::out);
fout << game.highScore;
fout.close();
}

void setDirection()
{
switch (MOVE_DIRECTION)
{
case RIGHT : if (NEW_DIRECTION == RIGHT || NEW_DIRECTION == LEFT) break;
else MOVE_DIRECTION = NEW_DIRECTION; break;
case LEFT : if (NEW_DIRECTION == LEFT || NEW_DIRECTION == RIGHT) break;
else MOVE_DIRECTION = NEW_DIRECTION; break;
case UP : if (NEW_DIRECTION == UP || NEW_DIRECTION == DOWN) break;
else MOVE_DIRECTION = NEW_DIRECTION; break;
case DOWN : if (NEW_DIRECTION == DOWN || NEW_DIRECTION == UP) break;
else MOVE_DIRECTION = NEW_DIRECTION; break;
}
}

Share this post


Link to post
Share on other sites
Thanks for the example, I'll have a look at it if somthing else confuses me :)

I am going to try the clever way of adding one on the frount and removing the end one, even though it meens the snake moves a tile at a time instead of smoothly.

Thanks for the help anyway.

Share this post


Link to post
Share on other sites

This topic is 4717 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this