c语言编程一个象棋游戏,急求:C语言编写的中国象棋游戏一个

急求:C语言编写的中国象棋游戏一个

來源:互聯網  2009-09-08 12:30:35  評論

分類: 電腦/網絡 >> 程序設計 >> 其他編程語言

問題描述:

由于学习需要......热烈欢迎个位大侠,高手相助!小生在此道谢了!!! 希望留下您的邮箱或者QQ以遍交流。20分哦

參考答案:

先弄明白数据的结构:

MantisChessDef.h里的东西一定要先看一下, 否则会摸不到头脑的。

还有棋盘坐标:

象棋棋盘大小9x10,为了便于编程,规定棋盘每条边留有一个元素的边界。

这样棋盘大小(包括边界)变成11x12。棋盘x坐标轴向右,y轴向下。

黑棋永远在上方,在标准开局时左上角的黑车坐标是(1,1)。

局面用这三个变量表示:

static POINT g_pointChessman[32]; //棋子坐标

static int g_iChessmanMap[11][12]; //棋位状态

static int g_iSide; //轮到哪方走

智能部分有几个函数的前三个参数就是这个东西, 应该不难理解吧?

---------------------------------------------------------------------------------------

search函数:

先说明一下, 经常有朋友问我要原理, 但我公开源代码是给大家一个参考, 而不是什么教程,所以我不想说那些理论的东西。

基本原理是α-β搜索, 很多人工智能的教科书上都有讲到, 没看过的的赶快去找一本来啃一啃;

虽然这些书上的文字大多晦涩难懂,但毕竟讲得明明白白。

没有书的朋友请发挥一下主观能动性, 去找一找,不要来问我要, 因为我也没有。

我在这里只分析一下search函数:

弄懂α-β搜索后来看看这个博弈树, 看怎么编程实现它。

先规定一下, 我们用一个整数表示局面的好坏.

这个数越大说明局面对 "走棋方" 越有利,0表示双方实力相等。

1a( 1) ┬ 2a(-1) ┬ 3a(-1)

│ └ 3b( 1)

└ 2b(-5) ┬ 3c( 2)

├ 3d(-4)

└ 3e( 5)

分析一下这棵树,有这么个特点: 父结点的值 = -MAX(子结点的值)

我们还知道1、每个结点对应一个局面。2、底层的结点的值是"估"出来的。

于是我们可以写出伪代码了:

伪代码: 搜索一个结点下的分支, 得到这个结点的值。

参数: 局面,搜索深度

返回值:结点的值

int search(局面,int depth)

