教你實現簡單的貪喫蛇------C語言版

前言

貪喫蛇相信大家小時候都玩過,那麼你有沒有想過有一天你也能自己編程實現它呢?今天我就在這裏教大家用C語言,通過自頂向下逐步求精的方法,實現一個簡易的貪喫蛇。

準備工作

(首先心裏要有點X數)既然是簡易的,那麼沒有高大上的畫面,我們實現的一定是字符版。爲了方便我們之後的思考,我們先把最基礎的幾個參數和初始參數定下來。

#include<stdio.h>
#include<conio.h>

#define SNAKE_MAX_LENGTH 20
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define BLANK_CELL ' '
#define WALL_CELL '*'
#define SNAKE_FOOD '$'
#define stay 'S'

char map[12][12] =
{ "************",
"*XXXXH     *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"************"
};
int snakeX[SNAKE_MAX_LENGTH] = { 5,4,3,2,1 };
int snakeY[SNAKE_MAX_LENGTH] = { 1,1,1,1,1 };
int snake_length = 5;

對沒錯,那個二元字符組就是遊戲界面,第二行那一條就是我們的蛇了。

一條會動、會死的蛇

秉着由簡入繁的原則,我們先來實現最基本的功能,就是一條能夠接受我們命令(WASD)動起來,而且撞到牆或者自己會死的蛇。讓我們先敲下僞代碼。

輸出字符矩陣
    WHILE not 遊戲結束 DO
        ch=輸入的字符
        move(ch);
        輸出字符矩陣
    END WHILE
    輸出 Game Over!!! 

然後按照僞代碼敲下main函數,以及我們秉着“自頂向下、逐步求精”的原則自己寫的move函數以及判斷死亡的函數。這裏用到了兩個小技巧,一個是getch()這個函數,可以讓程序不需要每次輸入後再敲個回車;另一個是p這個值(其實就是布爾值)來幫助記錄蛇的死活。

void print(char map[12][12]) {

    int i;
    for (i = 0; i < 12; i++) {
        int j;
        for (j = 0; j < 12; j++) {
            printf("%c", map[i][j]);
        }
        printf("\n");
    }
}

int hit_it_self(int X[], int Y[]) {
    int i;
    for(i = 2; i < snake_length; i++)
        if (X[0] == X[i] && Y[0] == Y[i]) return 0;
    return 1;
}

int hit_the_wall(int X[], int Y[]) {
    if (X[0] == 0 || X[0] == 11) return 0;
    if (Y[0] == 0 || Y[0] == 11) return 0;
    return 1;
}

void move(int x) {

    map[snakeY[snake_length - 1]][snakeX[snake_length - 1]] = ' ';
    map[snakeY[0]][snakeX[0]] = 'X';
    int i;
    tempX = snakeX[snake_length - 1];
    tempY = snakeY[snake_length - 1];
    for (i = 1; i < snake_length; i++) {
        snakeX[snake_length - i] = snakeX[snake_length - i - 1];
        snakeY[snake_length - i] = snakeY[snake_length - i - 1];
    }
    switch (x) {
    case 'A':
        snakeX[0] = snakeX[0] - 1;
        break;
    case 'W':
        snakeY[0] = snakeY[0] - 1;
        break;
    case 'D':
        snakeX[0] = snakeX[0] + 1;
        break;
    case 'S':
        snakeY[0] = snakeY[0] + 1;
        break;
    }
    map[snakeY[0]][snakeX[0]] = 'H';
}

int main() {
    print(map);
    int p = 1;
    char movement;
    while (p>0) {
        movement = getch();
        int x = movement;
        move(x);
        int p1 = hit_it_self(snakeX, snakeY);
        int p2 = hit_the_wall(snakeX, snakeY);
        if (p1 == 0 || p2 == 0) p = 0;
        else {
            system("cls");//清屏
            print(map);
        }
    }
    printf("GAME OVER");
}

放食物,喫食物

貪喫蛇的另一功能就是喫食物,然後變長。這裏有兩個難點:一個就是放置食物需要隨機,而C語言的隨機是隨機數表法生成的僞隨機,所以我們這裏就需要一點小技巧,根據啓動程序的時間來決定隨機的seed;另一個則是喫食物後我要實現立刻再生成一個食物,以及蛇的身體長度加一。下面就是相關的函數。

int food = 0;
int foodX, foodY;
int tempX, tempY;

void set_food(char map[12][12]){
     srand((int)time(0));
     foodX = (int)(rand()%10) + 1;
     foodY = (int)(rand()%10) + 1;
     int judge = 1;
     int i;
     for(i =0; i < snake_length; i++){
        if(snakeX[i] == foodX && snakeY[i] == foodY){
            judge = 0;
            break;
        }
     }
     if(judge == 1){
        map[foodY][foodX] = '$';
        food  = 1;
        //return;
     }
     //if(judge == 0) set_food(map);如果迭代就會偶爾導致程序崩潰。
}

void eat(char map[12][12]){
    snake_length++;
    snakeX[snake_length - 1] = tempX;
    snakeY[snake_length - 1] = tempY;
    map[tempY][tempX] = 'X';
    food = 0;
}

自己會動

接下來就是最後的部分,我需要蛇能夠自己動起來。就是說,當我沒有輸入的時候,蛇要自己沿着原方向繼續走。這裏我們用了兩個函數,一個是kbhit()來解決輸入的問題,另一個則是clock()或者sleep()來解決自己動的問題,我這裏選擇的是前者。其中kbhit的功能是有輸入則返回輸入值,沒有則返回0;clock()則是返回程序執行的時間。
具體代碼如下:(這裏筆者就將整個main函數再重新敲一遍了。)

char my_kbhit(char movement){
    if(kbhit()){
        movement = getch();
        return movement;
    }
    else return movement;
}

int main() {
    int game_time;
    print(map);
    int p = 1;
    char movement = stay;

    while (p>0) {
        game_time = clock();
        if(game_time % (1000 - 50 * snake_length)  == 0) {
            movement = my_kbhit(movement);

        while(food == 0){
            if(snake_length <= 20){
            set_food(map);
            }
        }

        int x = movement;
        move(x);
        int p1 = hit_it_self(snakeX, snakeY);
        int p2 = hit_the_wall(snakeX, snakeY);
        if (p1 == 0 || p2 == 0) p = 0;
        else {
            if(snakeX[0] == foodX && snakeY[0] == foodY) eat(map);
            system("cls");
            print(map);
        }
    }}
    if(snake_length == 20){
        system("cls");
        printf("YOU WIN");
        return;
    }
    printf("GAME OVER");
}

這裏爲了增加難度,我讓被模的數隨蛇的長度增加而減少,從而實現了蛇越長動得越快來給遊戲增加樂趣。

後記

學會了貪喫蛇,其實很多小遊戲的字符版就都可以實現了,比如 flappy bird、 或者一個簡單的“雷電”。當然讀者也可以自如發揮來美化這個程序,比如利用 system 的種種函數來設計邊框,背景顏色,亦或者想個辦法讓打印的時候不那麼閃,這些就留給讀者進一步自由發揮了。
總之目前的效果就是這樣啦。
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章