C++ 之 簡單的五子棋AI程序


     本人是大一新生,寒假無聊,抱着試試看的心態(沒有想到可以完成),寫了C++的簡單五子棋程序,開心。
     下面是效果圖:

     一、首先講講大致思路
            五子棋實現的基礎
                   二維數組是五子棋實現的基礎。二維數組就像一個棋盤,其中0等於空格,1等於黑棋,2等於白棋,這里電腦就好比只能理解0和1的仆人,我們通過映射,使得五子棋變成計算機能夠處理的0和1,進而計算機知道做我們要它做什么。
舉例:5個連成水平線的白棋  ———  00022222000;但是無論怎么樣,我們不是電腦,必須看到真正棋才會下(除非你會下盲棋),所以我們需要一個函數,把二維數組中的數字信息轉化為圖像信息。我的代碼如下:

void gobang::display(){
    system("cls");
    for(int i = 0; i < 15; i ++){
        for(int j = 0; j < 15; j ++){
            if(i == Y && j == X){
                cout << "";
            }
            else if(chessboard[i][j] == 1){
                cout << "";
            }
            else if(chessboard[i][j] == 2){
                cout << "";
            }
            else{
                cout << ". ";
            }
                
        }
        cout << endl;
    }
}

這個顯示函數是很“自由”的,你完全可以選擇更好的顯示方法,甚至有顏色什么的(我還不會...)。                      
 
                五子棋的類的大致瀏覽:               

                 數據:int chessboard[15][15]    //存儲棋盤信息

                             int Y, X                            //光標,用於玩家選擇位置落子
                             int player, computer        //記錄玩家和電腦棋子的顏色
                             point laozi                       //記錄最新落子位置,便於判斷是否連成五子(point 是 創建的一個結構體:struct point{int y, x})
                  最主要函數:void play()                        //最主要的函數,下面是該函數調用的函數
                  次要函數:
                            int selMode()                    //選擇模式
                            void turnPlayer()              //玩家操作
                            void turnComputer()        //電腦操作
                            bool isEnd()                     //判斷是否連成五子
                  其他函數
                  具體代碼如下:

 

class gobang{
private:
    int chessboard[15][15];          //記錄棋盤信息 
    int player;                      //玩家棋子 
    int computer;                    //電腦棋子 
    int Y, X;                        //光標位置 
    point laozi;                     //落子位置 
public:
    void play();
        int selMode();
        void turnPlayer();
        void turnComputer(); 
        bool isEnd();
protected:
    bool isInBoard(point p); 
    void display();
    int score(point p, int who);
    point newPoint(point p, dir d, int lenth);
};

 


           五子棋的步驟(play函數):選擇模式(選擇電腦先手/玩家先手+數據初始化),循環(玩家/電腦操作 + 判斷是否結束,如果是,說明勝利棋方跳出循環,如果不是,交換棋方)
           下面是所需要的代碼(除了電腦操作部分,比其他復雜一些,最后說)。
            play函數

void gobang::play(){
    int i = selMode();
    if(i == 1){
        computer = 1;
        player = 2;
    }
    else{
        player = 1;
        computer = 2;
    }
    int cur = 1;
    while(1){
        if(cur == player){
            turnPlayer();
        }
        else{
            turnComputer();
        }
        if(isEnd()){
            if(cur == player){
                cout << "Player Win !";
                system("pause");
                break;
            }    
            else{
                cout << "Computer Win !";
                system("pause");
                   break; 
            }
        }
        else{
            cur = (cur == 1) ? 2 : 1;
        }        
    }
} 

           選擇模式:這個函數包含兩個部分,選擇 和 數據初始化

int gobang::selMode(){
    system("cls");
    cout << "*************************************************" << endl;
    cout << "******************0、退出************************" << endl;
    cout << "******************1、電腦先手********************" << endl;
    cout << "******************2、玩家先手********************" << endl;
    cout << "*************************************************" << endl;
    while(1){
        int i;
        cin >> i;
        if(i == 0){
            exit(1);
        }
        else if(i == 1 || i == 2){
            Y = 9;
            X = 9;
            for(int j = 0; j < 15; j ++){
                for(int k = 0; k < 15; k ++){
                    chessboard[j][k] = 0;
                }
            }
            display();
            return i;
        }
        else{
            cout << "非法輸入,請重新輸入!" << endl;
        }
    }
}

                    玩家操作:玩家操作我采用光標Y,X可視化(display()) 和 鍵盤操作的方法(w:上 ,s : 下 , a : 左, d : 右, j : 確定落子)。其中操作函數中都會有laozi這個數據,是方便后面的判斷是否結束的

void gobang::turnPlayer(){
    cout << "turn player" << endl;    
    while(1){
        char c = getch();
        if(c == 'w'){
            if(Y != 0){
                Y --;
                display();
            }
        }
        else if(c == 's'){
            if(Y != 14){
                Y ++;
                display();
            }
            
        }
        else if(c == 'a'){
            if(X != 0){
                X --;
                display();
            }
        }
        else if(c == 'd'){
            if(X != 14){
                X ++;
                display();
            }
        }
        else if(c == 'j' && chessboard[Y][X] == 0){
            laozi.y = Y;
            laozi.x = X;
            chessboard[Y][X] = player;
            display();
            break;
        }
    } 
}

                     判斷是否結束:

bool gobang::isEnd(){
    for(int i = 1; i <= 4; i ++){
        dir d;
        int count = 0;
        switch(i){
            case 1:
                d = d1;
                break;
            case 2:
                d = d2;
                break;
            case 3:
                d = d3;
                break;
            case 4:
                d = d4;
                break;
        }
        for(int j = -4; j <= 4; j ++){
            point p1 = newPoint(laozi, d, j);
            if(isInBoard(p1) && chessboard[p1.y][p1.x] == chessboard[laozi.y][laozi.x]){
                count ++;
            }
            else{
                count = 0;
            }
            if(count == 5){
                return true;
            }
        }
    }
    return false;
}

在這個函數里出現了一個新的函數——newpoint(point p, dir d, int lenth),其中point p 為 點的位置, int lenth 為長度, dir d 為單位向量
dir d 講解 :dir 是創建的一個結構體,用來表示單位向量。它有很大的作用,通過它可以把橫豎撇捺4個方向用一個簡單的dir d 的 方向變量來處理,大大減少了代碼量。代碼如下:

struct dir{
    int dy;
    int dx;
};

const dir d1 = {0, 1};               //
const dir d2 = {1, 0};               //
const dir d3 = {1, -1};              //
const dir d4 = {1, 1};               //

