字符遊戲——貪吃蛇

還記得在我們小時候玩過的貪吃蛇嗎?正在學C語言的我,現在雖然無法完全還原那些年玩過的貪吃蛇經典小遊戲,但卻可以利用字符設計出低配版貪吃蛇啦!在下用了很長時間,終於完成了自己製作的第一款C語言遊戲,在這裏和大家分享!

                             字符·貪吃蛇遊戲設計及算法

在設計之前,我們首先要回憶貪吃蛇的基本玩法和細節:
1.蛇只能進行上下左右四個方向的移動
2.蛇可以吃到食物,吃到食物後會在蛇尾增加一節長度
3.蛇頭碰到牆壁或者自己的身體時遊戲結束
這便是最基本的貪吃蛇,此外還有很多拓展出的功能,這裏不再一一贅述。
在此基礎上,我製作了人工控制移動的貪吃蛇。
最開始,我們需要了解貪吃蛇的基本元素,如蛇頭蛇尾,食物,邊界等。

#define SNAKE_MAX_LENGTH 50
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define BLANK_CELL ' '
#define SNAKE_FOOD '$'
#define WALL_CELL '*'

char map[12][13] = { "************",
"*HXXXX     *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"************" };

人工蛇,顧名思義,就是自己控制蛇的走位,不斷吃食物壯大自己。
因此,上述三個基本特徵分別可以通過下面的方法實現:
1.輸入一個字符,判斷移動方向,以WASD爲例:W向上,A向左,S向下,D向右
2.在地圖上食物以“H 座標相等時蛇長+1
3.用IF條件判斷蛇頭H座標與牆壁或者與任意一處蛇身X座標是否相同,若是,遊戲結束
總體框架用僞代碼表示出來就是
print map[12][13]
while(not 遊戲結束)
生成食物
輸入字符c
SWIHCH(c)
CASE a:蛇頭向左走 break;
CASE d:蛇頭向右走 break;
CASE w:蛇頭向上走 break;
CASE s:蛇頭向下走 break;
END switch
END WHILE
print Game Over!!!
之後,利用函數不斷的完善。

其中,關於移動的函數是最重要的,算法如下:
length爲蛇長;
i爲循環值,初始化爲0;
snakex爲蛇身的x座標,snakey爲蛇身的y座標(snakex[0] snakey[0]分別爲蛇頭的x座標,y座標);

當輸入字符時,判斷蛇頭走向,以決定snakex[0]和snakey[0]的值變化,然後:

while(i <=length-1)DO
{snakex[i]=snakex[i-1]
snakey[i]=snakey[i-1]
}
便完成了移動。

之後,還有蛇吃食物的蛇長變化,
爲使其在蛇尾部分長度+1,設計如下算法:
length++;
lastx=snakex[length-1]
lasty=snakey[length-1]
MOVE;
IF(snake eat food)THEN
snakex[length-1]=lastx;
snakey[length-1]=lasty;
這樣就完成了蛇長的變化。
在總框架的基礎上,完成各部分的函數,這樣,貪吃蛇之人工蛇版本便完成了:

#include <stdio.h>
#include <stdlib.h>
#include<time.h>
#define SNAKE_MAX_LENGTH 50
#define SNAKE_HEAD 'H'
#define SNAKE_BODY 'X'
#define BLANK_CELL ' '
#define SNAKE_FOOD '$'
#define WALL_CELL '*'


char map[12][13] ={
"************",
"*XXXXH     *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"*          *",
"************" };
//全局變量:初始狀態表
int food = 0;//定義開始時食物數量爲0
int x = 0, y = 0;//定義食物的座標(x,y)
int snakex[SNAKE_MAX_LENGTH] = { 5, 4, 3, 2, 1 };//蛇身的橫座標
int snakey[SNAKE_MAX_LENGTH] = { 1, 1, 1, 1, 1 };//蛇身的縱座標
int lastX = 1, lastY = 1;//表示蛇尾部的座標
int length = 5;//定義初始長度,在該變量完成蛇長的變化
void move(int x, int y);//蛇移動函數
void output(void);//蛇移動效果打印函數
void clear(void);//蛇移動前的蛇圖像清空
void printmap(void);//打印
int gameover(void);//判定遊戲結束(碰到身子或邊界)
void snakefood(void);//用來生成食物
void eatfood(void);//蛇吃食物後身體增長
int main() {
    int i;
    printmap();
    //打印初始狀態圖
    char c;
    int a = 0, b = 0;
    while ((scanf_s("%c", &c)) != EOF)//輸入字母W,A,S,D
    {
        switch (c){//進行移動
        case 'a'://左移時
            a = 1;
            b = 0;
            snakefood();
            move(a, b);
            if (gameover())//判定遊戲是否結束
            {
                printf("\nGAME OVER!!!\n");
                break;
            }
            else
                printmap();
            break;
        case 'd'://右移
            b = 1;
            a = 0;
            snakefood();
            move(a, b);
            if (gameover())//判定遊戲是否結束
            {
                printf("\nGAME OVER!!!\n");
                break;
            }
            else
                printmap();
            break;
        case 's'://上移
            a = -1;
            b = 0;
            snakefood();
            move(a, b);
            if (gameover())//判定遊戲是否結束
            {
                printf("\nGAME OVER!!!\n");
                break;
            }
            else
                printmap();
            break;
        case 'w'://下移
            b = -1;
            a = 0;
            snakefood();
            move(a, b);
            if (gameover())//判定遊戲是否結束
            {
                printf("\nGAME OVER!!!\n");
                break;
            }
            else
                printmap();
            break;
        }
    }//判定貪吃蛇的移動(通過move函數實現)並通過output函數實現map上的變化

    return 0;
}
void move(int a, int b){
    int i;
    if (a == 1 && b == 0)
    {
        clear();//清空map
        lastX = snakex[length - 1];
        lastY = snakey[length - 1];//記錄當前蛇尾座標
        for (i = length - 1; i >= 1; i--)
        {
            snakex[i] = snakex[i - 1];
            snakey[i] = snakey[i - 1];//移動
        }
        snakex[0]--;//蛇頭移動
        eatfood();//判斷是否吃了食物
        output();//移動完成
    }
    if (a == 0 && b == 1)
    {
        clear();
        lastX = snakex[length - 1];
        lastY = snakey[length - 1];
        for (i = length - 1; i >= 1; i--)
        {
            snakex[i] = snakex[i - 1];
            snakey[i] = snakey[i - 1];
        }
        snakex[0]++;
        eatfood();
        output();
    }
    if (a == -1 && b == 0)
    {
        clear();
        lastX = snakex[length - 1];
        lastY = snakey[length - 1];
        for (i = length - 1; i >= 1; i--)
        {
            snakex[i] = snakex[i - 1];
            snakey[i] = snakey[i - 1];
        }
        snakey[0]++;
        eatfood();
        output();
    }
    if (a == 0 && b == -1)
    {
        clear();
        lastX = snakex[length - 1];
        lastY = snakey[length - 1];
        for (i = length - 1; i >= 1; i--)
        {
            snakex[i] = snakex[i - 1];
            snakey[i] = snakey[i - 1];
        }
        snakey[0]--;
        eatfood();
        output();
    }

}

void clear(void){
    int i;
    for (i = 0; i < length; i++)
        map[snakey[i]][snakex[i]] = BLANK_CELL;//將蛇原來位置清空
}
void output(void){
    int i;
    map[snakey[0]][snakex[0]] = SNAKE_HEAD;
    for (i = 1; i < length; i++)
        map[snakey[i]][snakex[i]] = SNAKE_BODY;//蛇移動後的位置

}
void printmap(void){
    int i;
    for (i = 0; i < 12; i++)
    {
        printf("%s\n", map[i]);
    }
    //蛇可以完成移動,並打印
}
int gameover(void){
    int i;
    int fail = 0;
    for (i = 1; i<length; i++)//用來判斷是否頭碰身子
    {
        if (snakex[0] == snakex[i] && snakey[0] == snakey[i])
            fail = 1;
    }
    if (snakex[0]>10 || snakey[0] > 10 || snakex[0] < 1 || snakey[0] < 1 || fail == 1)//前四個條件爲是否觸碰邊界,後一個條件判斷是否頭碰身子
        return 1;
    else
        return 0;
}
void snakefood(void){
    srand(time(NULL));

    if (food == 0)
    {   
        x = rand() % 10 + 1;
        y = rand() % 10 + 1;
        if (map[x][y] == ' ')//確保在空白位置出現食物
        {
            map[x][y] = SNAKE_FOOD;//在地圖可到達位置上隨機生成食物
            food++;//確保每次只出現一個食物
        }

    }
}
void eatfood(void){
    if (snakey[0] == x&&snakex[0] == y)//判斷蛇是否吃到食物
    {
        length++;
        food = 0;//食物已經被吃掉,清空
        snakex[length - 1] = lastX;
        snakey[length - 1] = lastY;//蛇長增加一個
    }
}

遊戲成果如下:這裏寫圖片描述

在後期,可以通過引用windows函數庫來優化界面,例如每次循環後清空整個界面,使其更加直觀等等,大家可以嘗試。

我們可以看到,先寫出總控代碼是多麼重要,它的清晰程度決定了你設計算法以及對應函數需要的時間和思考量。

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