C語言版貪喫蛇:第二部分

C語言版貪喫蛇:第二部分

本章將學習以下內容

  • 學習蛇的運動原理
    • 利用鏈表存儲 蛇每一節的座標
  • 根據座標打印出蛇
  • 利用隨機數生成食物
  • 打印食物

如何運動?

運動的原理:

類似動畫的原理,蛇每移動一次,重新繪製一次圖像,通過不斷的重繪,形成連續的動作。

如何運動:

  • 方法一:
    • 用數組存儲每一節蛇身的座標
    • 運動一次,蛇頭變換一次座標,後面的蛇身拷貝前一節蛇身的座標。
  • 方法二:

    • 考慮到,每運動一次,實際上只有蛇頭和蛇尾的位置有變化。
    • 可以通過將蛇尾接到蛇頭前面,就可以得到新的蛇位置。

比較兩種方法,可以發現方法二更爲簡便。
基於方法二的特點,數組不能滿足我們的需求

  • 若蛇頭放在數組的首位,蛇尾加不到蛇頭的前面。
  • 若蛇尾放在數組的首位,隨着不斷運動,數組不斷擴大。

所以這裏就需要介紹一種新的數據結構
也就是: 鏈表


蛇的存儲結構——鏈表

這裏的鏈表用數組實現,沒有指針。

簡單的說,這裏的鏈表就是一個結構體數組,數組裏的每一個元素都存有

  • 這一節蛇的座標 ( x , y )
  • 下一節在數組中的位置 next
  • 上一節在數組中的位置pre

當我們需要把蛇尾接到蛇頭的前面時,我們只需

  1. 要把蛇尾的 pre 設置爲蛇頭
  2. 把蛇頭的next 設置爲蛇尾
  3. head指向新的蛇頭
  4. nail指向新的蛇尾(原蛇尾的前一個)

通過這種方法,我們能夠保證蛇在數組裏的存儲區段不變。

除此之外,再定義兩個變量,存儲蛇頭,蛇尾在數組中的位置。
打印時,從蛇頭開始,利用 next,不斷往蛇尾移即可。


蛇的打印

  1. 定義相關變量
// 定義一個結構體用來存儲蛇的每節身體的座標
struct snake
{
    int x, y;
    int next; 
    //保存當前節點的下一個節點的位置(數組下標)
    int pre;  
    //保存上一個節點的位置
} S[100];

// 定義三個變量存 蛇長,蛇頭,蛇尾
int lenth, head, nail;
  1. 定義個函數printsnake( )打印蛇
// 打印蛇
void printsnake()
{
    int i, j = head;
    for (i = 1; i <= lenth; i++)
    {
        // 注意 裏面的參數是j,j從蛇頭移動到蛇尾
        gotoxy(Snake[j].x, Snake[j].y);
        printf("*");
        j = Snake[j].next;
    }
}
  1. 在init初始化函數加入蛇的初始化信息
void init()
{
    printwall();
    // 初始化蛇頭蛇尾的座標
    Snake[1].x = 4;
    Snake[1].y = 4;
    Snake[2].x = 4;
    Snake[2].y = 5;
    head = 1;
    nail = 2;
    lenth = 2;
    Snake[head].next = nail;
    Snake[nail].pre = head;
    printsnake();
}
  1. 編譯運行,測試是否正常打印

這裏寫圖片描述


食物

  1. 我們需要一個能夠生成隨機數的函數用來產生食物的座標,
  2. 同時還要一個函數判斷座標是否合法

    1. 不能與圍牆重合
    2. 不能與蛇重合


產生隨機數的方法

srand((unsigned long)time(0));
    a = rand() % 10;
  • 在包含了頭文件time.h後
  • 在程序的任意位置加上這兩段代碼
  • 運行後,a將獲得一個小於10的一個隨機整數。


判斷食物是否合法

int ok_food()
{
    // 判斷是否和牆重合
    // 橫座標不能等於1,或70 ; 縱座標不能等於1,或20
    if(food_x==1||food_x==70) return 0;
    if(food_y==1||food_y==20) return 0;
    // 判斷是否和蛇重合
    int j = head;
    for(int i = 1; i <= lenth; i++){
        if(food_x == Snake[j].x && food_y == Snake[j].y) return 0;
        j = Snake[j].next;
    }
    // 都沒有,則返回1
    return 1;
}


