【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 - 总结
到这里,已经可以实现初步的人机对战游戏,但是这个时候的电脑还不怎么聪明,只会“走一步棋,看一步棋”,下期讲解人机博弈算法进阶版,让电脑下棋子实现“走一步,看三步”。
- 01_开发象棋游戏简介
- 02_绘画象棋棋盘
- 03_象棋棋子摆放
- 04_象棋走棋规则——車、炮、士
- 05_象棋走棋规则——象、马、将、兵
- 06_象棋游戏法则
- 07_人机博弈算法开端
- 08_人机博弈高阶算法
- 09_交叉编译移植Linux系统
标签:07,int,象棋,电脑,Step,step,棋子,走棋,Qt 来源: https://blog.csdn.net/qq_39388660/article/details/117883090