1. 程式人生 > >面向物件以及運用最大最小搜尋的井字棋設計(附原始碼)

面向物件以及運用最大最小搜尋的井字棋設計(附原始碼)

這裡寫圖片描述
一:實驗題目 井字棋遊戲設計
利用面向物件程式設計的知識,通過設計board、player、game類,實現一個具有人人對弈、人機對弈以及機機對弈的井字棋遊戲。
要求:
①對類設定和實現的要求
1.封裝:需要對遊戲中的資料進行相應的封裝保護。 在井字棋中的棋盤資料可以被 player 讀取並放置下一步棋,但 player 不應該有權 限對棋盤進行隨意改動。
2.繼承:playerHuman 和 playerComputer 派生於 player。
3.多型:game 類只調用 player。Player 可以作為虛基類,通過純虛擬函式實現多型
②對介面的要求
1. 介面清晰,互動友好,可以使玩家在不需要說明的情況下操作。
③對程式穩定性的要求
1. 程式應該具有一定的容錯性,可以在使用者輸入錯誤的情況下給出提示並且保持正常工作。

二:實驗目的
① 熟悉運用c++的封裝、繼承和多型。
② 掌握c++通過虛擬函式實現多型的辦法。
③ 學習通過基類指標繫結派生類物件的方法。
④ 熟悉工程的建立和測試。
⑤ 掌握最大最小搜尋法,進行博弈。

三:程式設計與測試
先大體說一下思路,具體演算法後面補上。首先,分析了一下每個類應該具有的作用:
Board類:
1.儲存一個每個點可以有三種狀態的3×3矩陣,
2.畫出棋盤
3.訪問棋盤上各點
4.設定棋盤上某一點
因此用一個3×3的int矩陣儲存資訊,-1和1代表已經落子,0代表未落子。然後設定Draw、Get、Set三個函式來分別畫出、訪問和設定棋盤。
Player類:
這是個比較開放的,為了方便,我的設定是這樣的
1. name,根據不同時候建立的時候給一個name,方便輸出結果,比如說,同樣是player_human,假如在人人對弈中,他可能叫“player1”或者“player2”,而在人機對弈中,他就叫“You”,再加上一個Get_name的函式,這樣在輸出遊戲結果的時候據更加合理而且方便。
2. movement,記錄當前走子(1~9),本來設定了悔棋這個功能,但是覺得程式冗餘了很多(每次下的時候都要問一句悔棋否?),一個這麼幾步的遊戲沒必要設定,所以後來純粹是作為記錄走子使用,可有可無。
3. mark,這是一個靜態成員變數,用來記錄當前有多少個玩家,作用下面說。
4. hand,這是個用來記錄先後手的成員變數,hand的設定是這樣的
hand = pow(-1,mark),也就是說先後手是根據玩家建立的順序確定的,因為每個遊戲只有兩個玩家,所以他們的hand一定是一正一負的,而且game結束之後,player會被析構掉,所以保證了player一定不超過兩個。
5.一個move的虛擬函式,用來確定落子,根據player型別不同,這個move函式實現不同,實現多型。
6.check函式,判定玩家落子是否正確,保證玩家不會在已經下了的位置或者棋盤外的位置下。

Player_human類:繼承於player
1. 沒有自己獨有的成員變數
成員函式:
1. Read_input讀入玩家的輸入
2. Move實現:利用check函式保證輸入合法後返回走子值

Player_weak_computer類:
1. move實現:產生一個1到9的隨機數,用check函式檢查到合法即返回。

Player_strong_computer類:
1. 利用極大極小搜尋尋求最佳策略。(詳細演算法後面解釋)