point gobang::newPoint (point p, dir d, int lenth){
    point p1 = {p.y + d.dy * lenth, p.x + d.dx * lenth};
    return p1;
}

      二、核心部分(電腦操作)講解
             電腦的下棋就是要找出最佳點。什么是最佳點,最佳點的判斷指標是什么?這就需要給每個點打分。
             打分的具體方法
                   1.在棋盤空位置上預先添加要判斷的棋子;
                   2.選擇橫豎撇捺中的一個方向(利用dir d);
                   3.找出與中心點相連的棋子數量,並且利用left[5],right[5]數組存儲連子左右兩邊各4個棋子的顏色情況(可能會遇到邊界問題,解決辦法:通過 isInBoard(point p) 函數判斷棋子是否出邊界,如果出邊界,則棋子顏色視為反方(opp)棋子存儲在數組中,如果沒有出邊界,則棋子按本來情況存儲在數組中。
                   4.利用連子數量以及連子左右兩邊各4個棋子的顏色情況來判斷棋型,是否為連五,活四(高級/低級),沖死,活三(高級或低級),眠三(高級或低級),活二,低級活二,眠二中的一種。
                   5.綜合四個方向,進行打分:

情況15給予14分;
情況2能成4或者是雙死4或者是43給予13

 

情況3:能雙活3給予12

 

情況4:能33(高級)給予11

 

情況5能成4給予10

 

情況6能成低級死4給予9

 

情況7能成單活3給予8

 

情況8能成跳活3給予7

 

情況9能成雙活2給予6

 

情況10能成2給予5

 

情況11能成低級活2給予4

 

情況12能成3給予3

 

情況13能成2給予2分;

 

情況14其他情況(nothing給予1分。


             五子棋棋型知識(如果不知道可以看這里):

最常見的基本棋型大體有以下幾種:連五,活四,沖四,活三,眠三,活二,眠二

①連五:顧名思義,五顆同色棋子連在一起,不需要多講。
圖2-1  

②活四:有兩個連五點(即有兩個點可以形成五),圖中白點即為連五點。
稍微思考一下就能發現活四出現的時候,如果對方單純過來防守的話,是已經無法阻止自己連五了。
圖2-2  


③沖四:有一個連五點,如下面三圖,均為沖四棋型。圖中白點為連五點。
相對比活四來說,沖四的威脅性就小了很多,因為這個時候,對方只要跟着防守在那個唯一的連五點上,沖四就沒法形成連五。
圖2-3  圖2-4  圖2-5  


④活三:可以形成活四的三,如下圖,代表兩種最基本的活三棋型。圖中白點為活四點。
活三棋型是我們進攻中最常見的一種,因為活三之后,如果對方不以理會,將可以下一手將活三變成活四,而我們知道活四是已經無法單純防守住了。所以,當我們面對活三的時候,需要非常謹慎對待。在自己沒有更好的進攻手段的情況下,需要對其進行防守,以防止其形成可怕的活四棋型。
圖2-6  圖2-7 

其中圖2-7中間跳着一格的活三,也可以叫做跳活三

⑤眠三只能夠形成沖四的三,如下各圖,分別代表最基礎的六種眠三形狀。圖中白點代表沖四點。眠三的棋型與活三的棋型相比,危險系數下降不少,因為眠三棋型即使不去防守,下一手它也只能形成沖四,而對於單純的沖四棋型,我們知道,是可以防守住的。
圖2-8  圖2-9  圖2-10 

2-11 圖2-12 圖2-13 

如上所示,眠三的形狀是很豐富的。對於初學者,在下棋過程中,很容易忽略不常見的眠三形狀,例如圖2-13所示的眠三。

有新手學了活三眠三后,會提出疑問,說活三也可以形成沖四啊,那豈不是也可以叫眠三?
會提出這個問題,說明對眠三定義看得不夠仔細:眠三的的定義是,只能夠形成沖四的三。而活三可以形成眠三,但也能夠形成活四。

此外,在五子棋中,活四棋型比沖四棋型具有更大的優勢,所以,我們在既能夠形成活四又能夠形成沖四時,會選擇形成活四。

溫馨提示:學會判斷一個三到底是活三還是眠三是非常重要的。所以,需要好好體會。
后邊禁手判斷的時候也會有所應用。 

⑥活二:能夠形成活三的二,如下圖,是三種基本的活二棋型。圖中白點為活三點。
活二棋型看起來似乎很無害,因為他下一手棋才能形成活三,等形成活三,我們再防守也不遲。但其實活二棋型是非常重要的,尤其是在開局階段,我們形成較多的活二棋型的話,當我們將活二變成活三時,才能夠令自己的活三綿綿不絕微風里,讓對手防不勝防。
圖2-14  圖2-15  圖2-16 

⑦眠二:能夠形成眠三的二。圖中四個為最基本的眠二棋型,細心且喜歡思考的同學會根據眠三介紹中的圖2-13找到與下列四個基本眠二棋型都不一樣的眠二。圖中白點為眠三點。
圖2-17 圖2-18  
圖2-19  圖2-20 

這個部分是網上摘抄,網址:http://game.onegreen.net/wzq/HTML/142336.html   
      既然我們有了給每個點打分的方法,就該尋找最佳點了,但是這還不夠,因為我們知道,下棋一般有兩個思考方向,進攻或者防守(如果在給某點打分時放入的棋子為電腦棋子,則為進攻,反之,則為防守)。為什么要把進攻和防守分開考慮呢?為什么不可以把進攻得分和防守得分相加起來呢?我也試過,首先要把每種棋型的得分進行更改,即使這樣也往往得不到一個好的結果。我可以通過一個比喻說明這個問題:某個學校舉辦學科競賽,有語文和數學兩個科目,但是每個班可以選擇一名同學參加其中一項比賽,為此,老師對每個同學進行語文和數學測試,測試后,老師有2個方法尋找最佳的學生:1.語文和數學成績一起考慮(比如相加)來尋找最佳學生;2.分別找出語文和數學成績最好的兩名同學,在比較選擇。你們認為那種方法選出的學生比賽取得好成績的概率更高呢?

             通過這個比喻,我們尋找最佳點的方法也就出來,分別求出最佳進攻點,如果有多個最佳進攻點,則選擇其中防守得分最高的,對於最佳防守點也如此。然后進一步比較,如果最佳進攻點得發大於等於最佳防守點,則以最佳進攻點為最佳點,反之,最佳防守點為最佳點。
具體代碼如下:
void turnComputer()

 

void gobang::turnComputer(){
    cout << "turn computer" << endl; 
    point best1, best2;
    do{
        srand(time(NULL));
        best1.y = best2.y = rand()%15;
        best1.x = best2.x = rand()%15;
    } 
    while(chessboard[best1.y][best1.x] != 0);
    int a1 = score(best1, computer), b1 = score(best1, player);
    for(int i = 0; i < 15; i ++){
        for(int j = 0; j < 15; j ++){
            if(chessboard[i][j] != 0){
                continue;
            }
            point cur = {i, j};
            int m1 = score(cur, computer);
            int m2 = score(cur, player);
            if(m1 > a1){
                best1 = cur;
                a1 = m1;
                b1 = m2; 
            }
            else if(m1 == a1){
                if(m2 > b1){ 
                    best1 = cur;
                    b1 = m2;
                }
            }
        }
    }
    int a2 = score(best2, player), b2 = score(best2, computer);
    for(int i = 0; i < 15; i ++){
        for(int j = 0; j < 15; j ++){
            if(chessboard[i][j] != 0){
                continue;
            }
            point cur = {i, j};
            int m1 = score(cur, player);
            int m2 = score(cur, computer);
            if(m1 > a2){
                best2 = cur;
                a2 = m1;
                b2 = m2;
            }
            else if(m1 == a2){
                if(m2 > b2){
                    best2 = cur;
                    b2 = m2;
                }
            }
        }
    }    
    if(a1 >= a2){
        laozi = best1;
    }
    else{
        laozi = best2;
    }
    chessboard[laozi.y][laozi.x] = computer;
    display();
}

void score(point p, int who)

int gobang::score(point p, int who){
    int win5 = 0, alive4 = 0, die4 = 0, ddie4 = 0, alive3 = 0, 
    dalive3 = 0, die3 = 0, alive2 = 0, dalive2 = 0, die2 = 0, nothing = 0;
    int opp;
    if(who == 1){
        opp = 2;
    }
    else{
        opp = 1;
    }
    for(int i = 1; i <= 4; i ++){
        dir d;
        switch(i){
            case 1:
                d = d1;
                break;
            case 2:
                d = d2;
                break;
            case 3:
                d = d3;
                break;
            case 4:
                d = d4;
                break;
       }
       int l = 1;
       point le, ri, p1;
       int left[5], right[5];
       p1 = newPoint(p, d, -1);
       le = p;
       while(isInBoard(p1) && chessboard[p1.y][p1.x] == who){
               le = p1;
               p1 = newPoint(p1, d, -1);
            l ++;
       }
       p1 = newPoint(p, d, 1);
       ri = p;
       while(isInBoard(p1) && chessboard[p1.y][p1.x] == who){
            ri = p1;
            p1 = newPoint(p1, d, 1);
            l ++;
       }
       for(int j = 1; j <= 4; j ++){
               p1 = newPoint(le, d, -j);
               if(isInBoard(p1)){
                   left[j] = chessboard[p1.y][p1.x];
            }
            else{
                left[j] = opp;
            }
               p1 = newPoint(ri, d, j);
               if(isInBoard(p1)){
                   right[j] = chessboard[p1.y][p1.x];
            }
            else{
                right[j] = opp;
            }
       }
       //具體棋型判斷
       if(l == 5){
           win5 ++;
       }
       else if(l == 4){
              if(left[1] == 0 && right[1] == 0){//alive4 
                      alive4 ++;
           }
           else if(left[1] == 0 || right[1] == 0){//die4
                   die4 ++;
           }
           else{//nothing
                   nothing ++;
           }
       }
       else if(l == 3){
              if((left[1] == 0 && left[2] == who) || (right[1] == 0 && right[2] == who)){//ddie4
                      ddie4 ++;
           }
           else if(left[1] == 0 && right[1] == 0 && (left[2] == 0 || right[2] == 0)){//alive3
                   alive3 ++;                             
           }
           else if((left[1] == 0 && left[2] == 0) || (right[1] == 0 && right[2] == 0)){//die3
                   die3 ++;
           }
           else if(left[1] == 0 && right[1] == 0){//die3
                   die3 ++; 
           } 
           else{//nothing
                   nothing ++;
           }
       }
       else if(l == 2){
              if((left[1] == 0 && left[2] == who && left[3] == who) && 
                 (right[1] == 0 && right[2] == who && right[3] == who)){//die4
                   ddie4 ++;
           }
           else if(left[1] == 0 && right[1] == 0 && 
                   ((left[2] == who && left[3] == 0) || (right[2] == who && right[3] == 0))){//dalive3
                   dalive3 ++;
           }
           else if((left[1] == 0 && left[3] == 0 && left[2] == who) || 
                   (right[1] == 0 && right[3] == 0 && right[2] == who)){//die3
                die3 ++;
           }
           else if((left[1] == 0 && right[1] == 0) && 
                   (left[2] == who || right[2] == who)){//die3
                   die3 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[3] == who) || 
                   (right[1] == 0 && right[2] == 0 && right[3] == who)){//die3
                die3 ++;
           }
           else if((left[1] == 0 && right[1] == 0 && right[2] == 0 && right[3] == 0) || 
                   (left[1] == 0 && left[2] == 0 && right[1] == 0 && right[2] == 0) || 
                   (left[1] == 0 && left[2] == 0 && left[3] == 0 && right[1] == 0)){//alive2
                   alive2 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[3] == 0) || 
                   (right[1] == 0 && right[2] == 0 && right[3] == 0)){//die2
                   die2 ++;
           }
           else{//nothing
                   nothing ++;
           }
       }
       else if(l == 1){
              if((left[1] == 0 && left[2] == who && left[3] == who && left[4] == who) || 
                 (right[1] == 0 && right[2] == who && right[3] == who && right[4] == who)){//ddie4
                   ddie4 ++;
           }
           else if((left[1] == 0 && right[1] == 0) && ((left[2] == who && left[3] == who && left[4] == 0) || 
                   (right[2] == who && right[3] == who && right[4] == 0))){//dalive3
                   dalive3 ++;
           }
           else if((left[1] == 0 && right[1] == 0) && 
                   ((left[2] == who && left[3] == who) || (right[2] == who && right[3] == who))){//die3
                   die3 ++;
           }
           else if((left[1] == 0 && left[4] == 0 && left[2] == who && left[3] == who) || 
                   (right[1] == 0 && right[4] == 0 && right[2] == who && right[3] == who)){//die3
                   die3 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[3] == who && left[4] == who) || 
                   (right[1] == 0 && right[2] == 0 && right[3] == who && right[4] == who)){//die3
                   die3 ++;
           } 
           else if((left[1] == 0 && left[3] == 0 && left[2] == who && left[4] == who) || 
                   (right[1] == 0 && right[3] == 0 && right[2] == who && right[4] == who)){//die3
                   die3 ++;
           }
           else if((left[1] == 0 && right[1] == 0 && right[3] == 0 && right[2] == who) && (left[2] == 0 || right[4] == 0)){//dalive2
                   dalive2 ++;
           }
           else if((right[1] == 0 && left[1] == 0 && left[3] == 0 && left[2] == who) && 
                   (right[2] == 0 || left[4] == 0)){//dalive2
                   dalive2 ++;
           }
           else if((left[1] == 0 && right[1] == 0 && right[2] == 0 && right[4] == 0 && right[3] == who) || 
                   (right[1] == 0 && left[1] == 0 && left[2] == 0 && left[4] == 0 && left[3] == who)){//dalive2
                   dalive2 ++;
           }
           else if((left[1] == 0 && left[3] == 0 && left[4] == 0 && left[2] == who) || 
                   (right[1] == 0 && right[3] == 0 && right[4] == 0 && right[2] == who)){//die2
                   die2 ++;
           }
           else if((left[1] == 0 && right[1] == 0 && right[2] == 0 && left[2] == who) || 
                   (right[1] == 0 && left[1] == 0 && left[2] == 0 && right[2] == who)){//die2
                   die2 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[4] == 0 && left[3] == who) || 
                   (right[1] == 0 && right[2] == 0 && right[4] == 0 && right[3] == who)){//die2
                   die2 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && right[1] == 0 && left[3] == who) || 
                   (right[1] == 0 && right[2] == 0 && left[1] == 0 && right[3] == who)){//die2
                   die2 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[3] == 0 && left[4] == who) || 
                   (right[1] == 0 && right[2] == 0 && right[3] == 0 && right[4] == who)){//die2
                   die2 ++;
           }
           else{//nothing
                   nothing ++;
           }
       }
    }
    if (win5 >= 1)
        return 14;//贏5

    if (alive4 >= 1 || die4 >= 2 || (die4 >= 1 && alive3 >= 1))
        return 13;//活4 雙死4 死4活3

    if (alive3 >= 2)
        return 12;//雙活3

    if (die3 >= 1 && alive3 >= 1)
        return 11;//死3高級活3

    if (die4 >= 1)
        return 10;//高級死4

    if (ddie4 >= 1)
        return 9;//低級死4

    if (alive3 >= 1)
        return 8;//單活3

    if (dalive3 >= 1)
        return 7;//跳活3

    if (alive2 >= 2)
        return 6;//雙活2

    if (alive2 >= 1)
        return 5;//活2

    if (dalive2 >= 1)
        return 4;//低級活2

    if (die3 >= 1)
        return 3;//死3

    if (die2 >= 1)
        return 2;//死2

    return 1;//沒有威脅
}

 

 

        

       三、完整代碼

