ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2021-06-13 18:30:03  阅读:202  来源: 互联网

标签:07 int 象棋 电脑 Step step 棋子 走棋 Qt


文章目录

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 - 总结

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

  • 01_开发象棋游戏简介
  • 02_绘画象棋棋盘
  • 03_象棋棋子摆放
  • 04_象棋走棋规则——車、炮、士
  • 05_象棋走棋规则——象、马、将、兵
  • 06_象棋游戏法则
  • 07_人机博弈算法开端
  • 08_人机博弈高阶算法
  • 09_交叉编译移植Linux系统

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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有