1.五子棋对战说明
2.实验报告
3.源代码
五子棋
作品特点:C语言程序五子棋
作品功能:五子棋人机对战,人人对战。
目录:1 五子棋介绍。
2五子棋棋型介绍。
3人人对战的实现。
4电脑下子的实现。
5棋型价值的计算。
6胜利及棋型的判断。
7补充说明
1五子棋介绍。
五子棋是一种两人对弈的纯策略型棋类游戏。只要任意一方在棋盘上且同一个回合上连为五子为胜。还有禁手规则,在本程序中不作讨论。
2五子棋棋型介绍。
本程序中的棋型均为本人自定义。本程序总共设计35种棋型。●表示玩家的棋子,◎表示电脑的棋子。以下称电脑方为己方,玩家方为对方。从一空点向某一方向判断该方向的棋型。某一方向指1-8方向从右顺时针开始数。
(1)空棋型。从一空点向一方向看连续2个为空的棋型。空棋型共1种。
如图,从左端的空点向右看会发现有连续2个空点。
(2)活棋型。2端无挡的棋型为活棋型。活棋型共8种:己方4种,对方4种。
左图为己活3 。从左端的空点向右看会发现己方有连续的3个子,且右端无挡。故
该点的1方向为己活3。
左图为对活2
(3)冲棋型。1端无挡的棋型为冲棋型。冲棋型共9种:己方4种,对方4种,边界1种。
左图为边界冲棋型。空点的右端为边界。
或左图为己冲2。从左端的空点向右看会发现己方有连续的2个子,且右端
有挡(此处有挡表示有对方的子或为边界)。故该点的
1方向为己冲2。
左图为对冲4。
(4)空活棋型。从一空点向一方向看有1个空点,继续看有己方或对方的活棋型。空活棋型共8种:
己方4种,对方4种。
左图为己空活2。从左端的空点向右看有1个空点,继续看会发现己方有连续的2个
子,且右端无挡。故该点的1方向为己空活2。
左图为对空活1。
(5)空冲棋型。从一空点向一方向看有1个空点,继续看有己方或对方或边界冲棋型。空冲棋型共9
种:己方4种,对方4种,边界1种。
左图为边界空冲棋型。空点的右端为空点再右看为边界。
或左图为己空冲2。
从左端的空点向右看有1个空点,继续看会发现己方
有连续的2个子,且右端有挡。故该点的1方向为己
空冲2。
3人人对战的实现。
双方玩家轮流下子,直到一方形成五连即判为胜方。下子时输入棋盘上显示的对应坐标。如果某方需要悔棋,则输入 15 15 即可。悔棋只能悔一步。在人机对战中亦可悔棋。
4电脑下子的实现。
人机对战中电脑下子是通过AI(int *p,int *q)这个函数实现的。用p 、q返回下子的坐标。先历遍棋盘上所有点,如发现一个空点则调用函数value(int p,int q)计算该空点的价值。每个点又由8个方向的棋型组成。调用函数qixing(int n,int p,int q)判断空点p q在n方向上的棋型号。对每种棋型进行赋值,然后对各个方向的棋型进行分析。最后计算出该空点的价值。如此找到棋盘上价值最大的空点,则电脑在该处下子。
5 棋型价值的计算。
棋型价值的计算是通过函数value(int p,int q)实现的。先调用函数qixing(int n,int p,int q) 判断空点p q在n方向上的棋型号。n为1-8方向从右顺时针开始数。
对8个方向的棋型进行分析后给出该点的价值。各种棋型的价值存在数组a[2][4][4]中。本程序将两相反方向的棋型进行合在一起进行分析。本程序分为六类进行讨论。
①空棋型and其他。
②边界冲棋型and其他。
③边界空冲棋型and其他。
④己活己活己活己冲对活对活对活对冲。
⑤己活对活己活对冲己冲对活己冲对冲。
⑥其他棋型。
⑦
6 胜利及棋型的判断。
胜利及棋型的判断都调用了函数yiwei(int n,int *i,int *j)。在n方向上对坐标 i j 移位。 n为1-8方向从右顺时针开始数。
胜利的判断:每下一次子从该点向1方向移位,移位后判断新点是否与下的子相同。如相同则继续移位判断,否则转向判断即n+=4;如转向后仍然没五连,则换下一个方向判断即n-=3;直到出现五连则胜利,如果8个方向判断完都没有五连则返回0,表示还未胜利。
棋型的判断:棋型的判断主要运用switch语句。在某一方向移位后判断该点的状态。最后得出该方向上的棋型号。
7补充说明
1.在WIN7环境下用VC++运行棋盘之间有空隙,影响美观。
建议在XP操作系统下运行。
2.程序经过多次修改。各次修改如下:
V1.1 修改了胜负判断函数win的算法。
V1.2 加上了人人对战的功能,并加上了开始界面。
V1.3 增加了一些注释,并对棋型值数组做了更改。
V1.4 修正了2个BUG,进一步对棋型值数组做了更改。
V1.5 修改了画棋盘draw的算法,改变了主函数main,增加函数start,并增加了赢棋之后是否继续的功
能,还完善了各棋型的赋值,进一步对棋型值数组
做了更改。
实验报告
人人对战
源码:
#include "windows.h" #include "stdio.h"
#include "conio.h"
#define up 'w'
#define left 'a'
#define down 's'
#define right 'd'
#define lz 'p'
#define cls 'm'
# define SPA 0
# define MAN 1
# define COM 2 /* 空位置设为0 ,玩家下的位置设为1 ,电脑下的位置设为2 */
struct stu //定义位置记录结构体
{
int x;
int y;
}weizhi;
int player=0;
int Q[15][15]={0}; //定义数组以记录落子情况
void gotoxy(int x, int y) ;//建立坐标函数
void drawqipan(); //绘制棋盘及数据初始化
void jilu(); //人机记录落子情况
int cluozi(int x,int y); //由电脑落子时调用
void luozi(); //人机玩家落子
int checkWin(); //检查游戏是否有输赢
void Keypress(char n); //人机光标位置移动
void Keypress1(char n); //人人光标位置移动
void yiwei(int n,int *i,int *j); /* 在n方向上对坐标i j 移位n为1-8方向从右顺时针开始数*/
int qixing(int n,int p,int q) ; /* 返回空点p q在n方向上的棋型号n为1-8方向从右顺时针开始数*/
int value(int p,int q); /* 计算空点p q的价值以k返回*/
void AI(int *p,int *q);/* 电脑下子*p *q返回下子坐标*/
void luozi1(); //人人玩家落子
void jilu1(); //人人记录落子情况
void gotoxy(int x, int y) //建立坐标函数
{
COORD c;
c.X=2*x;
c.Y=y;
SetConsoleCursorPosition
(GetStdHandle(STD_OUTPUT_HANDLE), c); //修改当前光标的位置
}
void drawqipan() //绘制棋盘及数据初始化
{
int i,j;
system("cls"); //清除屏幕
for(i=0;i<15;i++)
{
for(j=0;j<15;j++)
{
Q[i][j]=0;
printf("十");
}
printf("\n");
}
weizhi.x=6;weizhi.y=6; //程序数据初始化
gotoxy(6,6);
}
void jilu() //记录落子情况
{
Q[weizhi.y][weizhi.x]=player+1;
if(player==1) player=0; //玩家变换
else player=1;
}
void jilu1() //记录落子情况
{
Q[weizhi.x][weizhi.y]=player+1;
if(player==1) player=0; //玩家变换
else player=1;
}
int cluozi(int x,int y) //由电脑落子时调用
{
weizhi.x=x;weizhi.y=y;
gotoxy(weizhi.x,weizhi.y);
jilu();
printf("○");
gotoxy(weizhi.x,weizhi.y);
}
void luozi() //玩家落子
{
if(Q[weizhi.y][weizhi.x]==0) //判断当前位置是否已经落子{
gotoxy(weizhi.x,weizhi.y);
jilu();
printf("●");
gotoxy(weizhi.x,weizhi.y);
}
}
void luozi1() //玩家落子
{
if(Q[weizhi.x][weizhi.y]==0) //判断当前位置是否已经落子
{
if(player)
{
jilu1();
printf("●");
}
else
{
jilu1();
printf("○");
}
gotoxy(weizhi.x,weizhi.y);
}
}
int checkWin() //检查游戏是否有输赢
{
int p;
int r,c,rr,cc,count=0;
p=player==0?2:1;
for(c=0;c<15;c++)
{
for(r=0;r<15;r++)
{
if(Q[r][c]!=p)
continue;
//检查列
rr=r;cc=c;
while(--cc>=0 &&Q[rr][cc]==p)count++; cc=c;
while(++cc<15 &&Q[rr][cc]==p)count++; cc=c;
if(count+1>=5)
return p;
//检查行
count=0;
while(--rr>=0 &&Q[rr][cc]==p)count++; rr=r;
while(++rr<15 &&Q[rr][cc]==p)count++; rr=r;
if(count+1>=5)
return p;
//检查反斜边
count=0;
cc--;rr--;
while((cc>=0||rr>=0)
&&Q[rr][cc]==p){count++;cc--;rr--;} rr=r;cc=c;
cc++;rr++;
while((cc<15||rr<15)
&&Q[rr][cc]==p){count++;cc++;rr++;} rr=r;cc=c;
if(count+1>=5)
return p;
count=0;
//检查正斜边
count=0;
cc++;rr--;
while((cc<15||rr>=0)
&&Q[rr][cc]==p){count++;cc++;rr--;} rr=r;cc=c;
cc--;rr++;
while((cc>=0||rr<15)
&&Q[rr][cc]==p){count++;cc--;rr++;} rr=r;cc=c;
if(count+1>=5)
return p;
count=0;
}
}
return 0;
}
void Keypress(char n) //光标位置移动
{
switch(n)
{
case up:weizhi.y--;gotoxy(weizhi.x,weizhi.y);break;
//向上移动光标
case left:weizhi.x--;gotoxy(weizhi.x,weizhi.y);break;
//向左移动光标
case right: weizhi.x++;gotoxy(weizhi.x,weizhi.y);break;
//向右移动光标
case down: weizhi.y++;gotoxy(weizhi.x,weizhi.y);break;
//向下移动光标
case lz:luozi();break;
//开始落子操作
case cls:drawqipan();break;
//重新开始
}
}
void Keypress1(char n) //光标位置移动
{
switch(n)
{
case up:if(weizhi.y<=0)weizhi.y=14;else weizhi.y--;gotoxy(weizhi.x,weizhi.y);break;
//向上移动光标
case left:if(weizhi.x<=0)weizhi.x=14;else weizhi.x--;gotoxy(weizhi.x,weizhi.y);break;
//向左移动光标
case right:if(weizhi.x>=14)weizhi.x=0;else weizhi.x++;gotoxy(weizhi.x,weizhi.y);break;
//向右移动光标
case down:if(weizhi.y>=14)weizhi.y=0;else weizhi.y++;gotoxy(weizhi.x,weizhi.y);break;
//向下移动光标
case lz:luozi1();break;
//开始落子操作
case cls:drawqipan();break;
//重新开始
}
}
void yiwei(int n,int *i,int *j) /* 在n方向上对坐标i j 移位n为1-8方向从右顺时针开始数*/
{
switch(n)
{
case 1:*i+=1;break;
case 2:*i+=1;*j+=1;break;
case 3:*j+=1;break;
case 4:*i-=1;*j+=1;break;
case 5:*i-=1;break;
case 6:*i-=1;*j-=1;break;
case 7:*j-=1;break;
case 8:*i+=1;*j-=1;break;
}
}
int qixing(int n,int p,int q) /* 返回空点p q在n方向上的棋型号n为1-8方向从右顺时针开始数*/
{
int k,m=0; /* 棋型号注解: 己活000-003 己冲010-013 对活100-103 对冲110-113 己空活020-023 己空冲030-033 对空活120-123 对空冲130-133 空-1 边界冲-2 边界空冲-3*/
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14) k=-2; /* 边界冲棋型*/
switch(Q[q][p])
{
case COM:
{
m++;
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14)
{
k=m+9;
return k;
}
while(Q[q][p]==COM)
{
m++;
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14)
{
k=m+9;
return k;
}
}
if(Q[q][p]==0) k=m-1; /* 己方活棋型*/
else k=m+9; /* 己方冲棋型*/
}
break;
case MAN:
{
m++;
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14)
{
k=m+109;
return k;
}
while(Q[q][p]==MAN)
{
m++;
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14)
{
k=m+109;
return k;
}
}
if(Q[q][p]==SPA) k=m+99; /*
对方活棋型*/
else k=m+109; /* 对方冲棋型*/
}
break;
case SPA:
{
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14)
{
k=-3; /* 边界空冲棋型*/
return k;
}
switch(Q[q][p])
{
case COM:
{
m++;
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14)
{
k=m+29;
return k;
}
while(Q[q][p]==COM)
{
m++;
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14)
{
k=m+29;
return k;
}
}
if(Q[q][p]==SPA) k=m+19; /* 己方空活棋型*/
else k=m+29; /* 己方空冲棋型*/
}
break;
case MAN:
{
m++;
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14)
{
k=m+129;
return k;
}
while(Q[q][p]==MAN)
{
m++;
yiwei(n,&p,&q);
if(p<0||p>14||q<0||q>14)
{
k=m+129;
return k;
}
}
if(Q[q][p]==SPA) k=m+119; /* 对方空活棋型*/
else k=m+129; /* 对方空冲棋型*/
}
break;
case SPA:
k=-1;
break;
/* 空棋型*/
}
}
break;
}
return k;
}
int value(int p,int q) /* 计算空点p q的价值以k返回*/
{
int n=1,k=0,k1,k2,K1,K2,X1,Y1,Z1,X2,Y2,Z2,temp;
int a[2][4][4]= {40,400,3000,10000,6,10,600,10000,20,120,200,0,6,10,500,0,30,300,2500,5000,2,8,300, 8000,26,160,0,0,4,20,300,0};
/* 数组a中储存己方和对方共32种棋型的值己方0对方1 活0冲1空活2空冲3 子数0-3(0表示1个子,3表示4个子)*/
while(n!=5)
{
k1=qixing(n,p,q);
n+=4; /* k1,k2为2个反方向的棋型编号*/
k2=qixing(n,p,q);
n-=3;
if(k1>k2)
{
temp=k1; /* 使编号小的为k1,大的为k2 */
k1=k2;
k2=temp;
}
K1=k1;
K2=k2; /* K1 K2储存k1 k2的编号*/
Z1=k1%10;
Z2=k2%10;
k1/=10;
k2/=10;
Y1=k1%10;
Y2=k2%10;
k1/=10;
k2/=10;
X1=k1%10;
X2=k2%10;
/* X Y Z分别表示己方0对方1 活0冲1空活2空冲3 子数0-3(0表示1个子,3表示4个子)*/
if(K1==-1)
{
if(K2<0)
{
k+=0;
continue;
}
else k+=a[X2][Y2][Z2]+5;
continue;
}; /* 空棋型and其他*/
if(K1==-2)
{
if(K2<0)
{
k+=0;
continue;
}
else k+=a[X2][Y2][Z2]/2;
continue;
}; /* 边界冲棋型and其他*/
if(K1==-3)
{
if(K2<0)
{
k+=0;
continue;
}
else k+=a[X2][Y2][Z2]/3;
continue;
}; /* 边界空冲棋型and其他*/
if(((K1>-1&&K1<4)&&((K2>-1&&K2<4)||(K2>9&&K2<14)))||((K1>99&&K1<104)& &((K2>99&&K2<104)||(K2>109&&K2<114))))
{
/* 己活己活己活己冲对活对活对活对冲的棋型赋值*/
if(Z1+Z2>=2)
{
k+=a[X2][Y2][3];
continue;
}
else
{
k+=a[X2][Y2][Z1+Z2+1];
continue;
}
}
if(((K1>9&&K1<14)&&(K2>9&&K2<14))||((K1>109&&K1<114)&&(K2>109&&K2< 114)))
{
/* 己冲己冲对冲对冲的棋型赋值*/
if(Z1+Z2>=2)
{
k+=10000;
continue;
}
else
{
k+=0;