C语⾔实现各种⼈⼯智能算法,象棋⼈⼯智能算法的C++实现
(⼀)
点击上⽅“程序⼈⽣”,选择“置顶”
第⼀时间关注程序猿(媛)⾝边的故事
前⾔:⾃AlphaGo战胜世界著名九段围棋⼿李世⽯之后,我就对棋类⼈⼯智能产⽣了极⼤的兴趣,并想要⾃⼰实现象棋的⼈⼯智能。然⽽那个时候我还在读⾼⼆,没有这么深厚的代码基础,所以那个时候也就只能想想了。但是现在不⼀样了,通过学习编程,已经可以让我在棋类⼈⼯智能这个领域向前探索了。
推荐下⼩编的C++学习;513801371,不管你是⼩⽩还是⼤⽜,⼩编我都欢迎,不定期分享⼲货,包括⼩编⾃⼰整理的⼀份2019最新的C++和0基础⼊门教程,欢迎初学和进阶中的⼩伙伴。
每天晚上20:00都会开直播给⼤家分享C++知识和路线⽅法,⾥会不定期更新最新的教程和学习⽅法(进送2019C++学习教程),⼤家都是学习C++的,或是转⾏,或是⼤学⽣,还有⼯作中想提升⾃⼰能⼒的C++党,如果你是正在学习C++的⼩伙伴可以加⼊学习。最后祝所有程序员都能够⾛上⼈⽣巅峰,让代码将梦想照进现实,⾮常适合新⼿学习,有不懂的问题可以随时问我,⼯作不忙的时候希望可以给
⼤家解惑
⾸先说明⼀下本系列博客描述的⼈⼯智能算法不是基于机器学习、深度学习这么⾼深的知识,⽽是⼀种穷举最优⾛法的算法。之所以AlphaGo不能使⽤这种算法战胜李世⽯,是因为围棋棋局局势的判断是极为复杂的,想要穷举所有的情况,全世界所有的计算机⼀起运⾏⼀百万年也⽆法到最优⾛法。所以DeepMind团队的⼤佬就想出了另⼀种解决⽅案就是让AlphaGo⾃⼰学习⾼⽔平棋⼿间的对局,从⽽提升AlphaGo的棋⼒。然⽽象棋的棋局判断还是⽐较容易的,杀掉对⾯的⽼将就可以获胜,杀掉对⾯的车马炮等棋⼦就可以提⾼⾃⼰的胜率/降低对⽅的胜率。具体的算法在之后的篇章详细讲解。
实现本系列博客中算法的编程⼯具是Qt5.5.1。
既然实现象棋⼈⼯智能的算法的本质是穷举,那么就要到所有的通路,所谓的通路就是能够⾛棋的那些“路”们,⾛不通的那些“路”就要直接被pass掉。
1.先把棋盘抽象出来,象棋棋盘有10⾏9列,⾏标设为0~9,列标设为0~8。以左上⾓的坐标为(0,0),假设初始时上⽅为红棋,下⽅为⿊棋。则初始时所有棋⼦的坐标为:
车1(红⽅):(0,0);车2(红⽅):(0,8);
马1(红⽅):(0,1);马2(红⽅):(0,7);
相1(红⽅):(0,2);相2(红⽅):(0,6);
⼠1(红⽅):(0,3);⼠2(红⽅):(0,5);
将(红⽅):(0,4);
炮1(红⽅):(2,1);炮2(红⽅):(2,7);
兵1(红⽅):(3,0);兵2(红⽅):(3,2);兵3(红⽅):(0,4);兵4(红⽅):(0,6);兵5(红⽅):(0,8);
注:红⽅的棋⼦⾏列坐标对应⿊⽅棋⼦的⾏列坐标的关系为:红⽅⾏号+⿊⽅⾏号=9;红⽅列号+⿊⽅列号=8。
车1(⿊⽅):(9,8);车2(⿊⽅):(9,0);
马1(⿊⽅):(9,7);马2(⿊⽅):(9,1);
相1(⿊⽅):(9,6);相2(⿊⽅):(9,2);
嘎嘎们⼠1(⿊⽅):(9,5);⼠2(⿊⽅):(9,3);
将(⿊⽅):(9,4);
炮1(⿊⽅):(7,7);炮2(⿊⽅):(7,1);
兵1(⿊⽅):(6,8);兵2(⿊⽅):(6,6);兵3(⿊⽅):(6,4);兵4(⿊⽅):(6,2);兵5(⿊⽅):(6,0);
下⾯给⼤家看⼀下棋盘类的源代码,⾥⾯是关于棋盘类的⼀些属性(数据成员)和需要在棋盘上进⾏的⼀些操作(函数成员),在这⾥我只给⼤家提供⼀个框架,各种成员函数的具体实现就要靠⼤家开动脑筋了。
手动挡和自动挡#ifndef BOARD_H
#define BOARD_H
#include
#include "Stone.h"
#include "Step.h"
#include
#include
class Board : public QWidget
{
Q_OBJECT
public:
explicit Board(QWidget *parent = 0);
//32颗棋⼦
Stone _s[32];
//棋⼦的像素半径
int _r;
//选中棋⼦的id
int _selectid;
//该不该红棋⾛
bool _bRedTurn;
//保存棋⼦的⾏⾛步骤
QVector _steps;
//输⼊⾏列获取棋⼦的id
int getStoneId(int row,int col);
//计算即将⾏⾛的棋⼦与某⼀坐标之间有⼏颗棋⼦
int num_of_Stone(int moveid,int row,int col);
//输⼊⾏列坐标判断该坐标上有没有棋⼦
bool beStone(int row,int col);
bool canSelect(int id);
//最基本的能不能⾛的判断判断
bool canMove(int moveid,int row,int col,int killid);
//判断将能不能⾛
bool canMoveJIANG(int moveid,int row,int col,int killid);
//判断⼠能不能⾛
bool canMoveSHI(int moveid,int row,int col,int killid);
//判断象能不能⾛
bool canMoveXIANG(int moveid,int row,int col,int killid);
//判断车能不能⾛
bool canMoveCHE(int moveid,int row,int col,int killid);
/
/判断马能不能⾛
bool canMoveMA(int moveid,int row,int col,int killid);
//判断炮能不能⾛
bool canMovePAO(int moveid,int row,int col,int killid);
//判断兵能不能⾛
bool canMoveBING(int moveid,int row,int col,int killid);
//尝试函数
void trySelectStone(int id);
void tryMoveStone(int killid, int row, int col);
//判断两个棋⼦是否是同⼀⽅的
bool sameColor(int id1, int id2);
/
/⾛棋函数极其重载
void moveStone(int moveid, int killid, int row, int col);
void moveStone(int moveid, int row, int col);
//杀死棋⼦的函数
void killStone(int id);
男士护肤品排行//复活棋⼦的函数
void reliveStone(int id);
半导体股票有哪些龙头股void saveStep(int moveid, int killid, int row, int col, QVector& steps); //与⿏标点击有关的函数
void mouseReleaseEvent(QMouseEvent *);
void click(QPoint pt);
virtual void click(int id,int row,int col);成龙否认与林凤娇婚变
/
/获取⿏标点击位置的⾏列坐标
bool getRowCol(QPoint pt,int &row,int &col);
//与显⽰到窗⼝中有关的函数
对老师的新年祝福void drawStone(QPainter& painter,int id);
void paintEvent(QPaintEvent *);
//输⼊⾏列坐标 返回像素坐标
QPoint center(int row,int col);
//输⼊棋⼦的id 返回像素坐标
QPoint center(int id);
signals:
public slots:
};
#endif // BOARD_H
2.再把棋⼦抽象出来。每个棋⼦都有⼀个id,初始时共有32枚棋⼦,id从0到31;棋⼦所具有的属性除了id还有所处的⾏列位置,棋⼦的类型(车马炮将⼠相兵),棋⼦的颜⾊(红/⿊),棋⼦是否还存活着。id置为int型;棋⼦类型置为枚举类型enum
TYPE{JIANG,CHE,PAO,MA,BING,SHI,XIANG};棋⼦的颜⾊置为bool型_red,红棋为true,⿊棋为false;棋⼦是否还存活置为bool型,活着为true,被吃掉为false。
#ifndef STONE_H
#define STONE_H
#include
class Stone
{
public:
Stone();
//枚举棋⼦的所有类型
enum TYPE{JIANG,CHE,PAO,MA,BING,SHI,XIANG};
//棋⼦所处的⾏
int _row;
//棋⼦所处的列
int _col;
//棋⼦的id
int _id;
//棋⼦是否已死
bool _dead;
//棋⼦是否为红⼦
bool _red;
//棋⼦类型
TYPE _type;
//初始化棋⼦
void init(int id);
//获取棋⼦的类型名
QString getText();
};
#endif // STONE_H
3.按照象棋的规则实现每个棋⼦的⾛法的前期函数铺垫。这⼀部分是后期⼈⼯智能算法的基础,因为后期要将所有能⾛的通的“路”保存在⼀个C++容器(类似于C语⾔中的数组)⾥。
(1)确定某个⾏列位置上是否存在棋⼦。
这个函数在后⾯具体棋⼦的⾛法算法中应⽤的⾮常⼴泛。例如⾛马的时候需要判断是否别了马腿,也就是需要判定想要移动的马在要去的⽅向的正前⽅的位置是否有别的棋⼦挡住,即判断该位置上是否存在棋⼦;再例如如果出现了“对将”的情况,需要判断红将和⿊将之间与其在同⼀直线上的所有位置上是否存在棋⼦,若所有位置都不存在棋⼦则两个将可以对吃。
其实现的原理很简单,即输⼊⼀个⾏列坐标后遍历所有存活的棋⼦的⾏列坐标看⼀下有没有棋⼦与之完全吻合,若存在这样的棋⼦,则表⽰该⾏列坐标上存在棋⼦。
/确定某个⾏列位置上是否有棋⼦/
bool Board::beStone(int row,int col)
{
for(int i=0;i<32;i++)
if(_s[i]._row==row&&_s[i]._col==col&&!_s[i]._dead)
return true;
return false;
}
(2)计算某⼀棋⼦与某⼀⾏列坐标之间有⼏颗棋⼦。
这个函数主要应⽤在“对将”以及车和炮的⾛棋算法上。例如炮如果想要隔着炮架吃掉对⽅的棋⼦就需要保证该炮与想要吃掉的对⽅的棋⼦之间有且仅有⼀个棋⼦;再例如车想要⾛棋到某⼀⾏列坐标必须保证该车与想要⾛到的位置之间没有棋⼦。
有了(1)的铺垫,本函数的实现就变得容易了。⾸先需要判定⼀下即将⾏⾛的棋⼦的位置与⽬标位置在不在同⼀⾏(列)上。如果不在同⼀⾏(列)上则直接返回-1;如果在则可以遍历⼀整⾏(列)并调⽤(1)所介绍的函数beStone来统计即将⾏⾛的棋⼦与⽬标位置之间棋⼦的个数。
//计算即将⾏⾛的棋⼦与某⼀坐标之间有⼏颗棋⼦ 默认返回值为-1
int Board::num_of_Stone(int moveid,int row,int col)
{
int i;
int sum=0;
if(_s[moveid]._row==row)
{
if(col-_s[moveid]._col>0)
for(i=_s[moveid]._col+1;i
{
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论