#include <iostream>
#include <conio.h>
#include <ctime>
#include <cstdlib>
using namespace std;
 
struct point{
    int y;
    int x;
}; 
struct dir{
    int dy;
    int dx;
};
const dir d1 = {0, 1};//
const dir d2 = {1, 0};//
const dir d3 = {1, -1};//
const dir d4 = {1, 1};//

class gobang{
private:
    int chessboard[15][15];//記錄棋盤信息 
    int player;//玩家棋子 
    int computer;//電腦棋子 
    int Y, X;//光標位置 
    point laozi;//落子位置 
public:
    void play();
        int selMode();
        void turnPlayer();
        void turnComputer(); 
        bool isEnd();
protected:
    bool isInBoard(point p); 
    void display();
    int score(point p, int who);
    point newPoint(point p, dir d, int lenth);
};
void gobang::play(){
    int i = selMode();
    if(i == 1){
        computer = 1;
        player = 2;
    }
    else{
        player = 1;
        computer = 2;
    }
    int cur = 1;
    while(1){
        if(cur == player){
            turnPlayer();
        }
        else{
            turnComputer();
        }
        if(isEnd()){
            if(cur == player){
                cout << "Player Win !";
                system("pause");
                break;
            }    
            else{
                cout << "Computer Win !";
                system("pause");
                   break; 
            }
        }
        else{
            cur = (cur == 1) ? 2 : 1;
        }        
    }
} 
int gobang::selMode(){
    system("cls");
    cout << "*************************************************" << endl;
    cout << "******************0、退出************************" << endl;
    cout << "******************1、電腦先手********************" << endl;
    cout << "******************2、玩家先手********************" << endl;
    cout << "*************************************************" << endl;
    while(1){
        int i;
        cin >> i;
        if(i == 0){
            exit(1);
        }
        else if(i == 1 || i == 2){
            Y = 9;
            X = 9;
            for(int j = 0; j < 15; j ++){
                for(int k = 0; k < 15; k ++){
                    chessboard[j][k] = 0;
                }
            }
            display();
            return i;
        }
        else{
            cout << "非法輸入,請重新輸入!" << endl;
        }
    }
}
void gobang::turnPlayer(){
    cout << "turn player" << endl;    
    while(1){
        char c = getch();
        if(c == 'w'){
            if(Y != 0){
                Y --;
                display();
            }
        }
        else if(c == 's'){
            if(Y != 14){
                Y ++;
                display();
            }
            
        }
        else if(c == 'a'){
            if(X != 0){
                X --;
                display();
            }
        }
        else if(c == 'd'){
            if(X != 14){
                X ++;
                display();
            }
        }
        else if(c == 'j' && chessboard[Y][X] == 0){
            laozi.y = Y;
            laozi.x = X;
            chessboard[Y][X] = player;
            display();
            break;
        }
    } 
}
void gobang::turnComputer(){
    cout << "turn computer" << endl; 
    point best1, best2;
    do{
        srand(time(NULL));
        best1.y = best2.y = rand()%15;
        best1.x = best2.x = rand()%15;
    } 
    while(chessboard[best1.y][best1.x] != 0);
    int a1 = score(best1, computer), b1 = score(best1, player);
    for(int i = 0; i < 15; i ++){
        for(int j = 0; j < 15; j ++){
            if(chessboard[i][j] != 0){
                continue;
            }
            point cur = {i, j};
            int m1 = score(cur, computer);
            int m2 = score(cur, player);
            if(m1 > a1){
                best1 = cur;
                a1 = m1;
                b1 = m2; 
            }
            else if(m1 == a1){
                if(m2 > b1){ 
                    best1 = cur;
                    b1 = m2;
                }
            }
        }
    }
    int a2 = score(best2, player), b2 = score(best2, computer);
    for(int i = 0; i < 15; i ++){
        for(int j = 0; j < 15; j ++){
            if(chessboard[i][j] != 0){
                continue;
            }
            point cur = {i, j};
            int m1 = score(cur, player);
            int m2 = score(cur, computer);
            if(m1 > a2){
                best2 = cur;
                a2 = m1;
                b2 = m2;
            }
            else if(m1 == a2){
                if(m2 > b2){
                    best2 = cur;
                    b2 = m2;
                }
            }
        }
    }    
    if(a1 >= a2){
        laozi = best1;
    }
    else{
        laozi = best2;
    }
    chessboard[laozi.y][laozi.x] = computer;
    display();
}
int gobang::score(point p, int who){
    int win5 = 0, alive4 = 0, die4 = 0, ddie4 = 0, alive3 = 0, 
    dalive3 = 0, die3 = 0, alive2 = 0, dalive2 = 0, die2 = 0, nothing = 0;
    int opp;
    if(who == 1){
        opp = 2;
    }
    else{
        opp = 1;
    }
    for(int i = 1; i <= 4; i ++){
        dir d;
        switch(i){
            case 1:
                d = d1;
                break;
            case 2:
                d = d2;
                break;
            case 3:
                d = d3;
                break;
            case 4:
                d = d4;
                break;
       }
       int l = 1;
       point le, ri, p1;
       int left[5], right[5];
       p1 = newPoint(p, d, -1);
       le = p;
       while(isInBoard(p1) && chessboard[p1.y][p1.x] == who){
               le = p1;
               p1 = newPoint(p1, d, -1);
            l ++;
       }
       p1 = newPoint(p, d, 1);
       ri = p;
       while(isInBoard(p1) && chessboard[p1.y][p1.x] == who){
            ri = p1;
            p1 = newPoint(p1, d, 1);
            l ++;
       }
       for(int j = 1; j <= 4; j ++){
               p1 = newPoint(le, d, -j);
               if(isInBoard(p1)){
                   left[j] = chessboard[p1.y][p1.x];
            }
            else{
                left[j] = opp;
            }
               p1 = newPoint(ri, d, j);
               if(isInBoard(p1)){
                   right[j] = chessboard[p1.y][p1.x];
            }
            else{
                right[j] = opp;
            }
       }
       //具體棋型判斷
       if(l == 5){
           win5 ++;
       }
       else if(l == 4){
              if(left[1] == 0 && right[1] == 0){//alive4 
                      alive4 ++;
           }
           else if(left[1] == 0 || right[1] == 0){//die4
                   die4 ++;
           }
           else{//nothing
                   nothing ++;
           }
       }
       else if(l == 3){
              if((left[1] == 0 && left[2] == who) || (right[1] == 0 && right[2] == who)){//ddie4
                      ddie4 ++;
           }
           else if(left[1] == 0 && right[1] == 0 && (left[2] == 0 || right[2] == 0)){//alive3
                   alive3 ++;                             
           }
           else if((left[1] == 0 && left[2] == 0) || (right[1] == 0 && right[2] == 0)){//die3
                   die3 ++;
           }
           else if(left[1] == 0 && right[1] == 0){//die3
                   die3 ++; 
           } 
           else{//nothing
                   nothing ++;
           }
       }
       else if(l == 2){
              if((left[1] == 0 && left[2] == who && left[3] == who) && 
                 (right[1] == 0 && right[2] == who && right[3] == who)){//die4
                   ddie4 ++;
           }
           else if(left[1] == 0 && right[1] == 0 && 
                   ((left[2] == who && left[3] == 0) || (right[2] == who && right[3] == 0))){//dalive3
                   dalive3 ++;
           }
           else if((left[1] == 0 && left[3] == 0 && left[2] == who) || 
                   (right[1] == 0 && right[3] == 0 && right[2] == who)){//die3
                die3 ++;
           }
           else if((left[1] == 0 && right[1] == 0) && 
                   (left[2] == who || right[2] == who)){//die3
                   die3 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[3] == who) || 
                   (right[1] == 0 && right[2] == 0 && right[3] == who)){//die3
                die3 ++;
           }
           else if((left[1] == 0 && right[1] == 0 && right[2] == 0 && right[3] == 0) || 
                   (left[1] == 0 && left[2] == 0 && right[1] == 0 && right[2] == 0) || 
                   (left[1] == 0 && left[2] == 0 && left[3] == 0 && right[1] == 0)){//alive2
                   alive2 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[3] == 0) || 
                   (right[1] == 0 && right[2] == 0 && right[3] == 0)){//die2
                   die2 ++;
           }
           else{//nothing
                   nothing ++;
           }
       }
       else if(l == 1){
              if((left[1] == 0 && left[2] == who && left[3] == who && left[4] == who) || 
                 (right[1] == 0 && right[2] == who && right[3] == who && right[4] == who)){//ddie4
                   ddie4 ++;
           }
           else if((left[1] == 0 && right[1] == 0) && ((left[2] == who && left[3] == who && left[4] == 0) || 
                   (right[2] == who && right[3] == who && right[4] == 0))){//dalive3
                   dalive3 ++;
           }
           else if((left[1] == 0 && right[1] == 0) && 
                   ((left[2] == who && left[3] == who) || (right[2] == who && right[3] == who))){//die3
                   die3 ++;
           }
           else if((left[1] == 0 && left[4] == 0 && left[2] == who && left[3] == who) || 
                   (right[1] == 0 && right[4] == 0 && right[2] == who && right[3] == who)){//die3
                   die3 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[3] == who && left[4] == who) || 
                   (right[1] == 0 && right[2] == 0 && right[3] == who && right[4] == who)){//die3
                   die3 ++;
           } 
           else if((left[1] == 0 && left[3] == 0 && left[2] == who && left[4] == who) || 
                   (right[1] == 0 && right[3] == 0 && right[2] == who && right[4] == who)){//die3
                   die3 ++;
           }
           else if((left[1] == 0 && right[1] == 0 && right[3] == 0 && right[2] == who) && (left[2] == 0 || right[4] == 0)){//dalive2
                   dalive2 ++;
           }
           else if((right[1] == 0 && left[1] == 0 && left[3] == 0 && left[2] == who) && 
                   (right[2] == 0 || left[4] == 0)){//dalive2
                   dalive2 ++;
           }
           else if((left[1] == 0 && right[1] == 0 && right[2] == 0 && right[4] == 0 && right[3] == who) || 
                   (right[1] == 0 && left[1] == 0 && left[2] == 0 && left[4] == 0 && left[3] == who)){//dalive2
                   dalive2 ++;
           }
           else if((left[1] == 0 && left[3] == 0 && left[4] == 0 && left[2] == who) || 
                   (right[1] == 0 && right[3] == 0 && right[4] == 0 && right[2] == who)){//die2
                   die2 ++;
           }
           else if((left[1] == 0 && right[1] == 0 && right[2] == 0 && left[2] == who) || 
                   (right[1] == 0 && left[1] == 0 && left[2] == 0 && right[2] == who)){//die2
                   die2 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[4] == 0 && left[3] == who) || 
                   (right[1] == 0 && right[2] == 0 && right[4] == 0 && right[3] == who)){//die2
                   die2 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && right[1] == 0 && left[3] == who) || 
                   (right[1] == 0 && right[2] == 0 && left[1] == 0 && right[3] == who)){//die2
                   die2 ++;
           }
           else if((left[1] == 0 && left[2] == 0 && left[3] == 0 && left[4] == who) || 
                   (right[1] == 0 && right[2] == 0 && right[3] == 0 && right[4] == who)){//die2
                   die2 ++;
           }
           else{//nothing
                   nothing ++;
           }
       }
    }
    if (win5 >= 1)
        return 14;//贏5

    if (alive4 >= 1 || die4 >= 2 || (die4 >= 1 && alive3 >= 1))
        return 13;//活4 雙死4 死4活3

    if (alive3 >= 2)
        return 12;//雙活3

    if (die3 >= 1 && alive3 >= 1)
        return 11;//死3高級活3

    if (die4 >= 1)
        return 10;//高級死4

    if (ddie4 >= 1)
        return 9;//低級死4

    if (alive3 >= 1)
        return 8;//單活3

    if (dalive3 >= 1)
        return 7;//跳活3

    if (alive2 >= 2)
        return 6;//雙活2

    if (alive2 >= 1)
        return 5;//活2

    if (dalive2 >= 1)
        return 4;//低級活2

    if (die3 >= 1)
        return 3;//死3

    if (die2 >= 1)
        return 2;//死2

    return 1;//沒有威脅
}
bool gobang::isEnd(){
    for(int i = 1; i <= 4; i ++){
        dir d;
        int count = 0;
        switch(i){
            case 1:
                d = d1;
                break;
            case 2:
                d = d2;
                break;
            case 3:
                d = d3;
                break;
            case 4:
                d = d4;
                break;
        }
        for(int j = -4; j <= 4; j ++){
            point p1 = newPoint(laozi, d, j);
            if(isInBoard(p1) && chessboard[p1.y][p1.x] == chessboard[laozi.y][laozi.x]){
                count ++;
            }
            else{
                count = 0;
            }
            if(count == 5){
                return true;
            }
        }
    }
    return false;
}
bool gobang::isInBoard(point p){
    if(p.y >= 0 && p.y < 15 && p.x >= 0 && p.x < 15){
        return true;
    }
    else{
        return false;
    }
}

