C 語言初學教材 - 第三章 利用狀態控制遊戲

目前我們的猜數字遊戲很小,判斷三項條件利用三層巢狀的 if-else 陳述似乎也很夠用。然而當遊戲越來越複雜,例如需要做的條件判斷越來越多,如果只應用巢狀的 if-else 陳述進行控制權移轉,那會是很麻煩的事情。



設計軟體亦如是,大型程式該怎麼做控制權的移轉,常常也會讓開發者傷透腦筋。程式執行其實可以分成許多情況,就以猜數字遊戲來看,程式最後有五種情況,就是猜對數字、猜大超過 5 、猜大小於 5 、猜小超過 5 、 猜小小於 5 。


因此遊戲到最後的五種情況,也就是處於五種不同的狀態中。我們打算以狀態作為顯示最後結果的依據,這是說,猜數字遊戲會分成兩部份,首先是狀態設定,依三層巢狀的 if-else 陳述設定狀態,再來是依狀態顯示最後遊戲的訊息。


利用狀態操作程式的控制移轉是一種技巧,這個效果在我們目前的猜數字可能並不明顯,對我們往後繼續開發程式卻有莫大的助益。


我們提供利用狀態改寫的版本如下
#include <stdio.h>
#include <time.h>
#include <math.h>

enum STATE {RIGHT = 1, BIG_FOUR, BIG_FIVE, SMALL_FOUR, SMALL_FIVE};

int main(void)
{
    int answer = time(NULL);
    int guess;
    int diff;
    int state = 0;
    
    answer %= 25;
    if (answer == 0) {
        answer = 25;
    }
    
    printf("請猜一個數字 1 到 25: ");
    scanf("%d", &guess);
    diff = guess - answer;
    diff = (int) fabs(diff);
    
    if (guess == answer) {
        state = RIGHT;
    }
    else {
        if (guess > answer) {
            if (diff > 5) {
                state = BIG_FIVE;
            }
            else {
                state = BIG_FOUR;
            }
        }
        else {
            if (diff > 5) {
                state = SMALL_FIVE;
            }
            else {
                state = SMALL_FOUR;
            }
        }
    }
    
    if (state == RIGHT) {
        printf("猜對囉!\n");
    }
    else if (state == BIG_FIVE) {
        printf("猜的數字比答案大....還很遠,再加把勁!\n");
    }
    else if (state == BIG_FOUR) {
        printf("猜的數字比答案大....不錯喔!很接近了\n");
    }
    else if (state == SMALL_FIVE) {
        printf("猜的數字比答案小....還很遠,再加把勁!\n");
    }
    else if (state == SMALL_FOUR) {
        printf("猜的數字比答案小....不錯喔!很接近了\n");
    }
    else {
        printf("猜錯囉!\n");
    }
    
    return 0;
}

/* 《程式語言教學誌》的範例程式
    http://pydoing.blogspot.com/
    檔名:gn4.c
    功能:簡單的猜數字遊戲
    作者:張凱慶
    時間:西元2010年7月 */


第 5 行,我們利用了 C 語言設計一連串整數常數的關鍵字 enum ,來標示狀態
enum STATE {RIGHT = 1, BIG_FOUR, BIG_FIVE, SMALL_FOUR, SMALL_FIVE};


利用 enum 宣告出的特別的列舉型態,其為有名稱的列舉數,實際上是整數常數。這是說,列舉數中如果用等號設定初值,隨後的值都會逐項遞增 1 ,如果沒有用等號設定初值,第一項列舉數會被設成 0 ,隨後的值也都會逐項遞增 1 。


注意,列舉數的宣告不需要等號,首先是 enum ,然後空格,接上列舉型態的名稱,然後是用大括弧圍起來的列舉數。通常宣告列舉型態會放到引入標頭檔之下,也就是所有函數之外,這樣一來所有函數都可以使用。


所以五種狀態,我們用列舉數分別是 RIGHT 、 BIG_FOUR 、 BIG_FIVE 、 SMALL_FOUR 、 SMALL_FIVE ,程式的第 24 行到第 44 行為設定狀態,第 46 行到第 63 行則是依狀態顯示遊戲結果的訊息。


第 46 行到第 63 行,我們用了種不一樣的寫法
if (state == RIGHT) {
    printf("猜對囉!\n");
}
else if (state == BIG_FIVE) {
    printf("猜的數字比答案大....還很遠,再加把勁!\n");
}
else if (state == BIG_FOUR) {
    printf("猜的數字比答案大....不錯喔!很接近了\n");
}
else if (state == SMALL_FIVE) {
    printf("猜的數字比答案小....還很遠,再加把勁!\n");
}
else if (state == SMALL_FOUR) {
    printf("猜的數字比答案小....不錯喔!很接近了\n");
}
else {
    printf("猜錯囉!\n");
}


因為對不同條件判斷很常用到,所以 else 後面可以直接加另一個 if 陳述,最後的 else 則是可有可無,加的話程式就會執行以上皆非的情況,不加的話,以上皆非就會直接跳過,程式會繼續執行最後 else 底下大括弧的陳述。


來編譯執行看看吧!



問題與討論
  1. 為什麼要用狀態控制程式?
  2. 列舉數還可以有哪些應用?
  3. C 語言有哪些方式可以做多重選擇?
  4. 連續的 else if 最後若接 else ,這跟 switch-case 的 default 有什麼相似之處?




沒有留言: