Qt、C++实现五子棋人机对战与本地双人对战

 当初随便搞的大作业发现浏览和收藏还不少,所以将源代码分享出来,谢谢大家的收藏和点赞:

https://download.csdn.net/download/ls200013/88046653

1.基本介绍

软件基于Qt5.6.1,利用C++语言进行编写。

五子棋有两种游戏模式:人机对战和双人对战。

在进入游戏时玩家选择游戏模式。 选择人机对战玩家会执黑棋、电脑执白棋进行对决。选择双人对战会由玩家双方各自操作鼠标进行对战。

2.详细设计

2.1 模式选择界面

        进入游戏首先呈现的是模式选择界面。模式选择界面逻辑较为简单,不需要多余装饰,故可以使用QMessageBox类来呈现,根据用户点击的按钮进入到对应的游戏模式,并弹出游戏界面。

2.2 游戏界面

        五子棋为15*15一共225个点,14*14格。棋盘结构较为简单,可利用画家类QPainter自行绘制出棋盘,棋子同理。落子位置利用对mouseMoveEvent(QMouseEvent* event)重写来实现,根据鼠标移动来捕获落子标记点。

2.3 电脑算法

        电脑算法采用对落子位置计分来实现,电脑对不同位置的子有不同的计分方式,最终选出最优计分,将棋子落在得分最高处。电脑算法以守为主,守的分数>攻的分数。

3.主要编码实现

3.1 模式选择界面的编码实现:

//初始化
void MainWindow::initGame(){
    game = new GameModel();

    QMessageBox msgBox;
    msgBox.setWindowTitle("五子棋对战");
    msgBox.setText("请选择对战模式");
    QPushButton *Button1 = msgBox.addButton(tr("人机对战"), QMessageBox::ActionRole);
    QPushButton *Button2 = msgBox.addButton(tr("本地玩家对战"), QMessageBox::ActionRole);
    msgBox.setStandardButtons(QMessageBox::Close);
    msgBox.exec();
 
    if( msgBox.clickedButton() == Button1){
        game_type = AI;
    }else if (msgBox.clickedButton() == Button2) {
        game_type = MAN;
    }else{exit(0);}
    game->gameStatus = PLAYING;
 
    game->startGame(game_type);
    update();
}

 3.2 游戏界面的编码实现:

void MainWindow::paintEvent(QPaintEvent* event){
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing); //设置抗锯齿
//画棋盘
    for(int i=1;i<BOARD_GRAD_SIZE;i++){
        painter.drawLine(MARGIN+BLOCK_SIZE*i,MARGIN+BLOCK_SIZE,
                         MARGIN+BLOCK_SIZE*i,this->height()-MARGIN-BLOCK_SIZE);
        painter.drawLine(MARGIN+BLOCK_SIZE,MARGIN+BLOCK_SIZE*i,
                         this->width()-MARGIN-BLOCK_SIZE,MARGIN+BLOCK_SIZE*i);
    }
    //绘制选中点
    QBrush brush;
    brush.setStyle(Qt::SolidPattern);
    //选中点标记
    if(clickPosRow>0 && clickPosRow<BOARD_GRAD_SIZE &&
       clickPosCol>0 && clickPosCol<BOARD_GRAD_SIZE &&
        game->gameMapVec[clickPosRow][clickPosCol]==0){
        if(game->playerFlag){
            brush.setColor(Qt::black);
        }else{
            brush.setColor(Qt::white);
        }
        painter.setBrush(brush);
        painter.drawRect(MARGIN+BLOCK_SIZE*clickPosCol-MARK_SIZE/2,MARGIN+BLOCK_SIZE*clickPosRow-MARK_SIZE/2,MARK_SIZE,MARK_SIZE);
    }
    //落子绘制
    for(int i=0;i<BOARD_GRAD_SIZE;i++){
        for(int j=0;j<BOARD_GRAD_SIZE;j++){
            if(game->gameMapVec[i][j]==1){
                brush.setColor(Qt::black);
                painter.setBrush(brush);
                painter.drawEllipse(MARGIN+BLOCK_SIZE*j-CHESS_RADIUS,MARGIN+BLOCK_SIZE*i-CHESS_RADIUS,CHESS_RADIUS*2,CHESS_RADIUS*2);
            }else if(game->gameMapVec[i][j]==-1){
                brush.setColor(Qt::white);
                painter.setBrush(brush);
                painter.drawEllipse(MARGIN+BLOCK_SIZE*j-CHESS_RADIUS,MARGIN+BLOCK_SIZE*i-CHESS_RADIUS,CHESS_RADIUS*2,CHESS_RADIUS*2);
            }
 
        }
    }
    //判断输赢
    if(clickPosCol>0 && clickPosCol<BOARD_GRAD_SIZE &&
       clickPosRow>0 && clickPosRow<BOARD_GRAD_SIZE &&
       (game->gameMapVec[clickPosRow][clickPosCol]==1||game->gameMapVec[clickPosRow][clickPosCol]==-1)){  //代碼解析:game->gameMapVec[clickPosRow][clickPosCol]==1||game->gameMapVec[clickPosRow][clickPosCol]==-1,防止因為5個0(空白)相連也被判勝利
        if(game->isWin(clickPosRow,clickPosCol) && game->gameStatus == PLAYING){
            game->gameStatus = WIN;
            QString str;
            str = game->gameMapVec[clickPosRow][clickPosCol]==1?"黑棋":"白棋";
            QMessageBox::StandardButton btnValue = QMessageBox::information(this,"五子棋嬴家",str+"勝利");
            if(btnValue == QMessageBox::Ok){
                game->startGame(game_type);
                game->gameStatus = PLAYING;
            }
        }
    }
}

 棋盘,落子标记,黑子,白子如上图所示。

3. 电脑算法的编码实现

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执行下棋
void GameModel::actionByAI(int &clickRow,int &clickCol){
    //计算评分
    calculateScore();
    //从评分中找出最大分数的位置
    int maxScore = 0;
    std::vector<std::pair<int,int>> maxPoints;
    for(int row = 1;row<BOARD_GRAD_SIZE;row++){
        for(int col = 1;col<BOARD_GRAD_SIZE;col++){
            //前提是这个坐标是空的
            if(gameMapVec[row][col] == 0){
                if(scoreMapVec[row][col]>maxScore){     //找最大数和坐标
                    maxPoints.clear();
                    maxScore = scoreMapVec[row][col];
                    maxPoints.push_back(std::make_pair(row,col));
                }else if(scoreMapVec[row][col] == maxScore){
                    //如果有多个最大值就将他们存储起来,在后面的代码随机抽1个
                    maxPoints.push_back(std::make_pair(row,col));
                }
            }
        }
    }
    //随机落子,如果有多个点的话
    srand((unsigned)time(0));
    int index = rand()%maxPoints.size();
    std::pair<int,int> pointPair = maxPoints.at(index);
    clickRow = pointPair.first;
    clickCol = pointPair.second;
    updateGameMap(clickRow,clickCol);
}

4. 游戏效果

        游戏能够实现两种对战模式,并正确判断输赢。


                
  • 6
    点赞
  • 78
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萝卜丝菲特

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值