Game類:
設計思路是,一個game需要一個棋盤和兩名玩家,game的成員變數就是兩個player指標組,加上一個board,以及一個計算第幾輪的round變數。
成員函式:
1. 建構函式:需要變數是三個int值,分別指玩家選定的遊戲選項。
2. Get_round:獲取當前遊戲回合數
3. Add_round:在玩家走子後,讓回合數加一
4. Check:逐行逐列還有對角線檢查有沒有三子連線
5. Check_result:根據check的結果,判定當前局勢(玩家1/2獲勝,平局,進行中)
6. Get_result:根據check_result的結果,輸出對應的語句,並且如果遊戲結束返回true,否則返回false
7. Move:根據回合數交替呼叫對應玩家的move函式進行走棋,這裡是整個game函式的一個關鍵,為了發揮player類多型的優勢,上面game的成員變數是兩個由player類指標指向的不同的派生player,然後就可以通過round回合數確定該輪的玩家,進而通過這個指標呼叫基類的move函式介面,最終呼叫對應派生類的move函式。
8.
Main函式:
因為game函式設定的比較全面,所以主函式要做的工作非常少,就只要讓玩家輸入選取遊戲模式,然後對應地例項化一個game,接下來就是讓game一直move走棋,add_round,然後利用Get_result判斷遊戲是否結束,如果結束了再詢
問玩家是否繼續玩。

這裡寫圖片描述

Main函式+game建構函式思路:
1:詢問玩家是否開始遊戲
2:選擇模式:①人人對弈②人機對弈③機機對弈
3:再次選擇模式:如果是人機對弈,則選擇對弈弱電腦或強電腦,如果是機機對弈則選擇弱弱、強強、強弱對弈
4:如果是人人對弈、機機對弈中的弱弱對弈或強強對弈則不需要選擇誰先手,因為都一樣。如果是人機對弈或機機對弈中的強弱對弈則會詢問先手順序。

下面詳細說一下強AI用到的演算法——極大極小搜尋
極小極大的定義
Minimax演算法 又名極小極大演算法,是一種找出失敗的最大可能性中的最小值的演算法(即最小化對手的最大得益)。通常以遞迴形式來實現。
Minimax演算法常用於棋類等由兩方較量的遊戲和程式。該演算法是一個零總和演算法,即一方要在可選的選項中選擇將其優勢最大化的選擇,另一方則選擇令對手優勢最小化的一個,其輸贏的總和為0(有點像能量守恆,就像本身兩個玩家都有1點,最後輸家要將他的1點給贏家,但整體上還是總共有2點)。井字棋博弈就是一個對極大極小搜尋的經典應用。
使用Minimax演算法首先要設定不同玩家的最大利益,比如X玩家為正無窮(+∞),O玩家的最大利益為負無窮(-∞),這樣我們稱X玩家為MAX(因為他總是追求更大的值),成O玩家為MIN(她總是追求更小的值),各自都為爭取自己的最大獲益而努力。這裡要注意,同一個局面,無論對X玩家還是O玩家,它的評分都應該一樣。

現在,讓我們站在Min的立場來分析局勢(當然,也可以選擇Max立場,不過我的程式裡面用的是Min,那就以Min為最大利益)。於是構建出來的博弈樹如下(前面兩層,由於對稱性,只列舉三種情況):
這裡寫圖片描述
MAX總是會選擇MIN獲利中的最小值(對MAX最有利),同樣MIN也會一樣,選擇對自己最有利的(即MAX有可能獲得的最大值)。有點難理解,其實就是然後搶先把對對手有利的位置搶佔了,舉個例子,假如在一步走棋之後,對手有兩種走法,一種是直接贏,一種是遊戲繼續,假設對手足夠聰明,他肯定會選擇前者,那麼你的結局有兩個,輸或者遊戲繼續,為了獲取你的最大利益(達到Min),你肯定要阻止對手達到最優局面(Max),所以記錄不同走子得到的Max值,最後選取其中最小的一個。
整個過程不斷往下深鑽的,直到最底層(即葉節點)你才能往上回溯,確定那個是對你最有利的。
具體過程會像是這麼一個樣子的(網上插圖):
這裡寫圖片描述
一般來說,最大最小搜尋都要設定搜尋深度,當達到搜尋深度都還沒有得到遊戲結果,就需要一個評估函式得出當前局面的評估值,評估函式和搜尋深度很大程度上了AI的智慧程度(另一個辦法是通過剪枝把極大極小搜尋優化為Alpha-Beta搜尋,不過考慮到project說明裡面已經給了套路智慧的方法,也就是說完全可以依靠套路達到智慧,搜尋就沒有意義了,因此Alpha-Beta剪枝到極端就變成了純套路,只要搜尋一層或者不用搜索,演算法就失去意義了),因為井字棋可能局面不多,只有9!,因此我的評價函式就只有三種結果:輸贏或者平局。這樣的話,程式基本上是搜尋到底,不過這裡也造成了一個小“問題”,當局面為必勝的時候,AI有兩個選擇,①直接獲勝②製造下一輪有兩個獲勝位置的局面,這時候因為兩種情況的估值都是最有利的,因此AI會隨機選擇其中一個,當然這樣的情況可以通過在評價函式在補充一個對二連環的評估,返回一個小於贏但是大於平局的評估值來避免,鑑於最終結局都是贏,而且這樣更有趣味性,我保留了這個“小問題”。

