Thread: learning C - Snake game

Threaded View

Previous Post Previous Post   Next Post Next Post
  1. #1
    Registered User
    Join Date
    Dec 2022
    Posts
    4

    Lightbulb learning C - Snake game

    I'm learning C programming. And wrote a Snake game for practice. Since I'm self-learning I figured I could post my program here and ask for tips and recommendations to improve it and it could turn into a reference for beginners with more experienced programmers comments on it.

    It uses ANSI codes and Linux specific library to handle terminal drawings unfortunately. If there is a platform independent solutions please help me with that.
    It compiles with no additional linking or flag:
    `$ cc snake.c`

    Code:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <unistd.h>
    // For input handling
    #include <fcntl.h>
    #include <termios.h>
    
    #define W       32
    #define H       16
    #define MAX_INX (H * W)
    #define BODY_C  's'
    #define HEAD_C  'Q'
    #define APPLE_C '*'
    
    // Set this to 1 for passing through walls and comming out from the other side
    #define PASS_WALL 1
    
    // `CSI 0 ; 0 H` puts the cursor at 0, 0 (defaul position).
    // `CSI 0 J` clears from cursor to end of screen.
    // `CSI 3 J` clears entire screen and deletes all lines saved in the scrollback
    // buffer.
    #define cls()            printf("\033[H\033[J\033[3J")
    #define hide_cursor()    printf("\033[?25l")
    #define show_cursor()    printf("\033[?25h")
    #define set_cursor(x, y) printf("\033[%d;%dH", x, y)
    
    typedef struct Point {
        char c;
        struct Point* n;  // next point
    } Point;
    
    typedef struct Snake {
        int len;
        int vx;  // x axis velocity
        int vy;  // y axis velocity
        int inx;
        Point* head;
        Point* tail;
    } Snake;
    
    void drop_apple(Point* table) {
        while (1) {
            int a_inx = rand() % MAX_INX;
    
            if (table[a_inx].c == ' ') {
                table[a_inx].c = APPLE_C;
                break;
            }
        }
    }
    
    void init_game(Point* table, Snake* snake) {
        // Fill table with spaces
        for (int i = 0; i < H; ++i) {
            for (int j = 0; j < W; ++j) {
                table[(i * W) + j].c = ' ';
                table[(i * W) + j].n = NULL;
            }
        }
    
        // Fill vertical table borders with '|'
        for (int j = 0; j < W; j += (W - 1)) {
            for (int i = 0; i < H; ++i) {
                table[(i * W) + j].c = '|';
            }
        }
    
        // Fill horizontal table borders with '-'
        for (int i = 0; i < H; i += (H - 1)) {
            for (int j = 0; j < W; ++j) {
                table[(i * W) + j].c = '-';
            }
        }
    
        // We use a fixed position and body direction for now.
        int x = 1;
        int y = 1;
        int inx = (y * W) + x;
    
        // Tail of the snake
        table[inx].c = BODY_C;
        table[inx].n = &table[inx + 1];
        snake->tail = &table[inx];
    
        // Body of the snake
        for (int i = 1; i < snake->len; ++i) {
            table[inx + i].c = BODY_C;
            table[inx + i].n = &table[inx + i + 1];
        }
    
        // Head of the snake
        table[inx + snake->len].c = HEAD_C;
        table[inx + snake->len].n = NULL;
        snake->head = &table[inx + snake->len];
        snake->inx = inx + snake->len;
    
        srand(time(NULL));  // Initialization, should only be called once.
        drop_apple(table);
    }
    
    void draw_table(const Point* table, const Snake* snake) {
        cls();
        for (int i = 0; i < H; ++i) {
            for (int j = 0; j < W; ++j) {
                printf("%c", table[(i * W) + j].c);
            }
            printf("\r\n");
        }
        printf("j: down, k: up, h: left, l: right\n");
        printf("score: %d\n", snake->len - 3);
    }
    
    // Defined at the end of the file.
    int kbhit(void);
    void get_dir(int* vx, int* vy) {
        char c = ' ';
        if (kbhit()) {
            c = getchar();
        }
    
        switch (c) {
            case 'h':
                *vx = -1;
                *vy = 0;
                break;
            case 'j':
                *vy = 1;
                *vx = 0;
                break;
            case 'k':
                *vy = -1;
                *vx = 0;
                break;
            case 'l':
                *vx = 1;
                *vy = 0;
                break;
            default:; break;
        }
    }
    
    void update_table(Point* table, Snake* snake) {
        int vx = 0;
        int vy = 0;
        get_dir(&vx, &vy);
    
        // Ignore changing the velocity if trying to move backwards.
        if (!(snake->vx == -vx || snake->vy == -vy)) {
            snake->vx = vx;
            snake->vy = vy;
        }
        int inx = snake->inx + snake->vx + (snake->vy * W);
    
        // We are opting to move the tail before the head. So that the player
        // could run in a circle. Therefore we check for invalid index after
        // moving the tail.
    
        if (table[inx].c != APPLE_C) {
            // Move the tail one up in linked list
            Point* temp = snake->tail->n;
            snake->tail->n = NULL;
            snake->tail->c = ' ';
            snake->tail = temp;
        } else {
            // Don't move the tail and increase score (len) drop a new apple
            snake->len += 1;
            drop_apple(table);
        }
    
    #if PASS_WALL == 1
        // Validate the new head position.
        if (table[inx].c == BODY_C) {
            printf("Illegal index, you lost!\n");
            printf("Index %d\n", inx);
            exit(1);
        }
    
        // Move to the other side if hitting walls.
        if (table[inx].c == '|') {
            inx = (inx % W) > (W / 2) ? inx - (W - 2) : inx + (W - 2);
        } else if (table[inx].c == '-') {
            inx = (inx / W) > (H / 2) ? inx - ((H - 2) * W) : inx + ((H - 2) * W);
        }
    #else
        // Validate the new head position.
        if (table[inx].c != ' ' && table[inx].c != APPLE_C) {
            printf("Illegal index, you lost!\n");
            exit(1);
        }
    #endif
    
        // Set the new head position.
        table[inx].n = NULL;
        table[inx].c = HEAD_C;
        snake->head->n = &table[inx];
        snake->head->c = BODY_C;
        snake->head = &table[inx];
        snake->inx = inx;
    }
    
    int main(int argc, char** argv) {
        Point table[W][H];
        Snake snake = {.len = 3, .vx = 1, .vy = 0};
    
        init_game(&table[0][0], &snake);
    
        while (1) {
            update_table(&table[0][0], &snake);
            draw_table(&table[0][0], &snake);
    
            // loop's delay
            usleep(150000);
        }
    }
    
    // Copied from the internet for non-blocking, non-line-bufferd input read
    // Thanks to Thantos's answer in this thread:
    // https://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html
    int kbhit(void) {
        struct termios oldt;
        struct termios newt;
        int ch;
        int oldf;
    
        tcgetattr(STDIN_FILENO, &oldt);
        newt = oldt;
        newt.c_lflag &= ~(ICANON | ECHO);
        tcsetattr(STDIN_FILENO, TCSANOW, &newt);
        oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
        fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);
    
        ch = getchar();
    
        tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
        fcntl(STDIN_FILENO, F_SETFL, oldf);
    
        if (ch != EOF) {
            ungetc(ch, stdin);
            return 1;
        }
    
        return 0;
    }
    Last edited by sadegh; 12-21-2022 at 10:24 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. help with c snake game
    By stibs in forum C Programming
    Replies: 3
    Last Post: 12-28-2018, 06:19 AM
  2. Snake game
    By coder222 in forum C Programming
    Replies: 2
    Last Post: 12-08-2015, 03:03 PM
  3. Snake Game help on it please...
    By LekhAsh in forum C++ Programming
    Replies: 2
    Last Post: 01-30-2015, 01:45 AM
  4. Dat snake game...
    By esrelmantis in forum C++ Programming
    Replies: 3
    Last Post: 05-19-2013, 04:24 AM
  5. About SnaKE game..
    By ozumsafa in forum C Programming
    Replies: 3
    Last Post: 10-19-2007, 05:46 PM

Tags for this Thread