point gobang::newPoint (point p, dir d, int lenth){
    point p1 = {p.y + d.dy * lenth, p.x + d.dx * lenth};
    return p1;
}
void gobang::display(){
    system("cls");
    for(int i = 0; i < 15; i ++){
        for(int j = 0; j < 15; j ++){
            if(i == Y && j == X){
                cout << "";
            }
            else if(chessboard[i][j] == 1){
                cout << "";
            }
            else if(chessboard[i][j] == 2){
                cout << "";
            }
            else{
                cout << ". ";
            }
                
        }
        cout << endl;
    }
}
int main(){
    gobang game;
    game.play();
    return 0;
}

 

 

 

 

  

 

     四、寫五子棋程序的建議:          
               1.首先要尋找好的代碼,如果是那種完全是代碼卻一點都沒有講解的還是不要看了,給我幫助的博客有:https://www.cnblogs.com/songdechiu/p/5768999.html                              https://www.jb51.net/article/105675.htm
               2.要先有規划,這就好比新年家里要大掃除,如果不做規划,就不知道從何開始,進行到哪里,做得怎么樣。我一開始沒有規划,盲目打代碼,結果必定的錯誤百出,而且還難以尋找錯誤(連自己的代碼為什么這樣寫都糊里糊塗),后來重新開始,先寫一個思路圖,在打代碼,最后檢查錯誤,時間節省了很多。
             
               3.完成之后,不妨教給別人(寫博客是個好方法,可是應該沒什么人看),在寫的過程中,我要思考如何把一些問題講清楚明白,讓不會的人能理解,雖然我只是學到了皮毛,但是在這個過程中對程序的理解又有所提升。
        最后、因為我這個五子棋程序很簡單,下贏它不難(我不太會五子棋,下不贏5555),我希望自己可以用博弈樹在寫一個五子棋程序,加油吧!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM