计算每个合法的落子点的“权值”,然后再权值最大的点落子
以后,可以在这个基础之上,实现多个层次的计算.
对于每个空白点,分别计算周围的八个方向
因为在计算某个方向时,正向和反向需同时考虑,所以实际上只需计算4个方向即可:
如果黑棋走这个点
产生效果 | 评分 |
连2 | 10 |
死3 | 30 |
活3 | 40 |
死4 | 60 |
活4 | 200 |
连5 | 20000 |
如果白棋AI走这个点
产生效果 | 评分 |
连1(普通) | 5 |
连2 | 10 |
死3 | 25 |
活3 | 50 |
死4 | 55 |
活4 | 300 |
连5 | 30000 |
权值的计算,放在ChessData模块中。
ChessData.h
void calculateScore(ChessData* data);
ChessData.cpp
#include <string.h> //memset函数
// 最关键的计算评分函数
void calculateScore(ChessData* data)
{
if (!data) return;
// 统计玩家或者电脑连成的子
int personNum = 0; // 玩家连成子的个数
int botNum = 0; // AI连成子的个数
int emptyNum = 0; // 各方向空白位的个数
// 清空评分数组
memset(data->scoreMap, 0, sizeof(data->scoreMap));
for (int row = 0; row < BOARD_GRAD_SIZE; row )
for (int col = 0; col < BOARD_GRAD_SIZE; col ) {
// 空白点就算
if (row >= 0 && col >= 0 && data->chessMap[row][col] == 0)
{
// 遍历周围4个方向,分别计算正反两个方向
int directs[4][2] = { {1,0}, {1,1}, {0,1}, {-1,1 } };
for (int k = 0; k < 4; k ) {
int x = directs[k][0];
int y = directs[k][1];
// 重置
personNum = 0;
botNum = 0;
emptyNum = 0;
// 对黑棋评分(正向)
for (int i = 1; i <= 4; i ) {
if (row i * y >= 0 && row i * y < BOARD_GRAD_SIZE &&
col i * x >= 0 && col i * x < BOARD_GRAD_SIZE &&
data->chessMap[row i * y][col i * x] == 1) { // 真人玩家的子
personNum ;
} else if (row i * y >= 0 && row i * y < BOARD_GRAD_SIZE &&
col i * x >= 0 && col i * x < BOARD_GRAD_SIZE &&
data->chessMap[row i * y][col i * x] == 0) { // 空白位
emptyNum ;
break; // 遇到空白位置,停止该方向的搜索
} else // 出边界,或者遇到白棋,就停止该方向的搜索
break;
}
// 对黑棋评分(反向)
for (int i = 1; i <= 4; i ) {
if (row - i * y >= 0 && row - i * y < BOARD_GRAD_SIZE &&
col - i * x >= 0 && col - i * x < BOARD_GRAD_SIZE &&
data->chessMap[row - i * y][col - i * x] == 1) { // 玩家的子
personNum ;
}
else if (row - i * y >= 0 && row - i * y < BOARD_GRAD_SIZE &&
col - i * x >= 0 && col - i * x < BOARD_GRAD_SIZE &&
data->chessMap[row - i * y][col - i * x] == 0) { // 空白位
emptyNum ;
break;
} else // 出边界,或者有AI自己的棋子
break;
}
if (personNum == 1) // *二
data->scoreMap[row][col] = 10;
else if (personNum == 2) { // *三
if (emptyNum == 1) // 死三
data->scoreMap[row][col] = 30;
else if (emptyNum == 2) // 活三
data->scoreMap[row][col] = 40;
} else if (personNum == 3) { // *四
if (emptyNum == 1) //死四
data->scoreMap[row][col] = 60;
else if (emptyNum == 2) //活四
data->scoreMap[row][col] = 200;
}
else if (personNum == 4) // *五
data->scoreMap[row][col] = 20000;
// 进行一次清空
emptyNum = 0;
// 对白棋评分(正向)
for (int i = 1; i <= 4; i ) {
if (row i * y > 0 && row i * y < BOARD_GRAD_SIZE &&
col i * x > 0 && col i * x < BOARD_GRAD_SIZE &&
data->chessMap[row i * y][col i * x] == -1) { // 玩家的子
botNum ;
} else if (row i * y > 0 && row i * y < BOARD_GRAD_SIZE &&
col i * x > 0 && col i * x < BOARD_GRAD_SIZE &&
data->chessMap[row i * y][col i * x] == 0) { // 空白位
emptyNum ;
break;
} else
break;
}
// 对白棋评分(反向)
for (int i = 1; i <= 4; i ) {
if (row - i * y > 0 && row - i * y < BOARD_GRAD_SIZE &&
col - i * x > 0 && col - i * x < BOARD_GRAD_SIZE &&
data->chessMap[row - i * y][col - i * x] == -1) { // AI的子
botNum ;
} else if (row - i * y > 0 && row - i * y < BOARD_GRAD_SIZE &&
col - i * x > 0 && col - i * x < BOARD_GRAD_SIZE &&
data->chessMap[row - i * y][col - i * x] == 0) { // 空白位
emptyNum ;
break;
} else // 出边界
break;
}
if (botNum == 0) // 普通下子
data->scoreMap[row][col] = 5;
else if (botNum == 1) // 活二
data->scoreMap[row][col] = 10;
else if (botNum == 2) {
if (emptyNum == 1) // 死三
data->scoreMap[row][col] = 25;
else if (emptyNum == 2)
data->scoreMap[row][col] = 50; // 活三
} else if (botNum == 3) {
if (emptyNum == 1) // 死四
data->scoreMap[row][col] = 55;
else if (emptyNum == 2)
data->scoreMap[row][col] = 300; // 活四
} else if (botNum >= 4)
data->scoreMap[row][col] = 30000; // 活五,应该具有最高优先级
}
}
}
}
AI思考落子点
在各落子点,找到分值最大的点。如果有多个分值相同的点,直接在其中取一个随机点。
在ChesssData模块实现。
ChessData.h
typedef struct point {
int row;
int col;
} point_t;
point_t actionByAI(ChessData* data); // 机器执行下棋
ChessData.cpp
#include <time.h>
#include <stdlib.h>
point_t actionByAI(ChessData *data)
{
// 计算评分
calculateScore(data);
// 从评分中找出最大分数的位置
int maxScore = 0;
//std::vector<std::pair<int, int>> maxPoints;
point_t maxPoints[BOARD_GRAD_SIZE * BOARD_GRAD_SIZE] = { 0, };
int k=0;
for (int row = 0; row < BOARD_GRAD_SIZE; row )
for (int col = 0; col < BOARD_GRAD_SIZE; col )
{
// 前提是这个坐标是空的
if (data->chessMap[row][col] == 0)
{
if (data->scoreMap[row][col] > maxScore) // 找最大的数和坐标
{
//maxPoints.clear();
memset(maxPoints, 0, sizeof(maxPoints));
k = 0;
maxScore = data->scoreMap[row][col];
//maxPoints.push_back(std::make_pair(row, col));
maxPoints[k].row = row;
maxPoints[k].col = col;
k ;
}
else if (data->scoreMap[row][col] == maxScore) { // 如果有多个最大的数,都存起来
//maxPoints.push_back(std::make_pair(row, col));
maxPoints[k].row = row;
maxPoints[k].col = col;
k ;
}
}
}
// 随机落子,如果有多个点的话
srand((unsigned)time(0));
int index = rand() % k;
return maxPoints[index];
}
实现AI落子
void AI_GO() { //AI走棋
point_t point = actionByAI(&game);
clickPosRow = point.row;
clickPosCol = point.col;
Sleep(1000); //AI计算的太快,此处以假装思考
chessDown(clickPosRow, clickPosCol, CHESS_WHITE);
updateGameMap(&game, clickPosRow, clickPosCol);
}
11.判断棋局是否结束在ChessData模块定义判断输赢的接口
原理分析:
在4个方向上搜索。
以右下方向为例:(黑色棋子表示刚下的棋子)
从当前棋子开始,向右下方数5个