当前位置:文档之家› 五子棋源码实验报告及人机对战说明

五子棋源码实验报告及人机对战说明

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;

相关主题
文本预览
相关文档 最新文档