五子棋游戏算法设计,方法不限。
以两人为一组,分为甲方和乙方对弈。各自设计自己的算法: 针对对方下的每一步骤棋落子,由算法给出相应的本方落子,最终胜者只有一个。
Windows 10,Visual Studio 2019,编程语言C++
本实验核心算法是五元组评价算法。基本思路如下:
评估当前棋局中所有位置得分,机器落在得分最高的位置。
五子棋取胜条件是哪方5个棋子先连成线,哪方获胜。那么我们将五个相连的位置称为五元组。以15 * 15的棋盘为例,有572个五元组。
针对五元组中黑子和白子的数量情况的不同(不考虑相对位置,只看数量,共10种情况),对该五元组评分,如下表。每一个位置的得分就是包含这个位置的所有五元组的得分之和。评价表的好坏一定程度上决定了实验结果,本实验所采用的是一个比较出色的评价表,经过测试,所带来的结果也比较好。

以评价横向的五元组为例,部分代码如下:
//1、扫描横向的15行
for (int i = 0; i < size; i++) //对于每一行来说
{
for (int j = 0; j < size - 4; j++)
{
int k = j;
while (k < j + 5) //统计每个五元组的黑白棋数量
{
if (ChessBoard[i][k] == black)
bn++;
else if (ChessBoard[i][k] == white)
wn++;
k++;
}
//五元组中白子黑子中对应的数量的打分
tupleScoreTmp = scoreTable(bn, wn);
//为该五元组的5个位置添加分数
for (k = j; k < j + 5; k++)
score[i][k] += tupleScoreTmp;
//计数器们清零
bn = 0;
wn = 0;
tupleScoreTmp = 0;
}
}

【初始棋盘】【说明:若输入2 5,则表示第2行、第5列】

【机器(白手)胜】

【人(黑手)胜】

