Thread: Need help with a snake game (ncurses)

  1. #1
    Registered User
    Join Date
    Jan 2007
    Posts
    8

    Need help with a snake game (ncurses)

    Hello!

    I have been working on a text based snake game for a couple of days. But I can't get the snake to move as it should...

    Anyway, here's the code so far:
    Code:
    #include <ncurses.h>
    #include <time.h>
    
    #define D_UP    0
    #define D_DOWN  1
    #define D_LEFT  2
    #define D_RIGHT 3
    
    #define WIDTH  80
    #define HEIGHT 24
    typedef struct SnakeBlock_ {
        int dir;
        int x, y;
    } SnakeBlock;
    
    int main(void)
    {
        SnakeBlock snake[100];
        int gdir;
        int i;
        int x, y;
        int running = 0;
        int c;
        struct timespec sleep_time;
        WINDOW *key_detecter;
        
        initscr();
        
        key_detecter = newwin(HEIGHT, WIDTH, 0, 0);
        nodelay(key_detecter, TRUE);
        noecho();
        
        sleep_time.tv_sec = 0;
        sleep_time.tv_nsec = 999999999;
        x = WIDTH / 2;
        y = HEIGHT - 4;
        gdir = D_UP;
        running = 1;
        for (i = 0; i < 4; i++) {
            snake[i].x = x;
            snake[i].y = y + i;
            snake[i].dir = D_UP;
            mvaddch(snake[i].y, snake[i].x, 'O');
        }
        getch();
        while (running) {
            c = wgetch(key_detecter);
            switch (c) {
                case 'w':
                    if (gdir != D_DOWN) {
                        gdir = D_UP;
                    }
                    break;
                case 's':
                    if (gdir != D_UP) {
                        gdir = D_DOWN;
                    }
                    break;
               case 'a':
                   if (gdir != D_RIGHT) {
                       gdir = D_LEFT;
                   }
                   break;
               case 'd':
                   if (gdir != D_LEFT) {
                       gdir = D_RIGHT;
                   }
                   break;
               case 'q':
                   running = 0;
            }
            switch (gdir) {
                case D_UP:
                    snake[0].dir = D_UP;
                    for (i = 1; i < 4; i++) {
                        if (snake[i].dir != D_UP) {
                            snake[i].dir = D_UP;
                            break;
                        }
                    }
                    mvaddch(snake[3].y, snake[3].x, ' ');
                    break;
                case D_DOWN:
                    snake[0].dir = D_DOWN;
                    for (i = 1; i < 4; i++) {
                        if (snake[i].dir != D_DOWN) {
                            snake[i].dir = D_DOWN;
                            break;
                        }
                    }
                    mvaddch(snake[3].y, snake[3].x, ' ');
                    break;
                case D_LEFT:
                    snake[0].dir = D_LEFT;
                    for (i = 1; i < 4; i++) {
                        if (snake[i].dir != D_LEFT) {
                            snake[i].dir = D_LEFT;
                            break;
                        }
                    }
                    mvaddch(snake[3].y, snake[3].x, ' ');
                    break;
                case D_RIGHT:
                    snake[0].dir = D_RIGHT;
                    for (i = 1; i < 4; i++) {
                        if (snake[i].dir != D_RIGHT) {
                            snake[i].dir = D_RIGHT;
                            break;
                        }
                    }
                    mvaddch(snake[3].y, snake[3].x, ' ');
                    break;
            }
            for (i = 0; i < 4; i++) {
                switch (snake[i].dir) {
                    case D_UP:
                        snake[i].y--;
                        mvaddch(snake[i].y, snake[i].x, 'O');
                        break;
                    case D_DOWN:
                        snake[i].y++;
                        mvaddch(snake[i].y, snake[i].x, 'O');
                        break;
                    case D_LEFT:
                        snake[i].x--;
                        mvaddch(snake[i].y, snake[i].x, 'O');
                        break;
                    case D_RIGHT:
                        snake[i].x++;
                        mvaddch(snake[i].y, snake[i].x, 'O');
                        break;
                }
                refresh();
                nanosleep(&sleep_time, NULL);
            }
        }
        endwin();
        
        return 0;
    }
    Notice, that it's just a test program. And it doesn't work so good I'm afraid. Actualy it goes well when the snake moves forward but when you press a key to make the snake change direction it f*ucks up

    I don't know if I have though wrong from the beginning or if I have made some silly mistake in the code.
    However, the thoght was to make a list of all the snake "blocks" which stores there current x and y cordinats and the direction which they shall move in.

    I would be very thankful if I could get some useful hint and tips on how I should sove this litle problem. I DON'T want complete working code, however...

    (Sorry for my bad english - I'm from Sweden and only 15 years old).

  2. #2
    Registered User
    Join Date
    Jan 2007
    Posts
    8
    Oops... I realised that I posted in the wrong forum...

  3. #3
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    Separate the snake from the display.

    Like for example, by having more than just main() doing the whole work.

    Your idea of having a list of snake positions is good, so manage that in a separate function or two.
    As the snake moves, you append to the head and remove from the tail.
    When the snake eats something and grows, you delay the removing from the tail for a few iterations.

    Then have something which clears the display and refreshes the entire snake from the list you've built up.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  4. #4
    Registered User
    Join Date
    Jan 2007
    Posts
    8

    Thumbs up

    Quote Originally Posted by Salem
    Separate the snake from the display.
    Do you mean that I shouldn't write to the screen directly? Maybe a two dimesnional array which corresponds to what the user should see on the screen. And then make a function which go throgh the array and put it on screen...

    Quote Originally Posted by Salem
    Like for example, by having more than just main() doing the whole work.
    Yes, I know that's bad programming practise. But as I said it was more like a test program so I didn't care about it.

    Quote Originally Posted by Salem
    Your idea of having a list of snake positions is good, so manage that in a separate function or two.
    As the snake moves, you append to the head and remove from the tail.
    When the snake eats something and grows, you delay the removing from the tail for a few iterations.
    I have also thought something similiar but it didn't go so well... But it basically means to remove and insert elements in the "snaka block array", right?

    Thanks for the answer, Salem. I will try some more tmorrow.

  5. #5
    Registered User
    Join Date
    Jan 2007
    Posts
    8
    I have worked on the snake game for a few hours now and made a driver to test the functions... But there's one problem - the snake only grows and don't erase from it's tail.

    The code, right now:
    Code:
    #include <ncurses.h>
    #include <time.h>
    #define WIDTH        80
    #define HEIGHT       24
    #define MAX_LENGTH   1000
    #define START_LENGTH 3
    #define S_BLOCK      'O'
    #define D_UP         0
    #define D_DOWN       1
    #define D_LEFT       2
    #define D_RIGHT      3
    
    typedef struct SnakeBlock_ {
        int x;
        int y;
    } SnakeBlock;
    
    void init_game(void);
    void terminate_game(void);
    void remove_tail(SnakeBlock snake[], int length);
    void append_head(SnakeBlock snake[], int x, int y);
    void show_snake(SnakeBlock snake[], int length);
    void move_back_blocks(SnakeBlock snake[], int length);
    void draw_snake(SnakeBlock snake[], int clength);
    
    int main(void)
    {
        SnakeBlock snake[MAX_LENGTH];
        WINDOW *key_detecter;
        struct timespec sleep_time;
        int i;
        int x, y;
        int dir;
        int running;
        int clength;
        
        sleep_time.tv_sec  = 0;
        sleep_time.tv_nsec = 1000000000;
        init_game();
        
        key_detecter = newwin(HEIGHT, WIDTH, 0, 0);
        nodelay(key_detecter, FALSE);
        
        running = 1;
        dir = D_UP;
        x = WIDTH / 2;
        y = HEIGHT - START_LENGTH;
        
        for (i = 0; i < START_LENGTH; i++) {
            snake[i].x = x;
            snake[i].y = y + i;
        }
        clength = START_LENGTH;
    
        while(running) {
            y--;
            remove_tail(snake, clength);
            move_back_blocks(snake, clength);
            append_head(snake, x, y);
            draw_snake(snake, clength);
            refresh();
            sleep(1);
            /* nanosleep(&sleep_time, NULL); */
        }
        getch();
        terminate_game();
        
        return 0;
    }
    
    void init_game(void)
    {
        initscr();
        noecho();
    }
    
    void terminate_game(void)
    {
        clear();
        endwin();
    }
    
    void show_snake(SnakeBlock snake[], int length)
    {
        int i;
    
        for (i = 0; i < length; i++) {
            printf("Block %d: x = %d, y = %d\n", i + 1, snake[i].x, snake[i].y);
        }
    }
    
    void append_head(SnakeBlock snake[], int x, int y)
    {
        snake[0].x = x;
        snake[0].y = y;
    }
    
    void remove_tail(SnakeBlock snake[], int length)
    {
        snake[length - 1].x = -1;
        snake[length - 1].y = -1;
    }
    
    void move_back_blocks(SnakeBlock snake[], int length)
    {
        int i;
        SnakeBlock temp;
    
        for (i = length; i > 0; i--) {
            temp = snake[i - 1];
            snake[i] = temp;
        }
    }
    
    void draw_snake(SnakeBlock snake[], int length)
    {
        int i;
    
        for (i = 0; i < length; i++) {
            if (snake[i].y != -1) {
                mvaddch(snake[i].y, snake[i].x, S_BLOCK);
            }
        }
    }
    I would be thankful for a litle help, since I'm stuck.

  6. #6
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Simply not printing something won't work; you have to print a space there or something.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  7. #7
    Registered User
    Join Date
    Jan 2007
    Posts
    8
    Quote Originally Posted by dwks
    Simply not printing something won't work; you have to print a space there or something.
    Of course. I dunno.

  8. #8
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Try this.
    Code:
    void remove_tail(SnakeBlock snake[], int length)
    {
        mvaddch(snake[length - 1].y, snake[length - 1].x, ' ');
        snake[length - 1].x = -1;
        snake[length - 1].y = -1;
    }
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  9. #9
    Registered User
    Join Date
    Jan 2007
    Posts
    8
    Quote Originally Posted by dwks
    Try this.
    Code:
    void remove_tail(SnakeBlock snake[], int length)
    {
        mvaddch(snake[length - 1].y, snake[length - 1].x, ' ');
        snake[length - 1].x = -1;
        snake[length - 1].y = -1;
    }
    Yeah, I know. I realised that this morning. :P Now the snake behavies as I want. Time to add some more functionallity.

  10. #10
    Registered User
    Join Date
    Jan 2007
    Posts
    8
    Hmm... I've tried to make a function which shall make so that the snake grows. But it doesn't goes so well actually...
    The problem is when the snake "eats" a "food block", it just disappears and then shows up again in a other place.

    I know it's something wrong with my grow() function.

    The hole code looks like this right now:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <ncurses.h>
    #include <term.h>
    #define WIDTH        80
    #define HEIGHT       24
    #define MAX_LENGTH   1000
    #define START_LENGTH 3
    #define B_SNAKE      'O'
    #define B_FOOD       '*'
    #define D_UP         0
    #define D_DOWN       1
    #define D_LEFT       2
    #define D_RIGHT      3
    
    typedef struct SnakeBlock_ {
        int x;
        int y;
    } SnakeBlock;
    
    void init_game(void);
    void terminate_game(void);
    void remove_tail(SnakeBlock snake[], int length);
    void append_head(int x, int y, SnakeBlock snake[]);
    void move_back_blocks(SnakeBlock snake[], int length);
    void draw_map(int fx, int fy, SnakeBlock snake[], int length);
    void place_food(int *x, int *y, SnakeBlock snake[], int length);
    void grow(SnakeBlock snake[], int length);
    _Bool is_empty(int x, int y, SnakeBlock snake[], int length);
    
    int main(void)
    {
        SnakeBlock snake[MAX_LENGTH];
        WINDOW *key_detecter;
        struct timespec sleep_time;
        int i;
        int x, y;
        int dir;
        int running;
        int length;
        int c;
        int fx, fy;
        
        sleep_time.tv_sec  = 0;
        sleep_time.tv_nsec = 90000000;
        
        init_game();
        
        key_detecter = newwin(HEIGHT, WIDTH, 0, 0);
        nodelay(key_detecter, TRUE);
        
        running = 1;
        dir = D_UP;
        x = WIDTH / 2;
        y = HEIGHT - START_LENGTH;
        
        for (i = 0; i < START_LENGTH; i++) {
            snake[i].x = x;
            snake[i].y = y + i;
        }
        length = START_LENGTH;
        
        place_food(&fx, &fy, snake, length);
        while(running) {
            c = wgetch(key_detecter);
            switch (c) {
                case 'w':
                    if (dir != D_DOWN) {
                        dir = D_UP;
                    }
                    break;
                case 's':
                    if (dir != D_UP) {
                        dir = D_DOWN;
                    }
                    break;
                case 'a':
                    if (dir != D_RIGHT) {
                        dir = D_LEFT;
                    }
                    break;
                case 'd':
                    if (dir != D_LEFT) {
                        dir = D_RIGHT;
                    }
                    break;
                case 'q':
                    running = 0;
                    break;
            }
            switch (dir) {
                case D_UP:
                    y--;
                    break;
                case D_DOWN:
                    y++;
                    break;
                case D_LEFT:
                    x--;
                    break;
                case D_RIGHT:
                    x++;
                    break;
            }
            if (x == fx && y == fy) {
                grow(snake, length);
                length++;
                place_food(&x, &y, snake, length);
            }
            else {
                remove_tail(snake, length);
            }
            move_back_blocks(snake, length);
            append_head(x, y, snake);
            draw_map(fx, fy, snake, length);
            refresh();
            nanosleep(&sleep_time, NULL);
        }
        terminate_game();
        
        return 0;
    }
    
    void init_game(void)
    {
        initscr();
        noecho();
        curs_set(0);
    }
    
    void terminate_game(void)
    {
        curs_set(1);
        clear();
        endwin();
    }
    
    void append_head(int x, int y, SnakeBlock snake[])
    {
        snake[0].x = x;
        snake[0].y = y;
    }
    
    void remove_tail(SnakeBlock snake[], int length)
    {
        mvaddch(snake[length - 1].y, snake[length - 1].x, ' ');
        snake[length - 1].x = -1;
        snake[length - 1].y = -1;
    }
    
    void move_back_blocks(SnakeBlock snake[], int length)
    {
        int i;
        SnakeBlock temp;
    
        for (i = length; i > 0; i--) {
            temp = snake[i - 1];
            snake[i] = temp;init_game();
        }
    }
    
    void draw_map(int fx, int fy, SnakeBlock snake[], int length)
    {
        int i;
    
        for (i = 0; i < length; i++) {
            if (snake[i].y != -1 && snake[i].x != -1) {
                mvaddch(snake[i].y, snake[i].x, B_SNAKE);
            }
        }
        mvaddch(fy, fx, B_FOOD);
    }
    
    void place_food(int *x, int *y, SnakeBlock snake[], int length)
    {
        do {
            *x = nrand(0, WIDTH);
            *y = nrand(0, HEIGHT);
        }
        while (!is_empty(*x, *y, snake, length));
    }
    
    _Bool is_empty(int x, int y, SnakeBlock snake[], int length)
    {
        int i;
        
        for (i = 0; i < length; i++) {
            if (snake[i].x == x && snake[i].y == y) {
                return FALSE;
            }
        }
        
        return TRUE;
    }
    
    int nrand(int lower, int upper)
    {
        int dif = upper - lower;
        
        srand((unsigned int) time((time_t *) NULL));
        return (int)((double)rand() / ((double)RAND_MAX + 1) * dif);
    }
    
    void grow(SnakeBlock snake[], int length)
    {
        snake[length].x = snake[length - 1].x;
        snake[length].y = snake[length - 1].y;
    }
    Any suggestions.

  11. #11
    Registered User
    Join Date
    Jan 2007
    Posts
    8
    Now I now what was wrong - I passed the snake cordinats to place_food(), not the food coordinats. :S
    So I have now managed to make a complete working snake clone. And I'm really proud.

  12. #12
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    BTW, you're only supposed to call srand() once during the execution of a program. I would call it in init_game() in your case.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Open-source Game Project
    By Glorfindel in forum Projects and Job Recruitment
    Replies: 0
    Last Post: 03-24-2009, 01:12 AM
  2. 2D Game project requires extra C++ programmers, new or experienced
    By drallstars in forum Projects and Job Recruitment
    Replies: 2
    Last Post: 05-16-2007, 10:46 AM
  3. Game Engine Link Prob
    By swgh in forum Game Programming
    Replies: 2
    Last Post: 01-26-2006, 12:14 AM
  4. LISP (DrScheme) any one?
    By Jeremy G in forum A Brief History of Cprogramming.com
    Replies: 5
    Last Post: 03-31-2004, 12:52 PM
  5. Game Programmer's AIM Circle: Join Today
    By KingZoolerius66 in forum A Brief History of Cprogramming.com
    Replies: 28
    Last Post: 12-20-2003, 12:12 PM