程式測試圖:
遊戲設定選擇:
這裡寫圖片描述

遊戲選擇輸入異常處理

這裡寫圖片描述
遊戲過程:
這裡寫圖片描述

遊戲過程輸入異常處理:
這裡寫圖片描述

AI走棋後等待玩家確認:

這裡寫圖片描述
遊戲結束的提示:

這裡寫圖片描述

這裡寫圖片描述

四:實驗總結與心得
這次的實驗算得上是一個小工程了,在試驗中學到了不少實際中的工程方法。另外,整個程式的設計充分利用了C++面向物件設計的特點,對封裝、繼承、多型有了更深入的瞭解。下面是一些具體的收穫:
1. 類的宣告實現和主函式分離,工程上的需要,而且一些情況下,避免了程式碼重寫,易於使用和維護。
2. 學會建立工程(雖然是sublime幫我link的)。
3. 學會了使用ifdef、endif語句和pragma once語句防止重編譯。
4. 對面向物件程式設計有更深入的瞭解,懂得根據一個物件的屬性和功能進行分析,設計合適的成員變數和成員函式實現。
5. 對C++的繼承和多型,掌握更加熟練。
6. 掌握了極大極小演算法和Alpha-Beta演算法。

下面是一些實驗中的小發現:
1. 因為實驗中我的user陣列是基類player*型別的,在悔棋功能設計中,為了區別human和computer(只有human有悔棋功能),我嘗試使用sizeof(*user[0])這樣的方式獲得指標指向物件的大小(我在computer類裡面故意加了一個無用的int陣列,增加它的大小),結果發現,無論指標指向computer物件還是human物件,被指向的物件大小一樣(不是指標大小,是物件大小!)然後我用*player_human和*player_computer指標分別例項化了human和computer物件,發現這次他們的大小是不一樣的!回想起上次Homework寫的實驗報告,基類指標可以指向派生類物件,但是隻繼承了基類的成員變數和成員函式,
這裡寫圖片描述
下面把上次實驗報告寫的重新回顧一下:
多型下的指標呼叫,這是個比較難搞的知識點,下面粗略講一下。
1. 基類指標指向派生類,則只可以訪問基類函式和基類的成員變數,因此一般會通過虛擬函式提供介面訪問派生類成員變數。
2. 派生類指標指向基類,這樣做有一定的風險,必須通過合法轉換,因為派生類指標可以訪問派生類的成員函式,而基類物件不一定有派生類的成員函式或成員變數。
3. 如果基類和派生類有重定義函式,即派生類重定義了繼承的函式,則通過指標呼叫成員函式,取決於指標的初始型別(原因下面解釋),如果指標宣告時為基類指標,則會呼叫基類成員函式,反之呼叫派生類函式。
4. 如果基類和派生類有虛擬函式,則通過指標呼叫virtual函式,呼叫基類還是派生類的virtual取決於指標指向的物件型別。
⑥函式重定義和虛擬函式的區別(解釋上面)
最大的區別就是,重定義函式是靜態繫結的,而virtual函式是動態繫結的,也就是說重定義函式在編譯的時候根據宣告的型別就已經確定下來了,而virtual會在程式執行的時候根據指標指向的物件型別而改變。好奇之下,
查了一下實現原理,虛擬函式表,編譯原理層面,~卒,先mark down了。
2.#pragma once一句話就可以防止重編譯,比#ifdef #def #endif來得方便。(不知道在其他編譯器下可不可以)

原始碼:
board類

//board.h
#pragma once
#include <cstdio>
#include <iostream>
#include <cstdlib>
using namespace std;
#define cls() system("cls")


class board
{
private:
    int chessboard[4][4]; // for convenience
public:
    board()
    {
        for(int i = 1; i <= 3; i++)
        {
            for(int j = 1; j <= 3; j++)
                chessboard[i][j] = 0;
        }
    }
    int Get_board(int row,int col) const
    {
        if(row < 1 || row > 3)
            return -2;  //outside the boundary
        if(col < 1 || row > 3)
            return -2;
        return chessboard[row][col];
    }

    void Set_board(int row,int col,int hand)
    {
        if(row > 3 || row < 1 || col > 3 || col < 1) //outside the boundary
        {
            printf("outside the boundary\n");
            return ;
        }
        else
            chessboard[row][col] = hand;
    }

    void Draw_board() const //draw the current chess board
    {
        cls();
        for(int i = 0; i <= 4; i++)
        {
            for(int j = 0; j <= 4; j++)
            {
                if(i & 1 == 1)
                    printf("_");
                else
                {
                    if(j & 1 == 1)
                        printf("|");
                    else
                    {
                        int row = i / 2 + 1;
                        int col = j / 2 + 1;
                        if(chessboard[row][col] == 1) 
                            printf("O");
                        else if (chessboard[row][col] == -1)
                            printf("X");
                        else
                            printf(" " );
                    }
                }
            }
            cout << endl;
        }
    }
};

player類

// player.h

#pragma once

#include "board.h"


#define Wrong_input() printf("Wrong input! Please input again.\n")


#include <cstdlib>
#include <cstring>
#include <cmath>
#include <cstdio>
class player
{
protected:
    int hand;  //mark that whrther you move first
    int movement;//the player's current movement
    string name;

public:
    static int mark;
    virtual ~player()
    {
        mark--;  //remove a player
    }

    virtual int move(board) = 0;

    int Get_hand() const
    {
        return hand;
    }

    string Get_name()const
    {
        return name;
    }


    bool check(const board b) const
    {
        if(movement > 9 || movement < 1)
            return false;
        else 
        {
            int r = (movement - 1) / 3 + 1;
            int c = (movement - 1) % 3 + 1;
            if(b.Get_board(r,c) != 0)
                return false;
        }

            return true;
    }
};


class Player_Human :public player
{                        // human player
private:

public:
    Player_Human(string _name)
    {
        movement = 0;
        mark++;      // add a player
        hand = pow(-1,mark); 
        name = _name;
    }

    void Read_input(const board& b) 
    {
        char buf[30];
        cin.getline(buf,29);

        for(int i = 0; i < strlen(buf); i++)
        {
            if(buf[i] >= '1' && buf[i] <= '9') //find out the number in the input
            {
                movement = (int)buf[i] - 48; //turn char into int
                break;
            }
        }
        while(check(b) == false) //check whether the input is legal
        {
            Wrong_input();
            printf("Input again.\n");
            cin.getline(buf,29);
            //getchar();
            for(int i = 0; i < strlen(buf); i++)
            {
                if(buf[i] >= '0' && buf[i] <= '9') //find out the number in the input
                {
                    movement = (int)buf[i] - 48; //turn char into int
                    break;
                }
            }
        }
    }


    int move(board b)
    {
        printf("Please input your movement[1,9].\n");
        do
        {
            this -> Read_input(b);
        }
        while(check(b) == false);
        return movement;
    }
};


class Player_Weak_computer : public player
{                            //randon computer
public:
    Player_Weak_computer(string _name)
    {
        movement = 0;
        mark++;
        name = _name;
        hand = pow(-1,mark);
    }

    int move(board b)
        {
            int row,col;
            do
            {
                movement = rand() % 9 + 1;
            }
            while(check(b) == false);
            system("pause");
            return movement;
        }
};

class Player_Strong_computer : public player
{
private:
    const int INF,Processing,Draw;//INF = 100,Processing = 10,Draw = 0
     //INF means max_value,Processing means the game hasn't finshed and we need to search deeper 
    static int table[][3];
public:
    int none[100];
    Player_Strong_computer(string _name):INF(100),Processing(10),Draw(0)
    {
        movement = 0;
        mark++;
        name = _name;
        hand = pow(-1,mark);
    }

    int Get_game_state(board b)const
    {
        int state;
        int cnt[2];// cnt[0] counts computer side,cnt[1] counts human side
        bool finish = true;

        int r,c;
        for(int i = 0; i < 8; i++)
        {
            cnt[0] = cnt[1] = 0;
            for(int j = 0; j < 3; j++)
            {
                r = (table[i][j] - 1) / 3 + 1;
                c = (table[i][j] - 1) % 3 + 1;
                if(b.Get_board(r,c) == hand)
                    cnt[0]++;
                else if(b.Get_board(r,c) == -hand)
                    cnt[1]++;
                else//still has position null
                    finish = false;
            }
            if(cnt[0] == 3)
                return -INF;
            if(cnt[1] == 3)
                return INF;
        }
        if(finish)
            return Draw;
        else
            return Processing;
    }

    int Max_search(board b)
    {
        int state = this -> Get_game_state(b);
        if(state != Processing)
            return state;

        int value,best_value = -INF;
        for(int i = 1; i <= 3; i++)
        {
            for(int j = 1; j <= 3; j++)
            {
                if(b.Get_board(i,j) == 0)
                {
                    b.Set_board(i,j,-hand);
                    value = this -> Min_search(b); //to search your max_value = search your opponent's minimun value
                    if(best_value < value)
                        best_value = value;
                    b.Set_board(i,j,0);           //recover the board
                }
            }
        }
        return best_value;
    }

    int Min_search(board b)
    {
        int state = this -> Get_game_state(b);
        if(state != Processing)
            return state;

        int value,best_value = INF;
        for(int i = 1; i <= 3; i++)
        {
            for(int j = 1; j <= 3; j++)
            {
                if(b.Get_board(i,j) == 0)
                {
                    b.Set_board(i,j,hand);
                    value = this -> Max_search(b); //to search your min_value = search your opponent's maxmun value
                    if(best_value > value)
                        best_value = value;
                    b.Set_board(i,j,0);             //recover the board
                }
            }
        }
        return best_value;
    }

    int find_best_move(board b)
    {

        int best_value = INF;
        int index = 0;
        int best_move[9];
        for(int i = 1; i <= 9; i++)
        {
            int r = (i - 1) / 3 + 1;
            int c = (i - 1) % 3 + 1;
            if(b.Get_board(r,c) == 0)
            {
                b.Set_board(r,c,hand);
                int value = Max_search(b);
                //b.Draw_board();
                if(value < best_value)
                {
                    index = 0;
                    best_value = value;
                    best_move[0] = i;
                }
                else if(value == best_value)
                {
                    best_move[index++] = i;
                }
                b.Set_board(r,c,0);
            }
        }

        if(index > 0)
        {
            index = rand() % index ;
        }
        return best_move[index];
    }

    int move(board b)
    {
        movement = find_best_move(b);
        system("pause");
        return movement;
    }

};

game類

//game.h
#pragma once

#include "board.h"
#include "player.h"

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#define loop(i,u,v) for(int i = u; i <= v; i++)
#define rloop(i,u,v) for(int i = u; i >= v; i--)


class game
{
private:
    int round;
    player* user[2];
    board b;
public:

