五子棋设计方案Word格式.docx
《五子棋设计方案Word格式.docx》由会员分享,可在线阅读,更多相关《五子棋设计方案Word格式.docx(35页珍藏版)》请在冰豆网上搜索。
第三章五子棋人机对战程序设计6
3-1主菜单设置6
3-2关键模块介绍7
第四章测试运行情况22
第五章总结23
参考文献24
第一章概述
1-1系统要求简述
本系统主要为游戏者提供了娱乐,有益于为人们的日常生活带来快乐,为人们适当的减压,另外还可以提高游戏者的反应能力,和动手能力。
1-2应用语言概述
自从计算机问世以来,程序设计有了很大的进展。
进入20世纪80年代,出现了一种新的程序设计方法——面向对象的程序设计(ObjectOrientedProgamming),简称OOP。
面向对象的程序设计的特性,为程序设计人员提供了分析和解决问题的一种全新的方法。
C++语言是70年代出现的一种非常卓越的程度设计语言,是由贝尔实验室的DennisRitchie设计的一个通用的面向对象的程序设计语言。
C++语言具有丰富的数据类型和结构化设计所需要的语句。
C++语言的特点是效率高、功能强、简洁灵活。
C++语言所具有的地址操作和位操作,是它在一定程度上具有了低级语言的特性,因而可以在相当大的程度上,代替设计难度较大的汇编语言。
目前国际上广泛流行的计算机高级语言,既可用来写系统软件,也可用来写应用软件。
1-3开发工具
本项目所用的开发工具如表1-1所示
表1-1开发工具表
开发语言
C++
开发环境
Windowsxppro+MicrosoftVisualStudio2006
开发工具
MicrosoftVisualStudio2006
第二章功能与设计思想
2-1人机对战程序功能
五子棋是一个经典的小游戏,游戏者打开游戏后将会看到一张棋盘,这时游戏者可选择人对战或者人机对战,本程序主要功能来实现人机对战,游戏者通过鼠标的移动来移动棋子,通过单击鼠标来确定棋子的最终位置,先将棋子排成五子一线者为最终获胜者。
游戏结束
2-2人机对战设计思想
图2-1游戏的实现
先看上图,玩过五子棋的人一眼就能看出这才是一场真正的战斗!
(至少它不允许你随便下棋。
)
那么,我们该如何实现?
其实,只是在原来的基础上,把其中一个人用计算机代替而已。
也就是说,我们只要在原来的基础上,加上计算机的下棋算法就好了。
而计算机下棋的算法,正是一个程序的重点和难点。
这里先介绍一下:
计算机是没有头脑的,但是,计算机必须下棋。
那么,它该怎么办呢?
我们必须自己编程序,“教”计算机下棋。
然而,我们是否只是让计算机随便在棋盘上面放一颗棋子呢?
事情并不是这么简单。
计算机要下的那个位置,必定是它认为最好的!
当然,这里的最好是程序员给予计算机的,是计算机算法的体现。
最好的位置,人都难以保证!
但是,计算机可以。
这正是计算机能够在我们这个社会迅速发展的一个重要原因。
当然,计算机的可以是建立在人的程序的基础上的。
计算机的会是依赖于它的速度,人所不能及的速度。
上面说过,为什么要把数组的值赋值为1和-1,而不是0,1或者其它的数字呢?
这里就涉及到数字的妙用。
前面说了,五个连续棋子的值相加,如果绝对值是五,则不是五个棋子同色,应该结束游戏。
那么,如果绝对值不是五呢?
它就没有意义了吗?
不是!
不但不是,而是它有着更重要的意义。
它关系到我们这个程序的几乎全部的算法。
下面细说(数字是表示绝对值的):
4:
表示五个棋子中有一个空位置和四个同色的棋子。
3:
表示五个棋子中有两个空位置和三个同色的棋子;
也表示五个棋子中有四个同色棋子和一个异色棋子。
注意:
此时没有空位置,可以不考虑。
2:
表示五个棋子中有三个空位置和两个同色棋子;
也表示五个棋子中一个空位置和三个同色棋子和一个异色棋子;
1、0:
由于出现1和0的机会太少(除了开始的时候),我们不必多加考虑。
但是,因为刚才对方下棋的对方必定有一定的危险,我们只需要在刚刚下棋的附近找一个空位置下棋就可以了。
第三章五子棋人机对战程序设计
3-1主菜单设置
对菜单进行设置,游戏中分别添加开始,打开,退出和保存,其中开始中又包括人对人和人对机两项,并且对他们分别关联和做相应的消息映射。
图3-1游戏菜单
从上面图中,我们可以看到,工具栏变了。
因此我们也可以想到菜单也应该变了。
它们的修改如下:
修改菜单:
删除菜单项开始:
ID_START
添加菜单项人对人游戏:
ID_PLAYER
人对机游戏:
ID_CPMPUTER
修改工具栏:
删除原来的按扭对应ID:
添加两个新按扭对应ID:
ID_PLAYERID_CPMPUTER
3-2关键模块介绍
3-2-1
变量和函数
在view类中添加变量函数如下:
//保存vscomputer时白棋位置
CPointvspoint;
//是人与人游戏?
是人与机游戏?
intvscomputer;
CPointbpointcan4,//这个位置空,它旁边有四个黑棋
wpointcan4,//这个位置空,它旁边有四个白棋
bpointcan3,//这个位置空,它的旁边有三个黑棋
wpointcan3,//这个位置空,它的旁边有三个白棋
bpointcan2,//这个位置空,它的旁边有两个黑棋
wpointcan2,//这个位置空,它的旁边有两个白棋
bpointcan1;
//不是以上情况,这个位置空
//在得到最大值和方向上寻找落棋点(具体见后面介绍)
//其中i、j表示搜索起点,n表示方向
voidsearchcandown1(inti,intj,intn);
voidsearchcandown2(inti,intj,intn);
voidsearchcandown3(inti,intj,intn);
voidsearchcandown4(inti,intj,intn);
//计算最大值及方向
CPointmaxnum(inta,intb,intc,intd);
//最好落棋点
voidbestputdown(inti,intj);
//计算机下棋
voidcomputerdown();
//在位置point放下棋子
voidputdown(CPointpoint);
//人对人菜单
afx_msgvoidOnPlayer();
//人对机菜单
afx_msgvoidOnCpmputer();
3-2-2菜单函数
添加了菜单项,我们必须添加一个变量vscomputer,赋值为1,并约定:
//vscomputer:
2表示人对人,1表示人对机
voidCMyView:
:
OnPlayer()
{
//TODO:
Addyourcommandhandlercodehere
vscomputer=2;
OnStart();
}
OnCpmputer()
vscomputer=1;
其中,我们只是添加一个变量,而仍然利用原来的开始函数。
虽然我们的菜单项已经删除了,但它的函数还在,我们应该加以利用。
3-2-3人变成计算机
下面,我们就必须把游戏双方中的一方改为计算机。
我们把黑棋改为计算机,因为一般情况计算机比人强,应让人先下。
当然,要是人赢了,就必须让计算机先下了,在将在以后的算法中体现。
由于下棋只是在OnLButtonUp(UINTnFlags,CPointpoint)函数中,我们把它改为如下:
OnLButtonUp(UINTnFlags,CPointpoint)
Addyourmessagehandlercodehereand/orcalldefault
CDC*pDC=GetDC();
CDCDc;
if(Dc.CreateCompatibleDC(pDC)==FALSE)
AfxMessageBox("
Can'
tcreateDC"
);
//显示棋子
/////////////////////人对机
if(vscomputer==1)
{
if(point.x>
30&
&
point.x<
410&
point.y>
point.y<
410)
{
intpx=(point.x-30)/20;
intpy=(point.y-30)/20;
if(colorwhite&
wzq[px][py]==0)
{
Dc.SelectObject(m_bmwhite);
pDC->
BitBlt(px*20+32,py*20+32,160,160,&
Dc,0,0,SRCCOPY);
wzq[px][py]=1;
over(point);
colorwhite=false;
//保存白棋位置
vspoint=point;
//计算机下棋
computerdown();
}
}
}
//人对人
if(vscomputer==2)
{
elseif(wzq[px][py]==0)
{
Dc.SelectObject(m_bmblack);
wzq[px][py]=-1;
colorwhite=true;
}
CView:
OnLButtonUp(nFlags,point);
由上面可知,我们对人对机游戏的方法是采用:
人下完了之后,检查是否胜利,是则结束游戏,重新开始,并改为黑棋(即计算机)先下;
如果人没有胜利,也改为计算机下。
而对于计算机怎么下棋,我们只是用了一个函数computerdown()表示。
但是现在我们并不能运行程序,因为有一个没有定义的函数,怎么办呢?
仔细看程序的话,你还会发现一个变量Cpointvspoint,这是后来添加的它的用处是保存刚才白棋下的位置,有利于黑棋下棋时的定位。
接着,我们的主要问题就是实现computerdown()函数,让计算机能够自动下棋!
3-2-4计算机下棋
计算机是怎样下棋?
这就是定位的问题了。
即搜索棋盘,找出一个最佳点,放下黑棋。
我们实现的方法是:
全盘搜索,并把搜索到的位置,保存在变量。
由于有多种情况,我们定义变量如下:
CPointbpointcan4,//这个位置空,它旁边有四个黑棋
wpointcan4,//这个位置空,它旁边有四个白棋
bpointcan3,//这个位置空,它的旁边有三个黑棋
wpointcan3,//这个位置空,它的旁边有三个白棋
bpointcan2,//这个位置空,它的旁边有两个黑棋
wpointcan2,//这个位置空,它的旁边有两个白棋
//不是以上情况,这个位置空
并在搜索之前都赋值为(-1,-1),然后,进行搜索,并把相应的值保存在相应变量里面,而如果前面已经对变量赋值,我们依然赋值,用新值代替旧值。
我们只保存最后一个值,这样的一个好处是,避免了每次都从左上角开始,并且它的随机性比随机函数还随机。
全盘搜索完之后,由于上面的变量中至少有一个已经被赋值,即不是(-1,-1),我们可以采用多数优先的方法,让已经有多个同色棋子的位置先下棋。
其原理是,如果已经有四个黑棋,计算机再下一个黑棋就赢了;
否则,如果人已经有四个白棋,那么计算机就必须放下一个黑棋,阻止白棋下一步赢;
如果已经有三个黑棋,再下一个黑棋,变成四个;
否则,如果已经有三个白棋,下一个黑棋,破坏它;
两个棋子的同理;
否则,在刚才白棋下的地方,顺便找一个位置,下棋。
computerdown()函数如下:
//轮到计算机下棋
computerdown()
//把各种情形赋值为如下
bpointcan4=(-1,-1);
wpointcan4=(-1,-1);
bpointcan3=(-1,-1);
wpointcan3=(-1,-1);
bpointcan2=(-1,-1);
wpointcan2=(-1,-1);
bpointcan1=(-1,-1);
//搜索最好的落棋点
for(inti=0;
i<
19;
i++)
for(intj=0;
j<
j++)
bestputdown(i,j);
//判断放在哪里
//棋多的位置优先
//黑白一样多时黑先
//不是-1就表示已经被赋值!
if(bpointcan4.x!
=-1)
putdown(bpointcan4);
return;
elseif(wpointcan4.x!
putdown(wpointcan4);
elseif(bpointcan3.x!
putdown(bpointcan3);
elseif(wpointcan3.x!
putdown(wpointcan3);
elseif(bpointcan2.x!
putdown(bpointcan2);
elseif(wpointcan2.x!
putdown(wpointcan2);
else
putdown(bpointcan1);
上面又有两个新函数,分别定义为空函数,如下:
//搜索最佳位置
voidbestputdown(inti,intj);
//放下黑棋
现在,我们就必须对上面两个空函数进行定义了。
3-2-5在指定位置下棋
由于putdown(CPointpoint)函数的原理非常简单,我们先说明如下:
//黑棋下
putdown(CPointpoint)
Dc.SelectObject(m_bmblack);
pDC->
BitBlt(point.x*20+32,point.y*20+32,160,160,&
wzq[point.x][point.y]=-1;
//由于原来我们检查是否结束时用的是鼠标点下的坐标,而现在
//putdown(CPointpoint)函数用的是数组棋盘的坐标,所以必须转换
CPointoverpoint;
overpoint.x=point.x*20+30;
overpoint.y=point.y*20+30;
over(overpoint);
colorwhite=true;
3-2-6搜索最佳落棋点
现在就剩下voidbestputdown(inti,intj)函数没有定义了(虽然前面的变量函数已经说明了,当时我们这里是用程序扩展的思路进行的,故如此说明)。
它的实现原理是:
在四个方向上,各自计算那个方向上棋子的状态,我们的思路是利用原来定义的白棋为1,黑棋为-1,的思想,让同个方向上的五个棋子的值相加,取绝对值并赋值给为这个方向定义的局部变量num[i]。
为什么要用五个棋子的值相加呢?
因为,如果几个棋子是同色的,无论黑白,它的绝对值必然大,而对于几个棋子中有黑棋和白棋的,其值必然相加而抵消变小。
所以我们可以利用这种方法来寻找旁边有多个同色棋子的空位置(前面已经具体说明)。
在每一个棋盘位置,计算以它为起点的四个方向(横、竖、撇、捺),再比较这四个方向中哪个值最大,然后在这个方向上寻找落棋点。
我们添加函数如下:
//检查四个方向,各算出五个棋子的和并赋值
bestputdown(inti,intj)
//四个方向的值
intnum[4];
inta,k;
///////////////////////////////num[0]-->
a=0;
if(i<
15)
for(k=0;
k<
5;
k++)
a=a+wzq[i+k][j];
num[0]=abs(a);
//////////////////////////////num[1]"
|"
if(j<
a=a+wzq[i][j+k];
num[1]=abs(a);
///////////////////////////////num[2]"
\"
if(i<
15&
a=a+wzq[i+k][j+k];
num[2]=abs(a);
//////////////////////////////num[3]"
/"
if((i>
4)&
(j<
15))
a=a+wzq[i-k][j+k];
num[3]=abs(a);
//比较哪个方向同色棋最多
//由于我们搜索落棋点时用到最大值和方向,我们可以定义一个Cpoint类变量,//让它返回两个值。
可以说,这也是一种巧妙的想法,因为这样你就不用去写//内联函数了
CPointnumbig;
//numbig.x表示方向
//numbig.y表示最大值
numbig=maxnum(num[0],num[1],num[2],num[3]);
//在得到最大值和方向上寻找落棋点
switch(numbig.y)
case4:
searchcandown4(i,j,numbig.x);
break;
case3:
searchcandown3(i,j,numbig.x);
case2:
searchcandown2(i,j,numbig.x);
default:
searchcandown1(i,j,numbig.x);
同样的方法,我们必须为上面还没有定义的函数添加空函数。
//其中i、j表示搜索起点,n表示方向
3-2-7最大值函数的实现
现在先介绍CPointmaxnum(inta,intb,intc,intd)函数,它只是四个整数的比较:
CPointCMyView:
maxnum(inta,intb,intc,intd)
//point.x为方向值
//point.y为最大值
CPointpoint;
if(a>
=b)
point.x=0;
point.y=a;
else
point.x=1;
point.y=b;
if(c>
point.y)
point.x=2;
point.y=c;
if(d>
point.x=3;
point.y=d;
r