{

if(depth!=0)//不是底层结点

{

枚举出所有子结点(列出所有走法);

int count=子结点数;

int maxvalue= -∞;

for(int i=0;i

{

算出子结点局面;

maxvalue=max(maxvalue,search(子结点局面,depth-1));

}

return -maxvalue;

}

else //是底层结点

{

return 估计值;

}

}

这就是搜索算法的框架, 用到了递归。

MantisChess的智能部分函数都在MantisChessThink.cpp里, 其中search是搜索, 跟上面的这个search差不多,我把它copy出来注释一下:

int Search(int tmap[11][12],POINT tmanposition[32],int &tside,int man, POINT point,int upmax,int depth)

{

//前面的三个参数就是局面。

//man 和point 是走法,用来计算本结点的局面。 这里是把计算局面放在函数的开头,跟上面的伪代码不太一样。

//upmax: up - 上一层, max - 最大值, 这是α-β的剪枝用到的东西, 后面再讲。

//depth: 搜索深度

int ate,cur,maxvalue,curvalue,xs,ys;

int count;

//#####################这一段是计算本结点的局面#########################################

ate=32;

//移动棋子:

xs=tmanposition[man].x;ys=tmanposition[man].y; //原坐标

if (SideOfMan[tmap[point.x][point.y]]==!tside) //目标点有对方的棋子

{

ate=tmap[point.x][point.y]; //记录下被吃掉的棋子

if(ate==0 || ate==16)

{

return 9999;

}

tmanposition[ate].x=0; //目标点的棋子被吃掉

}

tmap[point.x][point.y]=man; //这两行是:

tmap[xs][ys]=32; //在map上的移动

tmanposition[man]=point;

tside=!tside;

//####################################################################################

depth--;

if(depth>0) //不是底层结点

{

int chessman[125];

POINT targetpoint[125];

if(EnumList(tmap,tmanposition,tside,chessman,targetpoint,count)) //枚举出所有子结点(列出所有走法)

{

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//这里是剪枝(不是α-β剪枝), 原理是在正式搜索之前先用较浅的搜索来得到误差较大的值

//然后根据这些值来对子结点排序, 只保留最好的S_WIDTH个结点进行正式搜索。

//显然,这个剪枝有一定的风险

if(depth>=2 && count>S_WIDTH+2)

{

int value[125];

cur=0;

maxvalue=-10000;

while(cur< count)

{

curvalue=Search(tmap,tmanposition,tside,chessman[cur],targetpoint[cur],-10000,depth-2);

value[cur]=curvalue;

if(curvalue>maxvalue)maxvalue=curvalue;

cur ++;

}

::Mantis_QuickSort(value,chessman,targetpoint,0,count-1); //排序

count=S_WIDTH;//剪枝

}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

maxvalue=-10000;

cur=0;

while(cur< count)

{

curvalue=Search(tmap,tmanposition,tside,chessman[cur],targetpoint[cur],maxvalue,depth);

if(curvalue>maxvalue)maxvalue=curvalue;

if(curvalue>=-upmax)goto _ENDSUB; //α-β剪枝, 符合剪枝条件的就Cut掉。 这里用了goto语句了, 千万别学我。

cur ++;

}

}

else maxvalue=9800;

}

else //是底层结点

{

maxvalue=Value(tmap,tmanposition,tside); //估值

}

_ENDSUB:

//返回之前要恢复父结点的局面

//####################################################################################

tmanposition[man].x=xs; //这两行是:

tmanposition[man].y=ys; //在face上的恢复

tmap[xs][ys]=man; //在map上的恢复

if(ate!=32)

{

tmanposition[ate]=point;

tmap[point.x][point.y]=ate;

}

else tmap[point.x][point.y]=32;

tside=!tside;

//####################################################################################

return -maxvalue;

}

上面的代码用到了α-β剪枝, 举个例子就明白了:

还是这个博弈树,从上往下遍历。

1a( 1) ┳ 2a(-1) ┳ 3a(-1)

┃ ┗ 3b( 1)

┗ 2b(-5) ┯ 3c( 2)

├ 3d(-4)

└ 3e( 5)

2a遍历完后 upmax=-1, 继续遍历完3c后返回2b, 发现3c=2>-upmax, 这时就不用管3d和3e了, 因为无论他们的值是多少 2b=-max(3c,3d,3e)<2a 一定成立,

也就是说2b可以安全地剪掉。这就是α-β剪枝。

从上面的代码来看我的MantisChess算法与标准的α-β剪枝搜索并没有什么不同, 只不过加了排序和剪枝而已。

[b]分类:[/b] 电脑/网络 >> 程序设计 >> 其他编程语言[br][b]问题描述:[/b][br]由于学习需要......热烈欢迎个位大侠,高手相助!小生在此道谢了!!! 希望留下您的邮箱或者QQ以遍交流。20分哦[br][b]参考答案:[/b][br]先弄明白数据的结构:

MantisChessDef.h里的东西一定要先看一下, 否则会摸不到头脑的。

还有棋盘坐标:

象棋棋盘大小9x10,为了便于编程,规定棋盘每条边留有一个元素的边界。

这样棋盘大小(包括边界)变成11x12。棋盘x坐标轴向右,y轴向下。

黑棋永远在上方,在标准开局时左上角的黑车坐标是(1,1)。

局面用这三个变量表示:

static POINT g_pointChessman[32]; //棋子坐标

static int g_iChessmanMap[11][12]; //棋位状态

static int g_iSide; //轮到哪方走

智能部分有几个函数的前三个参数就是这个东西, 应该不难理解吧?

---------------------------------------------------------------------------------------

search函数:

先说明一下, 经常有朋友问我要原理, 但我公开源代码是给大家一个参考, 而不是什么教程,所以我不想说那些理论的东西。

基本原理是α-β搜索, 很多人工智能的教科书上都有讲到, 没看过的的赶快去找一本来啃一啃;

虽然这些书上的文字大多晦涩难懂,但毕竟讲得明明白白。

没有书的朋友请发挥一下主观能动性, 去找一找,不要来问我要, 因为我也没有。

我在这里只分析一下search函数:

弄懂α-β搜索后来看看这个博弈树, 看怎么编程实现它。

先规定一下, 我们用一个整数表示局面的好坏.

这个数越大说明局面对 "走棋方" 越有利,0表示双方实力相等。

1a( 1) ┬ 2a(-1) ┬ 3a(-1)

│ └ 3b( 1)

└ 2b(-5) ┬ 3c( 2)

├ 3d(-4)

└ 3e( 5)

分析一下这棵树,有这么个特点: 父结点的值 = -MAX(子结点的值)

我们还知道1、每个结点对应一个局面。2、底层的结点的值是"估"出来的。

于是我们可以写出伪代码了:

伪代码: 搜索一个结点下的分支, 得到这个结点的值。

参数: 局面,搜索深度

返回值:结点的值

int search(局面,int depth)

{

if(depth!=0)//不是底层结点

{

枚举出所有子结点(列出所有走法);

int count=子结点数;

int maxvalue= -∞;

for(int i=0;i

{

算出子结点局面;

maxvalue=max(maxvalue,search(子结点局面,depth-1));

}

return -maxvalue;

}

else //是底层结点

{

return 估计值;

}

}

这就是搜索算法的框架, 用到了递归。

MantisChess的智能部分函数都在MantisChessThink.cpp里, 其中search是搜索, 跟上面的这个search差不多,我把它copy出来注释一下:

int Search(int tmap[11][12],POINT tmanposition[32],int &tside,int man, POINT point,int upmax,int depth)

{

//前面的三个参数就是局面。

//man 和point 是走法,用来计算本结点的局面。 这里是把计算局面放在函数的开头,跟上面的伪代码不太一样。

//upmax: up - 上一层, max - 最大值, 这是α-β的剪枝用到的东西, 后面再讲。

//depth: 搜索深度

int ate,cur,maxvalue,curvalue,xs,ys;

int count;

//#####################这一段是计算本结点的局面#########################################

ate=32;

//移动棋子:

xs=tmanposition[man].x;ys=tmanposition[man].y; //原坐标

if (SideOfMan[tmap[point.x][point.y]]==!tside) //目标点有对方的棋子

{

ate=tmap[point.x][point.y]; //记录下被吃掉的棋子

if(ate==0 || ate==16)

{

return 9999;

}

tmanposition[ate].x=0; //目标点的棋子被吃掉

}

tmap[point.x][point.y]=man; //这两行是:

tmap[xs][ys]=32; //在map上的移动

tmanposition[man]=point;

tside=!tside;

//####################################################################################

depth--;

if(depth>0) //不是底层结点

{

int chessman[125];

POINT targetpoint[125];

if(EnumList(tmap,tmanposition,tside,chessman,targetpoint,count)) //枚举出所有子结点(列出所有走法)

{

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//这里是剪枝(不是α-β剪枝), 原理是在正式搜索之前先用较浅的搜索来得到误差较大的值

//然后根据这些值来对子结点排序, 只保留最好的S_WIDTH个结点进行正式搜索。

//显然,这个剪枝有一定的风险

if(depth>=2 && count>S_WIDTH+2)

{

int value[125];

cur=0;

maxvalue=-10000;

while(cur< count)

{

curvalue=Search(tmap,tmanposition,tside,chessman[cur],targetpoint[cur],-10000,depth-2);

value[cur]=curvalue;

if(curvalue>maxvalue)maxvalue=curvalue;

cur ++;

}

::Mantis_QuickSort(value,chessman,targetpoint,0,count-1); //排序

count=S_WIDTH;//剪枝

}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

maxvalue=-10000;

cur=0;

while(cur< count)

{

curvalue=Search(tmap,tmanposition,tside,chessman[cur],targetpoint[cur],maxvalue,depth);

if(curvalue>maxvalue)maxvalue=curvalue;

if(curvalue>=-upmax)goto _ENDSUB; //α-β剪枝, 符合剪枝条件的就Cut掉。 这里用了goto语句了, 千万别学我。

cur ++;

}

}

else maxvalue=9800;

}

else //是底层结点

{

maxvalue=Value(tmap,tmanposition,tside); //估值

}

_ENDSUB:

//返回之前要恢复父结点的局面

//####################################################################################

tmanposition[man].x=xs; //这两行是:

tmanposition[man].y=ys; //在face上的恢复

tmap[xs][ys]=man; //在map上的恢复

if(ate!=32)

{

tmanposition[ate]=point;

tmap[point.x][point.y]=ate;

}

else tmap[point.x][point.y]=32;

tside=!tside;

//####################################################################################

return -maxvalue;

}

上面的代码用到了α-β剪枝, 举个例子就明白了:

还是这个博弈树,从上往下遍历。

1a( 1) ┳ 2a(-1) ┳ 3a(-1)

┃ ┗ 3b( 1)

┗ 2b(-5) ┯ 3c( 2)

├ 3d(-4)

└ 3e( 5)

2a遍历完后 upmax=-1, 继续遍历完3c后返回2b, 发现3c=2>-upmax, 这时就不用管3d和3e了, 因为无论他们的值是多少 2b=-max(3c,3d,3e)<2a 一定成立,

也就是说2b可以安全地剪掉。这就是α-β剪枝。

从上面的代码来看我的MantisChess算法与标准的α-β剪枝搜索并没有什么不同, 只不过加了排序和剪枝而已。

你可能感兴趣的:(c语言编程一个象棋游戏)