C++ Qt項目-五子棋(含AI對戰)

成品展示:C++ Qt項目-五子棋(含AI對戰)_哔哩哔哩_bilibili

源碼:​​​​​​​https://github.com/ngiokweng/QtProject/tree/master/Gobang

重點代碼解析


判斷輸嬴

代碼如下:

bool GameModel::isWin(int row,int col){
    /*判斷下棋點的水平、垂直、左斜、右斜方中
     * 有沒有5子相連的情況,如有則贏
     */
    for(int i=0;i<5;i++){
        //先判斷水平方向是否有5子相連
        if(row>0 && row<BOARD_GRAD_SIZE &&
           col-i>0 && col-i+4<BOARD_GRAD_SIZE &&
           gameMapVec[row][col-i] == gameMapVec[row][col-i+1] &&
           gameMapVec[row][col-i] == gameMapVec[row][col-i+2] &&
           gameMapVec[row][col-i] == gameMapVec[row][col-i+3] &&
           gameMapVec[row][col-i] == gameMapVec[row][col-i+4]){
            return true;
        }
        //先判斷垂直方向是否有5子相連
        if(row-i>0 && row-i+4<BOARD_GRAD_SIZE &&
           col>0 && col<BOARD_GRAD_SIZE &&
           gameMapVec[row-i][col] == gameMapVec[row-i+1][col] &&
           gameMapVec[row-i][col] == gameMapVec[row-i+2][col] &&
           gameMapVec[row-i][col] == gameMapVec[row-i+3][col] &&
           gameMapVec[row-i][col] == gameMapVec[row-i+4][col]){
            return true;
        }
        //先判斷"/"方向是否有5子相連,左下->右上
        if(
           row-i>0 && row-i+4<BOARD_GRAD_SIZE &&
           col+i-4>0 && col+i<BOARD_GRAD_SIZE &&
           gameMapVec[row-i][col+i] == gameMapVec[row-i+1][col+i-1] &&
           gameMapVec[row-i][col+i] == gameMapVec[row-i+2][col+i-2] &&
           gameMapVec[row-i][col+i] == gameMapVec[row-i+3][col+i-3] &&
           gameMapVec[row-i][col+i] == gameMapVec[row-i+4][col+i-4]){
            return true;
        }
        //先判斷"\"方向是否有5子相連,右下->左上
        if(row-i>0 && row-i+4<BOARD_GRAD_SIZE &&
           col-i>0 && col-i+4<BOARD_GRAD_SIZE &&
           gameMapVec[row-i][col-i] == gameMapVec[row-i+1][col-i+1] &&
           gameMapVec[row-i][col-i] == gameMapVec[row-i+2][col-i+2] &&
           gameMapVec[row-i][col-i] == gameMapVec[row-i+3][col-i+3] &&
           gameMapVec[row-i][col-i] == gameMapVec[row-i+4][col-i+4]){
            return true;
        }

    }
    return false;
}

原理

判斷下棋點的水平、垂直、左斜、右斜方中有沒有5子相連的情況,如有則贏。
以水平方向為例,我有5種不同的方式可以獲勝,如下圖:

 所以在判斷時,這幾種情況都要考慮

AI自動對戰算法

代碼:

//計算每格分數函數
void GameModel::calculateScore(){
    //統計玩家或者電腦連成的子
    int personNum = 0; //玩家連成子的個數
    int botNum = 0;   //AI連成子的個數
    int emptyNum = 0;   //各方向空白位的個數

    //清空評分數組
    scoreMapVec.clear();
    for(int i=0;i<BOARD_GRAD_SIZE;i++){
        std::vector<int> lineScores;
        for(int j=0;j<BOARD_GRAD_SIZE;j++){
            lineScores.push_back(0);
        }
        scoreMapVec.push_back(lineScores);
    }
    //計分
    /*計分個人理解:
     * 遍歷每一個格子,判斷哪些是空白的點(即為0的點),以該點為中心,判斷周圍的八個點向外延伸的四格裡,
     * 有多少個是黑子、白子、空白,以此作為依據來評分。下方算法是以守為主,所以守的分數>攻的分數
     */
    for(int row=0;row<BOARD_GRAD_SIZE;row++){
        for(int col=0;col<BOARD_GRAD_SIZE;col++){
            //空白點才算
            if(row>0 && col>0 && gameMapVec[row][col]==0){
                //遍歷周圍8個方向
                for(int y=-1;y<=1;y++){
                    for(int x=-1;x<=1;x++){
                        //重置
                        personNum = 0;
                        botNum = 0;
                        emptyNum = 0;
                        //原坐標不算
                        if(!(y==0 && x==0)){
                            //每個方向延伸4個子

                            //對玩家黑子評分(正反兩個方向)
                            for(int i=1;i<=4;i++){
                                if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
                                   col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
                                   gameMapVec[row+i*y][col+i*x]==1){ //真人玩家的子
                                    personNum++;
                                }else if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
                                         col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
                                         gameMapVec[row+i*y][col+i*x]==0){ //空白位
                                    emptyNum++;
                                    break;
                                }else{ //出邊界,或有白子
                                    break;
                                }
                            }
                            for(int i=1;i<=4;i++){
                                if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
                                   col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
                                   gameMapVec[row-i*y][col-i*x]==1){ //真人玩家的子
                                    personNum++;
                                }else if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
                                         col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
                                         gameMapVec[row-i*y][col-i*x]==0){ //空白位
                                    emptyNum++;
                                    break;
                                }else{ //出邊界,或有白子
                                    break;
                                }
                            }
                            if(personNum == 1){                 //殺2
                                scoreMapVec[row][col]+=10;
                            }else if(personNum == 2){           //殺3
                                if(emptyNum == 1)
                                    scoreMapVec[row][col]+=30;
                                else if(emptyNum == 2)
                                    scoreMapVec[row][col]+=40;
                            }else if(personNum == 3){           //殺4
                                //量變空位不一樣,優先級不一樣
                                if(emptyNum == 1)
                                    scoreMapVec[row][col]+=60;
                                else if(emptyNum == 2)
                                    scoreMapVec[row][col]+=110;
                            }else if(personNum == 4){           //殺5
                                scoreMapVec[row][col]+=10100;
                            }

                            //進行一次清空
                            emptyNum = 0;

                            //對AI白子評分
                            for(int i=1;i<=4;i++){
                                if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
                                   col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
                                   gameMapVec[row+i*y][col+i*x]==-1){ //AI的子
                                    botNum++;
                                }else if(row+i*y>0 && row+i*y<BOARD_GRAD_SIZE &&
                                         col+i*x>0 && col+i*x<BOARD_GRAD_SIZE &&
                                         gameMapVec[row+i*y][col+i*x]==0){ //空白位
                                    emptyNum++;
                                    break;
                                }else{ //出邊界
                                    break;
                                }
                            }
                            for(int i=1;i<=4;i++){
                                if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
                                   col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
                                   gameMapVec[row-i*y][col-i*x]==-1){ //AI的子
                                    botNum++;
                                }else if(row-i*y>0 && row-i*y<BOARD_GRAD_SIZE &&
                                         col-i*x>0 && col-i*x<BOARD_GRAD_SIZE &&
                                         gameMapVec[row-i*y][col-i*x]==0){ //空白位
                                    emptyNum++;
                                    break;
                                }else{ //出邊界
                                    break;
                                }
                            }
                            if(botNum == 0){
                                scoreMapVec[row][col]+=5;  //活1
                            }else if(botNum == 1){
                                scoreMapVec[row][col]+=10; //活2
                            }else if(botNum == 2){         //活3
                                if(emptyNum == 1)
                                    scoreMapVec[row][col]+=25;
                                else if(emptyNum == 2)
                                    scoreMapVec[row][col]+=50;
                            }else if(botNum == 3){         //活4
                                if(emptyNum == 1)
                                    scoreMapVec[row][col]+=55;
                                else if(emptyNum == 2)
                                    scoreMapVec[row][col]+=100;
                            }else if(botNum >= 4){         //活5
                                scoreMapVec[row][col]+=20000;
                            }


                        }
                    }
                }
            }
        }
    }

}

 AI落子原理
先給棋盤上每一個格評分,再從最高分的格中隨機抽1個來落子

計分原理
遍歷每一個格子,判斷哪些是空白的點(即為0的點),以該點為中心,判斷周圍的八個點向外延伸的四格裡,有多少個是黑子、白子、空白,以此作為依據來評分。上方算法是以守為主,所以守的分數>攻的分數

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
五子棋是一种非常古老的游戏,也是一种非常常见的人机对战游戏。下面是一个简单的C++五子棋人机对战项目实现的示例: 1. 定义棋盘类 首先,我们需要定义一个棋盘类。在这个类,我们需要定义棋盘的大小、棋子的状态(黑、白、空)、以及各种棋子的位置等信息。 ```cpp // 定义棋盘类 class ChessBoard { public: ChessBoard(int boardSize); // 构造函数,初始化棋盘 ~ChessBoard(); // 析构函数,释放棋盘内存 void reset(); // 重置棋盘 bool setChessman(int row, int col, ChessmanType type); // 在指定位置下子 ChessmanType getChessman(int row, int col) const; // 获取指定位置的棋子状态 bool isFull() const; // 判断棋盘是否已满 private: int m_boardSize; // 棋盘大小 ChessmanType** m_board; // 棋盘数组,存储各个位置的棋子状态 }; ``` 2. 定义AI类 接下来,我们需要定义一个AI类,用于计算机与人类玩家交互并生成下一步的走法。在这个类,我们需要实现一个算法来判断哪一步最有可能赢得比赛。 ```cpp // 定义AI类 class AI { public: AI(ChessBoard* board, ChessmanType aiType); // 构造函数,初始化AI ~AI(); // 析构函数,释放AI内存 void getNextStep(int& row, int& col); // 获取下一步走法 private: ChessmanType m_aiType; // AI的棋子类型(黑或白) ChessBoard* m_board; // 棋盘 }; ``` 3. 定义游戏类 最后,我们需要定义一个游戏类,用于处理游戏的流程和逻辑。在这个类,我们需要实现人机交互、判断胜负、判断游戏是否结束等功能。 ```cpp // 定义游戏类 class Game { public: Game(int boardSize); // 构造函数,初始化游戏 ~Game(); // 析构函数,释放游戏内存 void start(); // 开始游戏 private: ChessBoard* m_board; // 棋盘 AI* m_ai; // AI bool m_isPlayerTurn; // 当前是否是玩家回合 bool m_isGameOver; // 游戏是否结束 }; ``` 以上是一个简单的C++五子棋人机对战项目的实现示例。当然,这只是一个简单的示例,实际的实现可能会更加复杂,需要根据具体的需求进行调整和修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值