隨機打印食物

// 定義全局變量存儲食物位置
int food_x,food_y;

// 打印食物 @
void printfood()
{
    // 不斷產生隨機數,知道座標符合要求
    do
    {
        srand((unsigned long)time(0));
        food_x = rand() % 70;
        food_y = rand() % 20;
    }while(!ok_food());
    // ok_food()爲判斷食物是否合法的函數,合法返回1,不合法返回0
    gotoxy(food_x,food_y);
    printf("@");
}


在初始化函數init( ), 加入printfood( )

void init()
{
    //..
    //上一章代碼
    //..
    // 第一次打印食物
    printfood();
}
測試是否正常工作

這裏寫圖片描述


本章完整代碼

/*
    這裏是貪喫蛇源代碼

    chapter 1:
    解釋下頭文件:
    time.h 生成隨機數要用到
    windows.h 要用到裏面的函數 gotoxy
    ---------
    1.畫圍牆

    ===========================

    chapter 2:
    目標:繪製蛇,食物
        蛇的存儲結構:簡單的鏈表(數組實現功能)
        食物:隨機數的生成

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <windows.h>

// 定義一個結構體用來存儲蛇的每節身體的座標
struct snake
{
    int x, y;
    int next; //保存當前節點的下一個節點的位置(數組下標)
    int pre;  //保存上一個節點的位置
} Snake[100];

// 定義三個變量存 蛇長,蛇頭,蛇尾
int lenth, head, nail;
// 食物位置
int food_x, food_y;

// 光標移動函數
void gotoxy(int x, int y)
{
    COORD coord = {x, y};
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
// 打印蛇
void printsnake()
{
    int i, j = head;
    for (i = 1; i <= lenth; i++)
    {
        // 注意 裏面的參數是j,j從蛇頭移動到蛇尾
        gotoxy(Snake[j].x, Snake[j].y);
        printf("*");
        j = Snake[j].next;
    }
}
// 判斷食物是否合法
int ok_food()
{
    // 判斷是否和牆重合
    // 橫座標不能等於1,或70 ; 縱座標不能等於1,或20
    if(food_x==1||food_x==70) return 0;
    if(food_y==1||food_y==20) return 0;
    // 判斷是否和蛇重合
    int j = head;
    for(int i = 1; i <= lenth; i++){
        if(food_x == Snake[j].x && food_y == Snake[j].y) return 0;
        j = Snake[j].next;
    }
    // 都沒有,則返回1
    return 1;
}
// 打印食物
void printfood()
{
    // 不斷產生隨機數,知道座標符合要求
    do
    {
        srand((unsigned long)time(0));
        food_x = rand() % 70;
        food_y = rand() % 20;
    }while(!ok_food());
    // ok_food()爲判斷食物是否合法的函數,合法返回1,不合法返回0
    gotoxy(food_x,food_y);
    printf("@");
}

// 繪製圍牆
void printwall()
{
    /*
        chapter 1 繪製圍牆部分
        圍牆大小 70*20, 長70,寬20
    */

    // 繪製水平圍牆,
    for (int i = 1; i <= 70; i++)
    {
        gotoxy(i, 1);
        printf("#");
        gotoxy(i, 20);
        printf("#");
    }
    // 繪製豎直圍牆
    for (int i = 1; i <= 20; i++)
    {
        gotoxy(1, i);
        printf("#");
        gotoxy(70, i);
        printf("#");
    }
}
void init()
{
    printwall();
    // 初始化蛇
    Snake[1].x = 4;
    Snake[1].y = 4;
    Snake[2].x = 4;
    Snake[2].y = 5;
    head = 1;
    nail = 2;
    lenth = 2;
    Snake[head].next = nail;
    Snake[nail].pre = head;
    // 第一次打印蛇
    printsnake();
    // 第一次打印食物
    printfood();
}
int main()
{
    init(); // 初始化函數,繪製圍牆
    system("pause");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章