#include <iostream>
using namespace std;
#include <string>
#include <Windows.h>
#define size 15 //棋盘大小
//核心 【五元组评分算法】
string ChessBoard[size][size]; //棋盘,二维数组实现
int score[size][size]; //评分表
string white = "-●"; //白棋 由于控制台是黑色的,所以反着
string black = "-○"; //黑棋
string board = "-┼-"; //棋盘线 表示无子落下
void initBoard(); //初始化棋盘
void printBoard(); //打印棋盘
int scoreTable(int w, int b); //五元组评分
bool machine(int& goalX, int& goalY); //机器落子
int judge(int flag, int x, int y); //判断输赢
int cnt = 0;
int main()
{
//初始化棋盘
initBoard();
printBoard();
cout << "【说明】若输入2 5,则表示第2行,第5列" << endl;
while (1) //设置一个无限循环
{
//简单版 始终让人类执黑手
cout << "人(黑手)落子:";
int hx, hy;
cin >> hx >> hy;
//判断人的落子是否合法 因为人先手 所以是否平局只在机器落子中判断即可
while (1)
{
if (hx >= 1 && hx <= size && hy >= 1 && hy <= size && ChessBoard[hx - 1][hy - 1] == board)
break; //输入合法,跳出循环
else
{
cout << "人(黑手)落子不合法,请重新输入:";
cin >> hx >> hy;
}
}
//更新棋盘
ChessBoard[hx - 1][hy - 1] = black;
printBoard();
cout << "人(黑手)落子完成" << endl << endl;
//判断棋局胜负
if (judge(1,hx - 1,hy - 1))
{
cout << "人(黑手)胜出!!!" << endl;
break;
}
//人落子处理完成
/********************************************************************/
//开始处理机器落子
//机器 白手
int wx, wy;
//白手落子
if (machine(wx, wy) == false)
{
cout << "平局" << endl;
break;
}
//更新棋盘
ChessBoard[wx][wy] = white;
printBoard();
cout << "机器(白手)已在("<<wx + 1 <<","<< wy+1 << ")落子" << endl;
//判断棋局胜负
if (judge(0,wx,wy))
{
cout << "机器(白手)胜出!!!" << endl;
break;
}
}
return 0;
}
//初始化棋盘
void initBoard()
{
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
ChessBoard[i][j] = board;
}
}
}
//打印棋盘
void printBoard()
{
if(cnt != 0)
system("cls"); //清屏
cnt = 1;
cout << "棋盘如下:" << endl;
cout << "\t ";
for (int i = 0;i < size;i++)
{
cout << i + 1<< " ";
if (i + 1 <= 9)
cout << " ";
}
cout << endl;
for (int i = 0; i < size; i++)
{
// cout <<i + 1<<"\t";
cout << "\t";
if (i + 1 <= 9)
cout << " ";
cout <<i + 1;
for (int j = 0; j < size; j++)
{
cout << ChessBoard[i][j];
}
cout << endl;
}
}
//根据黑子数和白子数,在评分表中获得评分 评分表有10种情况
int scoreTable(int b, int w)
{
//1、既有黑子又有白子,判0分
if (w > 0 && b > 0)
return 0;
//2、全部为空,并没有落子,判分为7
if (w == 0 && b == 0)
return 7;
//3、机器落1子,判35分
if (w == 1)
return 35;
//4、机器落2子,判800分
if (w == 2)
return 800;
//5、机器落3子,判15000分
if (w == 3)
return 15000;
//6、机器落4子,判800000分
if (w == 4)
return 800000;
//7、人类落1子,判15分
if (b == 1)
return 15;
//8、人类落2子,判400分
if (b == 2)
return 15;
//9、人类落3子,判1800分
if (b == 3)
return 1800;
//10、人类落4子,判100000
if (b == 4)
return 100000;
return -1;
}
//机器落子 确定mx my即可 返回true有子可落,返回false无子可落
bool machine(int& goalX, int& goalY)
{
//每次机器落子,都初始化score评分数组
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
score[i][j] = 0;
}
}
//每次机器寻找落子位置,评分都重新算一遍,先定义并初始化一些变量
int bn = 0; //五元组中的黑棋数量
int wn = 0; //五元组中的白棋数量
int tupleScoreTmp = 0; //五元组得分临时变量
goalX = -1; //初始化目标位置坐标
goalY = -1; //初始化目标位置坐标
int maxSocre = -1; //最大分数
//扫描6个方向
//1、扫描横向的15行
for (int i = 0; i < size; i++) //对于每一行来说
{
for (int j = 0; j < size - 4; j++)
{
int k = j;
while (k < j + 5) //统计每个五元组的黑白棋数量
{
if (ChessBoard[i][k] == black)
bn++;
else if (ChessBoard[i][k] == white)
wn++;
k++;
}
tupleScoreTmp = scoreTable(bn, wn); //五元组中白子黑子中对应的数量的打分
//为该五元组的5个位置添加分数
for (k = j; k < j + 5; k++)
{
score[i][k] += tupleScoreTmp;
}
//计数器们清零
bn = 0;
wn = 0;
tupleScoreTmp = 0;
}
}
//2、扫描纵向15行
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size - 4; j++)
{
int k = j;
while (k < j + 5)
{
if (ChessBoard[k][i] == black)
bn++;
else if (ChessBoard[k][i] == white)
wn++;
k++;
}
tupleScoreTmp = scoreTable(bn, wn);
//为该五元组每个位置添加分数
for (k = j; k < j + 5; k++)
{
score[k][i] += tupleScoreTmp;
}
//置零
bn = 0;
wn = 0;
tupleScoreTmp = 0;
}
}
//3、扫描右上角到中点方向
for (int i = size - 1; i >= 4; i--)
{
for (int k = i, j = 0; j < size && k >= 0; j++, k--)
{
int m = k;
int n = j;
while (m > k - 5 && k - 5 >= -1) //这里是-1
{
if (ChessBoard[m][n] == black)
bn++;
else if (ChessBoard[m][n] == white)
wn++;
m--;
n++;
}
if (m == k - 5)
{
tupleScoreTmp = scoreTable(bn, wn);
for (m = k, n = j; m > k - 5; m--, n++)
{
score[m][n] += tupleScoreTmp;
}
}
//置零
bn = 0;
wn = 0;
tupleScoreTmp = 0;
}
}
//4、扫描中点到左下角方向
for (int i = 1; i < size; i++)
{
for (int k = i, j = size - 1; j >= 0 && k < size; j--, k++)
{
int m = k;
int n = j;
while (m < k + 5 && k + 5 <= size)
{
if (ChessBoard[n][m] == black)
bn++;
else if (ChessBoard[n][m] == white)
wn++;
m++;
n--;
}
if (m == k + 5)
{
tupleScoreTmp = scoreTable(bn, wn);
for (m = k, n = j; m < k + 5; m++, n--)
{
score[n][m] += tupleScoreTmp;
}
}
//置零
bn = 0;
wn = 0;
tupleScoreTmp = 0;
}
}
//5、扫描左上角到中点方向
for (int i = 0; i < size - 4; i++)
{
for (int k = i, j = 0; j < size && k < size; j++, k++)
{
int m = k;
int n = j;
while (m < k + 5 && k + 5 <= size)
{
if (ChessBoard[m][n] == black)
bn++;
else if (ChessBoard[m][n] == white)
wn++;
m++;
n++;
}
if (m == k + 5)
{
tupleScoreTmp = scoreTable(bn, wn);
for (m = k, n = j; m < k + 5; m++, n++)
{
score[m][n] += tupleScoreTmp;
}
}
//置零
bn = 0;
wn = 0;
tupleScoreTmp = 0;
}
}
//6、扫描中点到右下角方向
for (int i = 1; i < size - 4; i++)
{
for (int k = i, j = 0; j < size && k < size; j++, k++)
{
int m = k;
int n = j;
while (m < k + 5 && k + 5 <= 15)
{
if (ChessBoard[n][m] == black)
bn++;
else if (ChessBoard[n][m] == white)
wn++;
m++;
n++;
}
if (m == k + 5)
{
tupleScoreTmp = scoreTable(bn, wn);
for (m = k, n = j; m < k + 5; m++, n++)
{
score[n][m] += tupleScoreTmp;
}
}
//置零
bn = 0;
wn = 0;
tupleScoreTmp = 0;
}
}
//从空位置(能落子的位置)中找到得分最大的位置
for (int i = 0; i < size; i++)
{
for (int j = 0; j < size; j++)
{
if (ChessBoard[i][j] == board && score[i][j] > maxSocre)
{
goalX = i;
goalY = j;
maxSocre = score[i][j];
}
}
}
//没有找到坐标说明平局了
if (goalX != -1 && goalY != -1)
return true;
return false; //平局
}
//判断刚刚的落子是否产生输赢
int judge(int flag,int x, int y)
{
string tmp;
if (flag == 1) //flag = 1,说明是人刚刚落子,判断人是否赢
tmp = black;
else tmp = white;
//判断上、下、左、右、左上、右下、右上、左下8个方向
//1、判断左
int sum = 0;
for (int i = x - 1; i >= 0; i--)
{
if (ChessBoard[i][y] == tmp)
sum++;
else
break;
}
//2、判断右
for (int i = x + 1; i < size; i++)
{
if (ChessBoard[i][y] == tmp)
sum++;
else
break;
}
if (sum >= 4)
return 1;
//3、判断上
sum = 0;
for (int i = y - 1; i >= 0; i--)
{
if (ChessBoard[x][i] == tmp)
sum++;
else break;
}
//4、判断下
for (int i = y + 1;i < size;i++)
{
if (ChessBoard[x][i] == tmp)
sum++;
else
break;
}
if (sum >= 4)
return 1;
//5、判断左上
sum = 0;
for (int i = x - 1, j = y - 1;i >= 0 && j >= 0;i--,j--)
{
if (ChessBoard[i][j] == tmp)
sum++;
else
break;
}
//6、判断右下
for (int i = x + 1, j = y + 1;i < size && j < size;i++,j++)
{
if (ChessBoard[i][j] == tmp)
sum++;
else
break;
}
if (sum >= 4)
return 1;
//7、判断右上
sum = 0;
for (int i = x + 1,j = y - 1;i < size && j >= 0;i++,j--)
{
if (ChessBoard[i][j] == tmp)
sum++;
else
break;
}
//8、判断左下
for (int i = x - 1,j = y + 1;i >= 0 && j < size;i--,j++)
{
if (ChessBoard[i][j] == tmp)
sum++;
else
break;
}
if (sum >= 4)
return 1;
return 0;
}