c语言 五子棋游戏 实现人机对战 人工智能ai的一种简单思路
思路:
首先在main函数里选择电脑的棋子颜色,调用不同的函数。
轮到电脑下子时,基本思路就是遍历棋盘上的每一个空位,并逐个计算价值量,寻找价值量最大的那个位置,并将这个位置传回start游戏函数,进行电脑的下子。
每找到一个空档,首先逐一检查它上、下、左、右、左上、左下、右上、右下八个方位是否有棋子。
例如该空档上方向无子则跳过价值量为零,检查到左下发现有棋子,则继续查看有几个棋子,从是否有一个颜色相同的棋子开始,一直到有四个棋子,逐一累加价值量,每次应判断这些棋子的颜色是否和电脑自己的颜色相同,有相同、不同两种情况,两者所叠加的价值量不同,然后再判断这几个颜色相同的棋子组成的这条线的下一个位置是否有棋子,有颜色相同、不同、无棋子三种情况,三者所叠加的价值量不同。
另外为了使价值量的区别更大,更容易把控,判断出不同数量的连续棋子后会先加不同的权重,数量越多,权重指数级增长。另外,为了区分活三和连四两种特殊情况,为它们单独加了极大的价值量,方便电脑判断。
代码实现:
函数
typedef struct computerchessposition
{
int x;
int y;
} comcp;//记录计算机的落子位置
comcp结构体用于返回电脑选定的落子位置的x,y坐标
comcp* AI(comcp* head, int Chessboard[][15], int ln,int color) // 历遍棋盘,遇到空点则计算价值,取最大价值点下子
{
int k, max = 0, x=0, y=0;
int i = 0, j = 0;
for (i = 0; i < ln; i++)
for (j = 0; j < ln; j++)
if (Chessboard[i][j] == SPA)
{ // 历遍棋盘,遇到空点则计算价值,取最大价值点下子。
k = calculatevalue(i, j, Chessboard, color);//分别计算八个方位价值
if (k >= max)
{
x = i;
y = j;
max = k;
}
}
head->x = x;
head->y = y;
return head;
}
comcp* AI(comcp* head, int Chessboard[][15], int ln,int color);这一函数用于历遍棋盘,遇到空点则计算价值,取最大价值点下子
传入的参数说明:
comcp* head:用于返回电脑选定的落子位置的x,y坐标的结构体comcp
int Chessboard[][15]:用于记录棋盘的二维数组,15代表棋盘大小15x15
int ln:棋盘大小(横纵坐标的最大值)(也传15即可)
int color:这一局中电脑所持的棋子的颜色(黑色为1,白色为-1)
返回值说明:
return head;:返回用于返回电脑选定的落子位置的x,y坐标的结构体comcp
函数
int calculatevalue(int p, int q, int Chessboard[][15],int color)//分别计算八个方位价值
{
int k = 0;
int check;
int spare;
k += checkpos(p, q, Chessboard, color, 0, -1);//左
k += checkpos(p, q, Chessboard, color, 0, 1);//右
k += checkpos(p, q, Chessboard, color, -1, 0);//上
k += checkpos(p, q, Chessboard, color, 1, 0);//下
k += checkpos(p, q, Chessboard, color, -1, -1);//左上
k += checkpos(p, q, Chessboard, color, 1, -1);//左下
k += checkpos(p, q, Chessboard, color, -1, 1);//右上
k += checkpos(p, q, Chessboard, color, 1, 1);//右下
return k;
}
int calculatevalue(int p, int q, int Chessboard[][15],int color)这一函数用于分别计算八个方位价值
传入的参数说明:
int p:AI函数中遍历到的x值
int q:AI函数中遍历到的y值
int Chessboard[][15]:与上同,用于记录棋盘的二维数组,15代表棋盘大小15x15
int color:与上同,这一局中电脑所持的棋子的颜色(黑色为1,白色为-1)
返回值说明:
return k:k为该遍历到的位置的价值量的累加值,也就是这个位置最终的价值量
函数
int checkpos(int p, int q, int Chessboard[][15], int color, int pos_x, int pos_y)//每个方向分别判断有几个子
{
int k = 0;
int check;
int spare;
if (checkaround(p, q, Chessboard, pos_x, pos_y))//判断每一个空位选定的方向有没有相邻的子
{
check = ai_judga_line(Chessboard, 15, p + pos_x, q + pos_y, pos_x, pos_y, 1, color);//判断连成线的1个子是不是一样的
k += checkline(1, check, p, q, Chessboard, color);//1
check = ai_judga_line(Chessboard, 15, p+ pos_x, q + pos_y, pos_x, pos_y, 2, color);//判断连成线的2个子是不是一样的
k += checkline(2, check, p, q, Chessboard, color);//2
check = ai_judga_line(Chessboard, 15, p + pos_x, q + pos_y, pos_x, pos_y, 3, color);//判断连成线的3个子是不是一样的
k += checkline(3, check, p, q, Chessboard, color);//3
check = ai_judga_line(Chessboard, 15, p + pos_x, q + pos_y, pos_x, pos_y, 4, color);//判断连成线的4个子是不是一样的
k += checkline(4, check, p, q, Chessboard, color);//4
}
return k;
}
int checkpos(int p, int q, int Chessboard[][15], int color, int pos_x, int pos_y)这个函数用于对于每个方向分别判断相邻有几个子
传入的参数说明:
int p:与上同,AI函数中遍历到的x值
int q:与上同,AI函数中遍历到的y值
int Chessboard[][15]:与上同,用于记录棋盘的二维数组,15代表棋盘大小15x15
int color:与上同,这一局中电脑所持的棋子的颜色(黑色为1,白色为-1)
int pos_x:用于表示方向的纵坐标
int pos_y:用于表示方向的横坐标
例,( 0, -1)为左,(-1, 1)为右上
返回值说明:
return k:k为该遍历到的位置的价值量的累加值
函数
int checkaround(int p, int q, int Chessboard[][15], int pos_p, int pos_q)//判断每一个空位选定的方向有没有相邻的子
{
if (Chessboard[p + pos_p][q + pos_q] != 0)
{
return 1;
}
return 0;
}
int checkaround(int p, int q, int Chessboard[][15], int pos_p, int pos_q)这一函数用于判断每一个空位选定的方向有没有相邻的子
传入的参数说明:
int p:与上同,AI函数中遍历到的x值
int q:与上同,AI函数中遍历到的y值
int Chessboard[][15]:与上同,用于记录棋盘的二维数组,15代表棋盘大小15x15
int pos_x:与上同,用于表示方向的纵坐标
int pos_y:与上同,用于表示方向的横坐标
例,( 0, -1)为左,(-1, 1)为右上
返回值说明:
周围有棋子则return 1,否则return 0
5.ai_judga_line函数
int ai_judga_line(int Chessboard[][15], int ln, int XS, int YS, int dx, int dy,int num,int color)//判断连成线的num个子是不是一样的
{
assert((Chessboard != NULL) && (ln > 0));
if ((XS < ln) && (YS < ln) && (XS >= 0) && (YS >= 0) && (dx != 0 || dy != 0)) //起点坐标在棋盘内//坐标增量不为同时0
{
if (((XS + dx * (num-1)) > ln) || ((XS + dx * (num - 1)) < 0) || ((YS + dy * (num - 1)) > ln) || ((YS + dy * (num - 1)) < 0) || (0 == Chessboard[XS][YS]))//判断终点坐标//在棋盘外
{
return 0; //不在棋盘内,或者起点是没下子
}
else
{
int i = 0;
for (i = 1; i < num; ++i)
{
if (Chessboard[XS][YS] != Chessboard[XS + (dx * i)][YS + (dy * i)])
{
return 0; //如果不是连续num个一样的
}//end if3
}//end for1
if (Chessboard[XS][YS] != color)
{
return 1; //num个都是对手的棋子,且都在棋盘内
}
if (Chessboard[XS][YS] == color)
{
return 2; //num个都是自己的棋子,且都在棋盘内
}
}//end if 2
}
return 0; //其他情况
}
int ai_judga_line(int Chessboard[][15], int ln, int XS, int YS, int dx, int dy,int num,int color)这个函数用于判断连成线的num个子是不是一样的
传入的参数说明:
int Chessboard[][15]:与上同,用于记录棋盘的二维数组,15代表棋盘大小15x15
int ln:与上同,棋盘大小(横纵坐标的最大值)(也传15即可)
int XS:相当于p+ pos_x,遍历到的位置周围的某方向上的棋子位置
int YS:相当于q + pos_y,遍历到的位置周围的某方向上的棋子位置
int dx:即int pos_x:用于表示方向的纵坐标
int dy:即int pos_y:用于表示方向的横坐标
int num:判断连成线的num个子是不是一样的
int color:与上同,这一局中电脑所持的棋子的颜色(黑色为1,白色为-1)
返回值说明:
在这一方向上有相同的棋子连成num个则return 1,否则return 0
函数
int checkline(int linenum,int check,int p, int q, int Chessboard[][15], int color)//根据落子数量计算价值量
{
int num = linenum + 1;
int k = 0;
int value=1;
for (; linenum > 0; linenum--)//加权重
{
value = value * 10;
}
if (check)
{
k += value;
if (check == 1)//对手的棋子
{
k += 20;
}
if (check == 2)//自己的棋子
{
k += 10;
}
if (q - num >= 0)//这几个相连的棋子的下一个位置还在棋盘内
{
if (Chessboard[p][q - num] == 0)//下一个位置的空的
{
k += 10;
if (linenum == 3)//活三
{
k += 10000000;
}
}
if (Chessboard[p][q - num] == color)//下一个位置是自己的棋子
{
if (linenum == 4)//连四
{
k += 100000000;
}
}
if (Chessboard[p][q - num] == (0 - color))//下一个位置是对方的棋子
{
k += 20;
}
}
}
return k;
}
int checkline(int linenum,int check,int p, int q, int Chessboard[][15], int color)这一函数用于根据落子数量计算价值量
传入的参数说明:
int linenum:连成linenum个的相同颜色的棋子
int check:在这一方向上有棋子则为 1,否则为 0
int p:与上同,AI函数中遍历到的x值
int q:与上同,AI函数中遍历到的y值
int Chessboard[][15]:与上同,用于记录棋盘的二维数组,15代表棋盘大小15x15
int color:与上同,这一局中电脑所持的棋子的颜色(黑色为1,白色为-1)
返回值说明:
return k:k为该遍历到的位置的价值量的累加值
最后的话:
这是我刚刚接触c语言时老师布置的作业中的一段代码,由于不够有经验,因此在函数和变量的命名上还有待改进,并且由于我对人工智能中加权的权值如何确定上还存在很多疑问,因此这段代码中加的权值也不够完美,请大家见谅