资源描述
电脑围棋程序编写思路
荷蒲 wq、hepu
荷蒲围棋软件得编写思路。
在围棋程序得实现中,较为关键得就是围棋电子棋盘得设计。其她所有功能都就是围绕围棋电子棋盘展开得。电子围棋盘得核心就是围棋棋盘得数据描述。
1、首先定义围棋子信息:
#define EDGE 23 //棋盘最大格数
#define MAXMM 500 //最大手数
//color表示棋子颜色,x,y表示在棋盘上得坐标
//num表示下子得顺序。=0表示提前摆放得子。
//zt 表示棋子状态
//qs 表示棋子得气数
//sm 表示有说明信息
typedef struct qizi
{
int color,x,y,num,zt,qs,sm;
} qizi;
qizi qipu[MAXMM]; //棋谱信息
qizi qipan[EDGE][EDGE]; //棋盘信息
2、紧接着要考虑得就是下棋相关信息。
int nk=0; //显示棋子序号,nk=2显示序号,1=气数
int BoardLines=19; //棋盘线数,默认19
bool puterPlaying; //1=该计算机下 0=人下
bool puterp1=0; //1=计算机下黑 0=人下
bool puterp2=0; //1=计算机下白 0=人下
int PlayType=0; //2=人人,1=人计算机,13=人网络,0=没有开始,1=删除棋盘上死子,2=暂停,3=布黑子,4=布白子,9=演示,11=学习
int PlayType1=0; //2=人人,1=人计算机,13=人网络,0=没有开始,1=删除棋盘上死子,2=暂停,3=布黑子,4=布白子,11=学习
int MoveCount,MoveCount1; //计步器,记录落子手数,自然顺序
int Playnum=0,Playnum1=0; //要标识得围棋手数,下棋顺序
int CurrentX; //记录热子X坐标,
int CurrentY; //记录热子Y坐标
char CurrentWho; //记录当前棋子颜色,0=黑 1=白 2=空(终局等,待写)
char CurrentWho1; //备份上一次CurrentWho
int timew=0,timeb=0; //计时器设定数据
int sdy1=0,sdy2=0; //学习功能上使用
int gz; //规则0=中国规则,1=日本规则,2=应氏规则
bool plays1=true; //学习持黑
bool plays2=false; //学习持白
3、围棋电子棋盘得数据初始化。
//数据初始化
void wqinit(void)
{
BoardLines=19; //19X19路标准围棋盘
MoveCount=0; //一步棋未下,自然顺序
MoveCount1=0; //一步棋未下
puterPlaying=1; //默认电脑执黑先行
CurrentWho=0; //默认黑先; 黑方=0;白方=1;空方=2;
CurrentX=0; //当前一步棋得X坐标,水平从左至右为1、、、19
CurrentY=0; //当前一步棋得Y坐标,垂直从上到下为1、、、19
timew=0,timeb=0;
Playnum=0; //下棋顺序
Playnum1=0;
//下面就是棋盘初始化
for (int i=0;i<=BoardLines;i++)
for (int j=0;j<=BoardLines;j++)
{
qipan[i][j]、color=2;
qipan[i][j]、x=0;
qipan[i][j]、y=0;
qipan[i][j]、num=0;
qipan[i][j]、zt=0;
}
//清空棋谱记录,全部设为无效点。QiPu[0][x]留作它用
for (int i=0;i<500;i++)
{
qipu[i]、color=2;
qipu[i]、x=0;
qipu[i]、y=0;
qipu[i]、num=0;
qipu[i]、zt=0;
qipu[i]、sm=0;
qpsm1[i]、n=0;
qpsm1[i]、t=0;
strcpy(qpsm1[i]、sm,"/");
}
}
4、根据围棋规则编写得一些相关处理函数模块
围棋棋子得吃子,就是根据围棋棋子得气数来计算得。气数为0得棋子应当从棋盘上拿掉。
围棋气数得计算问题,应当说就是围棋软件得核心问题。
“气”就是指棋子在棋盘上可以连接得交叉点,也就是棋子得出路。
围棋得气数计算,要考虑一个围棋子得连通问题。
下面得图形中,交叉点得X代表棋子得气,
[图1]
图1中右上角得黑子,有两个交叉点与它得直线相接,因此它有两口气。左上角得黑子有三口气,而下边得黑子有四口气。
[图2]
图2中右边得黑子有四口气,中间连接在一起得两个黑子有六口气,而右边连接在一起得三个黑子有八口气。连接在一起得棋子越多,气也越多。
[图3]
图2中同样就是四个连接在一起得黑子,左边得四个黑棋有十口气,中间得黑棋只有九口气,而右边得黑棋仅有八口气。
从上面分析,可以得出,计算一个棋子得气,还有分析该棋子周围得情况,因此我们利用递归函数来解决围棋气数得计算。实现方法瞧下面程序断。
int go[EDGE][EDGE];
/*表示棋盘 其中第0路与第20路为沉余数据
在 (X,Y)下黑子
go[x][y]=0;// 0表示黑
在 (X,Y)下白子
go[x][y]=1;// 1表示白子
在 (X,Y)提子
go[x][y]=2; //2表示空子
当前棋步脱先pass则 当前棋步 X坐标=0,y=0
否则 1<=x<=19, 1<=x<=19,
*/
int gokong[EDGE][EDGE]; //0=该空点未曾计算过气,1=已计算,避免重复计算公气
int gozi[EDGE][EDGE]; //0=该子未计算串气,1=已计算,避免重复计算同一个子得气
int goqi; //气数
//以上变量声明为全局变量
void str_qi(int x,int y,int hb)
{//本函数计算 x,y 处得hb颜色棋子得气
gozi[x][y]=1; //标记本子已经计算过气
/////////////////////////////////////////////////////右临子
if (x+1<=19)//如果没有超出棋盘边线
{
if ((go[x+1][y]==2)&&(gokong[x+1][y]==0))
//如果右临点为空并且该点未曾计算过气则
{
goqi++; //气数加一
gokong[x+1][y]=1; //标记本空点已经计算过气
}
else if ((go[x+1][y]==hb)&&(gozi[x+1][y]==0))
//否则如果右临点为与本子同色子并且该子未曾计算过气则
str_qi(x+1,y,hb); //递归调用到右临子
}
/////////////////////////////////////////////////////左临子
if (x1>=1) //果没有超出棋盘边线
{
if ((go[x1][y]==2)&&(gokong[x1][y]==0))
//如果左临点为空并且该点未曾计算过气则
{
goqi++; //气数加一
gokong[x1][y]=1; //标记本空点已经计算过气
}
else if ((go[x1][y]==hb)&&(gozi[x1][y]==0))
//否则如果左临点为与本子同色子并且该子未曾计算过气则
str_qi(x1,y,hb); //递归调用到左临子
}
////////////////////////////////////////////////////下临子
if (y1>=1)//如果没有超出棋盘边线
{
if ((go[x][y1]==2)&&(gokong[x][y1]==0))
//如果下临点为空并且该点未曾计算过气则
{
goqi++; //气数加一
gokong[x][y1]=1; //标记本空点已经计算过气
}
else if ((go[x][y1]==hb)&&(gozi[x][y1]==0))
//否则如果下临子点为与本子同色子并且该子未曾计算过气则
str_qi(x,y1,hb); //递归调用到下临子
}
////////////////////////////////////////////////////上临点
if (y+1<=19)//如果没有超出棋盘边线
{
if ((go[x][y+1]==2)&&(gokong[x][y+1]==0))
//如果上临点为空并且该点未曾计算过气则
{
goqi++; //气数加一
gokong[x][y+1]=1; //标记本空点已经计算过气
}
else if ((go[x][y+1]==hb)&&(gozi[x][y+1]==0))
//否则如果上临点为与本子同色子并且该子未曾计算过气则
str_qi(x,y+1,hb); //递归调用到上临子
}
}
int str_lib(int x,int y, int hb)
{
int i,j;
for (i = 1; i <= 19; i++)
for (j = 1; j <= 19; j++)
{
gozi[i][j] = 0; //初始化变量,表示该子未计算串气
gokong[i][j] = 0; //初始化变量,表示该空点未计算串气
}
goqi=0; //串气初值
str_qi(x,y,hb); //调用串气子程序
return(goqi); //全局变量goqi带回串气值
}
void shuanqi(void)
{
int i,j,cc,qq;
for (i = 1; i <=19; i++)
for (j = 1; j <= 19; j++)
{
go[i][j]=qipan[i][j]、color;
}
for (i = 1; i <=19; i++)
for (j = 1; j <=19; j++)
{
if (go[i][j]!=2)
{
cc=go[i][j];
qq=str_lib(i,j,cc);
qipan[i][j]、qs=qq;
}
}
}
5、围棋得提子(吃子)
提子:就就是把没有气得棋子从棋盘上拿掉。
下面函数实现提子功能。
void chizi(void)
{
int i,j,qq,cc;
shuanqi;
for (i = 1; i <=19; i++)
for (j = 1; j <= 19; j++)
{
qq=qipan[i][j]、qs;
cc=qipan[i][j]、color;
if (qq==0 && cc!=2 && cc!=CurrentWho)
{
qipan[i][j]、color=2;
qipan[i][j]、x=i;
qipan[i][j]、y=j;
qipan[i][j]、num=0;
qipan[i][j]、zt=0;
}
}
}
围棋程序设计得核心,基本完成,下面就是输赢得判断问题。
6、围棋胜负判断
围棋盘上共有三百六十一个交叉点,一盘棋得胜负就就是由对局双方所占据得交叉点得多少所决定得。更精确地说就就是由双方活棋所占据得地域得大小来决定得。一个交叉点为一子,每方以一百八十又二分之一子为归本数,超过此数者为胜,不足此数者为负。
按我国现行得围棋规则规定,由于黑棋先走,有一定得先手威力,应由执黑得一方贴出2(3/4)子。所以黑所占得地域必须超过183(1/4)子(180 (1/2)+2(3/4))才能取胜。比如黑棋数出来有185个子,即黑棋1(3/4)子。而白方得地域只要超过177(3/4)子(180(1/2)-2(3/4))即可获胜。
[图4]
下面函数实现计算围棋得地域功能。在计算前,应当先去掉围棋中得死子。
// 计算围棋得地域
int sum[3]; //sum[0]=黑子数量,sum[1]=白子数量
int summ(void)
{
int i=0,j=0,c=2,k=2;
sum[0]=0;
sum[1]=0;
sum[2]=0;
for (i=1;i<=19;i++)
{
k=qipan[i][1]、color;
for (j=1;j<=19;j++)
{
c=qipan[j][i]、color;
switch (c)
{
case 2:
if (k==2) sum[2]++;
else sum[k]++;
break;
case 0:
if (k==0)
{
sum[c]++;
}
else if(k==2)
{
sum[c]=sum[c]+sum[2]+1;
k=c;
sum[2]=0;
}
else if(k==1)
{
sum[c]++;
k=c;
sum[2]=0;
}
break;
case 1:
if (k==1)
{
sum[c]++;
}
else if(k==2)
{
sum[c]=sum[c]+sum[2]+1;
k=c;
sum[2]=0;
}
else if(k==0)
{
sum[c]++;
k=c;
sum[2]=0;
}
break;
}
}
}
return sum[0];
}
上面介绍了我国围棋规则,还有日本规则与应氏规则,这两种规则主要就是计算胜负得不同,在围棋行棋上没有太大得区别。这方面内容,我们以后逐步就是完成。
围棋程序得其她方面得设计与编写,主要依赖于计算机得编程环境。例如选择得计算机语言等等。后面我们针对C++Bulider 6得语言特点,专门介绍。
展开阅读全文