中国象棋(人机博弈) |
您所在的位置:网站首页 › u盘修复app安卓 › 中国象棋(人机博弈) |
项目效果展示
上述c 程序片段基于9 * 10二维数组的棋盘定义,并隐含默认黑方初始在0到4行,红方初始在5到9行。在这个小小的片段里,我们可以看出仅仅一个兵的走法就需要做出若干判断后才可以完成。 有些程序员为了去除函数调用的开销,将分别判断的小函数去掉,而将所有判断写在一个长长的switch当中来代替,这在一定程度上提高了走法产生的速度。 效率问题 走法产生通常伴随着搜索进行,并且调用频繁。但是像中国象棋或国际象棋这样有复杂规则棋类,走法的产生往往导致大量繁琐的判断。这在一定程度上形成了程序的性能瓶颈。在象棋中,每一种棋子的移动规则各有不同,这样的情形导致了较为复杂的判断。但是我们可以将每种棋子在某一位置上的最大可走步建成一个数据库。在产生走法时直接从中取出数据,进行少量判断就可以算出该棋子的合法走步。象的走法是田字格,并且象眼要空白。一般的做法是这样:1、先检查该棋子周围与该棋子横纵坐标差的绝对值均为2的位置是否落在己方半边棋盘上,如某个点超出己方半边棋盘,将其去除。 2、检查剩下的位置上是否有己方棋子,如有将其去除。 3、检查剩下的位置方向上与该棋子横纵坐标差的绝对值均为1的象眼上是否有棋子,如有将其去除。 4、剩下的位置即是合法走不。 而如果将象的可走位及象眼位置存入数据库,枚举象在某一位置的合法走步就只需如下操作: 1、从库中取出该位置上象的可走位置,检查可走位置上有无己方棋子。 2、从库中取出该位置上象的象眼位置,检查象眼上有无棋子。 显然,此操作的过程更为简单,但在去数据库中的数据时必须有快速的定址方法,否则次操作未必有速度上的优势。实际上,由于每一位置上只能有一个棋子,我们可以轻易地使用棋子位置作为数据库中的定址依据而快速的数据读取。 第一章 绘制棋盘 画10条横线画9条竖线画将军的九宫格 #include "Board.h" #include Board::Board(QWidget *parent) : QWidget(parent) { } void Board::paintEvent(QPaintEvent *) { QPainter painter(this); int d = 40; // 画10横线 for(int i=1; i_colTo); delete step; update(); } void SingleGame::getAllPossibleMove(QVector &steps) { int min=16, max=32; if(this->_bRedTurn) { min = 0, max = 16; } for(int i=min; i_moveid, step->_rowTo, step->_colTo); } void SingleGame::unfakeMove(Step* step) { reliveStone(step->_killid); moveStone(step->_moveid, step->_rowFrom, step->_colFrom); } /* 评价局面分 */ int SingleGame::calcScore() { int redTotalScore = 0; int blackTotalScore = 0; //enum TYPE{CHE, MA, PAO, BING, JIANG, SHI, XIANG}; static int chessScore[] = {100, 50, 50, 20, 1500, 10, 10}; // 黑棋分的总数 - 红旗分的总数 for(int i=0; i maxScore) { maxScore = score; } } return maxScore; } int SingleGame::getMinScore(int level, int curMaxScore) { if(level == 0) return calcScore(); // 1.看看有那些步骤可以走 QVector steps; getAllPossibleMove(steps); // 是红旗的possiblemove int minScore = 100000; while(steps.count()) { Step* step = steps.back(); steps.removeLast(); fakeMove(step); int score = getMaxScore(level-1, minScore); unfakeMove(step); delete step; if(score maxScore) { maxScore = score; if(ret) delete ret; ret = step; } else { delete step; } } // 4.取最好的结果作为参考 return ret; } 第五章 网络版本的实现android移植以下是 NetGame.h: #ifndef NETGAME_H #define NETGAME_H #include "Board.h" #include #include /* 1) 执红方还是黑方,这个信息有服务器发出,客户端接收 第一个字节固定是1,第二个字节是1,或者0,1表示接收方走红旗,0表示走黑棋 2)点击信息 第一个字节固定是2,第二个字节是row,第三个字节是col,第四个字节是点击的棋子id */ class NetGame : public Board { Q_OBJECT public: NetGame(bool server); ~NetGame(); QTcpServer* _server; QTcpSocket* _socket; void click(int id, int row, int col); public slots: void slotNewConnection(); void slotRecv(); }; #endif // NETGAME_H以下是NetGame.cpp : #include "NetGame.h" #include NetGame::NetGame(bool server) { _server = NULL; _socket = NULL; if(server) { _server = new QTcpServer(this); _server->listen(QHostAddress::Any, 9999); connect(_server, SIGNAL(newConnection()), this, SLOT(slotNewConnection())); } else { _socket = new QTcpSocket(this); _socket->connectToHost(QHostAddress("127.0.0.1"), 9999); connect(_socket, SIGNAL(readyRead()), this, SLOT(slotRecv())); } } NetGame::~NetGame() { } void NetGame::click(int id, int row, int col) { if(_selectid == -1 && id != -1) { if(_s[id]._red != _bSide) return; } Board::click(id, row, col); /* 发送给对方 */ char buf[4]; buf[0] = 2; buf[1] = 9-row; buf[2] = 8-col; buf[3] = id; _socket->write(buf, 4); } void NetGame::slotRecv() { QByteArray ba = _socket->readAll(); char cmd = ba[0]; if(cmd == 1) { // 初始化 char data = ba[1]; init(data==1); } else if(cmd==2) { int row = ba[1]; int col = ba[2]; int id = ba[3]; Board::click(id, row, col); } } void NetGame::slotNewConnection() { if(_socket) return; _socket = _server->nextPendingConnection(); connect(_socket, SIGNAL(readyRead()), this, SLOT(slotRecv())); /* 给对方发送数据 */ char buf[2]; buf[0] = 1; buf[1] = 0; _socket->write(buf, 2); init(buf[1]==0); } 总结人人对战,还是比较简单,只要能够做到精确计算坐标点,就可以实现。 最重要的是人机对战的方面,需要用到比如最大值最小值的算法,还要用到剪枝+优化的算法,还是比较难。第一次接触算法的人可能就比较简单,所以我想发表一下感言:懂数学的人真的能走遍天下。 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |