编程语言
首页 > 编程语言> > 【Qt象棋游戏】07_人机博弈算法开端

【Qt象棋游戏】07_人机博弈算法开端

作者:互联网

文章目录

01 - 人机博弈算法简述

  前面详细介绍了棋盘类的封装、棋子类的封装以及各种类型的棋子的走棋算法的实现。有了前面的铺垫,就能迈出电脑智能下棋的第一步了。
  电脑要实现人机博弈下棋对战分3步走:
  (1)电脑获取棋子所有走得通的路径;
  (2)从棋子所有能走的路径中计算出对电脑最优路径;
  (3)电脑实现走棋。

02 - 相关成员与方法

  编写代码前,预先看看增加了哪些类和成员。新建一个QVector容器,用于保存棋子属性信息,方便计算对电脑最优路径,具体内容如下:

#ifndef STEP_H
#define STEP_H
#include <QObject>
class Step : public QObject
{
    Q_OBJECT
public:
   int moveID;     //行走棋子的ID
   int killID;     //杀掉棋子的ID
   int rowFrom;    //起始行坐标
   int colFrom;    //起始列坐标
   int rowTo;      //目标位置行坐标
   int colTo;      //目标位置列坐标

signals:
public slots:
};
#endif // STEP_H

  同时在Chessarea.h里面添加电脑实现人机博弈下棋相关方法和槽函数:

   // 获取所有走棋路径存放到steps中
   void getAllPossibleMove(QVector<Step *>& steps);
   void fakeMove(Step* step);   	// 棋子假装走一步
   void unfakeMove(Step* step);     // 棋子假装走的那一步挪回来
   Step* getBestMove();             // 获取最佳走棋路径
   int   calcScore();               // 评估局面分

private slots:

   void computerMove(bool run);  // 电脑走棋

   //在widget构造函数上实现信号与槽()
   connect(chessarea, SIGNAL(sendCompute(bool)), chessarea, SLOT(computerMove(bool)));

03 - 获取电脑棋子能走路径

  前面遍历所有电脑(黑方)棋子,遍历到该棋子在棋盘上所有位置,把每个位置信息都输入canMove函数,canMove函数返回true的“步”就存放到容器Step中。

/**
 *
 *  @brief :获取电脑最优移动路径
 *
 *  @param : 无
 *
 *  @return: 最优棋子信息的属性(原坐标、目标坐标、ID、目标ID)
 *
 **/
Step *ChessArea::getBestMove()
{
     QVector<Step *> steps;

    // 获取电脑的所有走棋路径
    getAllPossibleMove(steps);

    // 初始化比重
    int maxScore = -100000;
    Step* ret;
    for(QVector<Step*>::iterator it=steps.begin(); it!=steps.end(); ++it)
    {
        Step* step= *it;

        //试着走一下
        fakeMove(step);

        //评估局面分
        int score = calcScore();


        //再走回来
        unfakeMove(step);
        //取最高的分数
        if(score > maxScore)
        {
            maxScore = score;

            ret = step;
        }
    }

    return ret;

}

  关于fakeMove函数和unfakeMove函数

/**
 *
 *  @brief : 棋子假装移动走一步
 *
 *  @param : steps : 保存移动棋子信息的属性(原坐标、目标坐标、ID、目标ID)
 *
 *  @return: 无
 *
 **/
void ChessArea::fakeMove(Step *step)
{
    //杀死棋子
    killStone(step->killID);

    if(step->killID != -1)
    {
         myChess[step->killID].row = -1;
         myChess[step->killID].col = -1;
    }

    myChess[step->moveID].row = step->rowTo;
    myChess[step->moveID].col = step->colTo;
}

/**
 *
 *  @brief : 棋子假装移动回一步
 *
 *  @param : steps : 保存移动棋子信息的属性(原坐标、目标坐标、ID、目标ID)
 *
 *  @return: 无
 *
 **/
void ChessArea::unfakeMove(Step *step)
{
    //复活棋子
    reliveStone(step->killID);

    if(step->killID != -1)
    {
         myChess[step->killID].row = step->rowTo;
         myChess[step->killID].col = step->colTo;
    }

    myChess[step->moveID].row = step->rowFrom;
    myChess[step->moveID].col = step->colFrom;

}

  棋局评分算法:先给所有棋子分配权重,根据棋子重要程度分配,将是最重要的棋子,因此权重最高,置位1500;车其次,置位100;马和炮其次,置为50;兵再其次置为20,士和相最不重要,置为10。遍历红方所以的棋子,将红方活着的棋子的权重累加出一个总分;遍历黑方所有的棋子,将黑方活着的棋子的权重累加出一个总分。默认电脑为黑方,所以返回的局面分应该以黑方的角度计算,返回黑棋总分 - 红旗总分。以下是函数实现:

/**
 *
 *  @brief : 获取棋局的评分
 *
 *  @param : 无
 *
 *  @return: 无
 *
 **/
int ChessArea::calcScore()
{
    //枚举的 车=0 马=1 炮=2 兵=3 将=4 士=5 相=6
       static int chessScore[]={100, 80, 80, 20, 1500, 10, 10};
       int redTotalScore   = 0;
       int blackTotalScore = 0;

       //计算红棋总分
       for(int i=0; i<16; i++)
       {
           //如果棋子已死则跳过累加
           if(myChess[i].isDead)
               continue;

           redTotalScore += chessScore[myChess[i].chessType];
       }


       //计算黑棋总分
       for(int i=16; i<32; i++)
       {
           //如果棋子已死则跳过累加
           if(myChess[i].isDead)
               continue;

           blackTotalScore += chessScore[myChess[i].chessType];
       }


       //返回黑棋总分 - 红棋总分
       return blackTotalScore - redTotalScore;
}

04 - 电脑走棋

  人类走红旗,电脑走黑棋。当到人类走红旗,直接移动鼠标走完棋子,然后触发信号,轮到黑方走棋,电脑调用getBestMove函数计算出走棋最优路径后,得到最优的Step直接移动棋子。

/**
 *
 *  @brief : 电脑走棋( 槽函数 )
 *
 *  @param : 布尔类型,由信号传递
 *
 *  @return: 无
 *
 **/
void ChessArea::computerMove(bool run)
{

    if(run == false)
    {
        if(this->bRedTurn)
            return;
        else
        {

            Step* step = getBestMove();

            // 移动棋子
            moveChess(step->moveID, step->killID, step->rowTo, step->colTo);

            bRedTurn = true;
            // 轮到人类走棋
            emit redTrueGo(bRedTurn);
        }
    }
    else
    {
        qDebug() << "computer move failed";
        return;
    }
}

05 - 总结

  到这里,已经可以实现初步的人机对战游戏,但是这个时候的电脑还不怎么聪明,只会“走一步棋,看一步棋”,下期讲解人机博弈算法进阶版,让电脑下棋子实现“走一步,看三步”。

标签:07,int,象棋,电脑,Step,step,棋子,走棋,Qt
来源: https://blog.csdn.net/qq_39388660/article/details/117883090