    game(int mode,int mode2,int game_order)
    {
        round = 1;
        if(mode == 1)//human vs human
        {
            user[0] = new Player_Human("player2"); //according to my setting, user[1] is the one who first move
            user[1] = new Player_Human("player1"); 
        }
        else if(mode == 2) // human vs computer
        {
            if(mode2 == 2)// human vs weak computer
            {
                if(game_order == 1) //human first
                {
                    user[1] = new Player_Human("You");
                    user[0] = new Player_Weak_computer("Weak computer");
                }
                else
                {
                    user[1] = new Player_Weak_computer("Weak computer");
                    user[0] = new Player_Human("You");
                }
            }
            else //human vs strong computer
            {
               if(game_order == 1) //human first
                {
                    user[1] = new Player_Human("You");
                    user[0] = new Player_Strong_computer("Strong computer");
                }
                else
                {
                    user[1] = new Player_Strong_computer("Strong computer");
                    user[0] = new Player_Human("You");
                }
            }
        }
        else // computer vs computer
        {
            if(mode2 == 1) //weak computer vs weak computer
            {

                user[1] = new Player_Weak_computer("Weak computer1");
                user[0] = new Player_Weak_computer("Weak computer2");
            }
            else if(mode2 == 2)//Weak computer vs strong computer
            {
                if(game_order == 1) // weak computer first
                {
                    user[1] = new Player_Weak_computer("Weak computer");
                    user[0] = new Player_Strong_computer("Strong computer");
                }
                else
                {
                    user[1] = new Player_Strong_computer("Strong computer");
                    user[0] = new Player_Weak_computer("Weak computer");
                }
            }
            else // Strong computer vs Strong conputer
            {
                user[1] = new Player_Strong_computer("Strong computer1");
                user[0] = new Player_Strong_computer("Strong computer2");
            }
        }
    }

    ~game()
    {
        delete *user;
    }

    int Get_round()
    {
        return round;
    }

    void Add_round()
    {
        round++;
    }

    void move()
    {
        b.Draw_board();
        int move = user[round % 2] -> move(b);
        int row = (move - 1) / 3 + 1;
        int col = (move - 1) % 3 + 1;
        if(b.Get_board(row,col) != 0)  //prevent wrong setting
        {
            printf("Wrong setting\n");
            return ;
        }

        int hand = user[round % 2] -> Get_hand();
        b.Set_board(row,col,hand);
        b.Draw_board();
    }

    bool Judge(int hand) //true means hand side win,false means he doesn't win but may not lose
    {
        bool flag;
        loop(i,1,3) //check row
        {
            flag = true;
            loop(j,1,3)
            {
                if(b.Get_board(i,j) != hand)
                {
                    flag = false;
                    break;
                }
            }
            if(flag == true)
                return true;
        }

        loop(i,1,3)       //check column
        {
            flag = true;
            loop(j,1,3)
            {
                if(b.Get_board(j,i) != hand)
                {
                    flag = false;
                    break;
                }
            }
            if(flag == true)
                return true;
        }


        for(int i = 1; i <= 3; i++)  //check diagonal line
        {
            bool flag = true;
            for(int j = 1; j <= 3; j++)
            {
                if(b.Get_board(j,i) != hand)
                {
                    flag = false;
                    break;
                }
            }
            if(flag == true)
                return true;
        }

        flag = true;
        for(int i = 1, j = 1; i <= 3; i++,j++)  //check diagonal line
        {
            if(b.Get_board(i,j) != hand)
            {
                flag = false;
                break;
            }
        }
        if(flag == true)
            return true;

        flag = true;
        for(int i = 1,j = 3; i <= 3; i++,j--)
        {
            if(b.Get_board(i,j) != hand)
            {
                flag = false;
                break;
            }
        }
        return flag;
    }

    int Check_result() // 1 means win, -1 means lose, 0 means not decided yet
    {
        if(round < 5)
            return 2;
        int hand = user[round % 2] -> Get_hand();
        if(Judge(hand) == true)
            return 1;
        if(Judge(-1 * hand) == true)// -1*hand means the opponent of the hand side
            return -1;
        if(round < 9)  // the game doesn't finish
            return 2;
        else           // round >= 9 means the game has finished
            return 0;  // 0 means draw
    }

    bool Get_result()  //bool to state whether the game has finished
    {
        if(this -> Check_result() == 1) //the player of this round wins
        {
            cout << user[round % 2] -> Get_name() << " win!" << endl;
            return true;
        }
        else if(this -> Check_result() == -1) //the player of this round loses
        {
            cout << user[round % 2 + 1] -> Get_name() << " win!" << endl;
            return true;
        }
        else if(this -> Check_result() == 0)  // the game draws
        {
            cout << "Draw!" << endl;
            return true;
        }
        else  // the game hasn't finshed
            return false;
    }
};

main函式

//main.cpp

#include "board.h"
#include "game1.h"
#include "player.h"


#define Wrong_input() printf("Wrong input! Please input again.\n")


#include <cmath>
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>

/*
#ifdef
#define loop(i,u,v) for(int i = u; i <= v; i++);
#define rloop(i,u,v) for(int i = u; i >= v; i--);
#endif
*/
using namespace std;
int player::mark = 0;//initialize the static parameter of player class
int Player_Strong_computer:: table [8][3] = { {1,2,3},{4,5,6},{7,8,9},{1,4,7},{2,5,8},{3,6,9},{1,5,9},{3,5,7} };


bool Judge_legal_input(char buf[],int min,int max,int& num)
{
    for(int i = 0; i < strlen(buf); i++)
    {
        int tmp = (int)buf[i] - 48;
        if(tmp >= min && tmp <= max)
        {
            num = tmp;
            return true;
        }
    }
    return false;
}

int main()
{
    srand(time(NULL));  //set random seed
    bool state = true; // to decide whethe the user want to play
    char buf[30];      
    while(state)
    {
        printf("Press any key to start a new game\nPress q to quit\n");

        cin.getline(buf,' ');
        if(buf[0] == 'q')
        {
            state = false;
            break;
        }

        int mode = 1; // 1:human vs human 2:human vs computer 3:cmoputer vs cmoputer
        int mode2 = 1;
        int order = 1;  //defaut order

        printf("1:human vs human\n");
        printf("2:human vs computer\n");
        printf("3:computer vs computer\n");

        cin.getline(buf,' ');
        while(Judge_legal_input(buf,1,3,mode) == false)
        {
            Wrong_input();
            cin.getline(buf,' ');
        }

        if(mode == 2)
        {
            printf("1:Strong computer\n2:Weak computer\n");
            cin.getline(buf,' ');
            while(Judge_legal_input(buf,1,2,mode2) == false)
            {
                Wrong_input();
                cin.getline(buf,' ');
            }

            printf("1:You play first\n2:Computer play first\n");
            cin >> buf;
            while(Judge_legal_input(buf,1,2,order) == false)
            {
                Wrong_input();
                cin.getline(buf,' ');
            }
        }
        else if(mode == 3)
        {
            printf("1:Weak computer vs weak computer\n" );
            printf("2:Weak computer vs strong computer\n" );
            printf("3:Strong computer vs strong computer\n" );
            cin.getline(buf,' ');
            while(Judge_legal_input(buf,1,3,mode2) == false)
            {
                Wrong_input();
                cin.getline(buf,' ');
            }
            if(mode2 == 2)
            {
                printf("1:Weak computer play first\n");
                printf("2:Strong computer play first\n");
                cin.getline(buf,' ');
                while(Judge_legal_input(buf,1,2,order) == false)
                {
                    Wrong_input();
                    cin.getline(buf,' ');
                }
            }
        }
        game chess(mode,mode2,order);

        while(chess.Get_round() <= 9)
        {
            chess.move();
            if(chess.Get_result() == true) //the game has finshed
                break; 
            chess.Add_round();
        }
    }
    return 0;
}