#C语言游戏2五子棋人机对战Word下载.docx
《#C语言游戏2五子棋人机对战Word下载.docx》由会员分享,可在线阅读,更多相关《#C语言游戏2五子棋人机对战Word下载.docx(28页珍藏版)》请在冰豆网上搜索。
BITMAPbm。
m_bmp.GetObject(sizeof(BITMAP>
&
bm>
//画图
if(IDB_BOARD==bitmap>
//画棋盘
pDC->
BitBlt(x,y,bm.bmWidth,bm.bmHeight,&
dc,0,0,SRCCOPY>
else
{//每个图片里有4X4个棋子,我只要画出一个就行了
intw=bm.bmWidth/4。
inth=bm.bmHeight/4。
TransparentBlt(pDC->
m_hDC,x,y,w,h,dc.m_hDC,0,0,w,h,RGB(255,255,255>
>
}
dc.SelectObject(pOldbmp>
//恢复画刷
}
pDC->
功能是贴图:
将dc中的位图,截取大小bm.bmWidth,bm.bmHeight,粘贴到pDC所指的设备,贴图坐标x,y。
最后一个参数为粘贴方式,SRCCOPY是直接粘贴。
TransparentBlt(pDC->
功能也是贴图,但图片背景透明:
将dc中的位图<
dc.m_hDC是dc的句柄),截取大小w,h,粘贴到pDC所指的设备,贴图坐标x,y,贴图大小为w,h,如果图片截取大小和粘贴大小不同,则拉伸或压缩图片。
最后一项是背景色,可以将图片背景透明化。
使用TransparentBlt必须包含头文件和类库,否则编译错误:
#include<
wingdi.h>
#pragmacomment(lib,"
msimg32.lib"
资源泄漏:
函数每执行一次,就要释放里面的资源,也就是析构。
m_bmp在析构时,因为已经被dc.SelectObject选用了,正在使用的资源是无法析构的。
这次执行无法析构,下次执行就要占用新的资源,资源越用越多就是资源泄露。
有的软件刚开始运行很正常,可运行时间一长系统越来越慢,最后甚至死机了,这就是资源泄露的危害。
保存和恢复画刷:
dc.SelectObject(&
将位图选到dc后,会返回一个画刷的句柄,让pOldbmp保存这个画刷。
注意,保存的画刷不是选中m_bmp之后的画刷,而是选中m_bmp之前的画刷。
dc.SelectObject(pOldbmp>
的意思是用初始画刷将m_bmp替换掉,m_bmp空闲出来也就能被顺利析构了。
函数弄好后就调用这个函数画图了。
先在OnInitDialog函数中加入以下代码,调整对话框大小,并隐藏按钮:
//TODO:
Addextrainitializationhere
MoveWindow(0,0,520,540>
//窗口定位
CenterWindow(>
//居中窗口
GetDlgItem(IDOK>
->
ShowWindow(SW_HIDE>
GetDlgItem(IDCANCEL>
或者直接在资源窗口中调整对话框:
然后在OnPaint函数中加入以下代码画图:
CDC*pDC=GetDC(>
//获取当前窗口句柄
Draw(13,13,IDB_BOARD,pDC>
//Draw(0,0,IDB_BLACK,pDC>
//画黑棋,只是为了查看显示效果
调整一下界面,希望你没有强迫症,可不要在调整上花太多时间了。
下面是我做出的效果:
三、下棋
现在要将棋子准确下到各个点上,我用的棋盘,间距为34,点击鼠标时获取点击坐标x,y,然后x/34,y/34,确定棋子下到了哪个点上。
////////////////////////OnLButtonUp函数///////////////////////////////////
添加消息处理函数:
我用的消息是WM_LBUTTONUP,也就是当鼠标左键抬起来的时候,函数响应:
以下是函数代码:
OnLButtonUp(UINTnFlags,CPointpoint>
Addyourmessagehandlercodehereand/orcalldefault
if(point.x>
0&
&
point.x<
480&
point.y>
point.y<
480>
{
intj=point.x/34。
inti=point.y/34。
intx=j*34。
inty=i*34。
CDC*pDC=GetDC(>
//获取当前设备句柄
Draw(x,y,IDB_BLACK,pDC>
CDialog:
OnLButtonUp(nFlags,point>
函数中,point.x,point.y为点击鼠标时的坐标
现在可以将棋子准确地下到点上了,可是就算点上有已经有棋子了,点鼠标后也会下棋。
所以我们用一个二维数组存储棋盘上的棋子。
添加成员变量:
初始化成员变量,在OnPaint函数里加入下面代码。
画棋盘的同时,初始化棋盘。
for(inti=0。
i<
15。
i++>
for(intj=0。
j<
j++>
chess[i][j]=0。
修改OnLButtonUp函数中的下棋代码原来是直接用Draw(x,y,IDB_BLACK,pDC>
画棋,现在改成下面的内容,当点上没有棋时才下棋:
if(0==chess[i][j]>
{
Draw(x,y,IDB_BLACK,pDC>
chess[i][j]=1。
//下的黑棋,这一点变成1
}
下棋功能完成了。
四、判断胜负
////////////////////////////iswin函数/////////////////////////////////////////
先添加一个BOOL类型的成员函数iswin(inti,intj>
,每下一个棋子都通过这个棋子判断是否赢了。
是则返回TRUE<
或1),否则返回FALSE(或0>
默认返回FALSE。
在OnLButtonUp函数中调用iswin。
如果赢了弹出窗口"
我赢了,结束战斗!
"
,初始化棋盘:
//判断输赢
if(iswin(i,j>
MessageBox("
"
提示"
MB_OK>
Invalidate(FALSE>
MessageBox弹出窗口"
Invalidate(FALSE>
会放出一个WM_PAINT消息,间接调用OnPaint函数,初始化棋盘。
/////////////////////////////search函数//////////////////////////////////////
那到底怎么判断输赢呢?
我采用的是下面这个方法:
申明变量alive1,alive2,用来判断活棋还是死棋。
申明变量count用来判断连子的个数。
以横向四子为例,我刚下了第三个棋子。
先从第三个棋子位置开始,往右边扫描,如果下的也是黑棋,则count++。
如果遇到的不是黑棋,判断是不是空地。
如果是空地alive1=1,表示右边一头是活的。
否则alive=0。
然后回到第三子位置,往左边扫描。
对于扫描,我这里添加一个成员函数search用来扫描
代码比较长,小心不要出错,调试很麻烦的。
intCMyDlg:
search(inti,intj,intm,intn>
inttempi,tempj,count=-1。
intalive1=0,alive2=0。
//第一次扫描
tempi=i。
tempj=j。
while(tempi>
0&
tempi<
15&
tempj>
tempj<
chess[tempi][tempj]==chess[i][j]>
tempi+=m。
tempj+=n。
count++。
if(chess[tempi][tempj]==0>
alive1=1。
//第二次扫描
tempi-=m。
tempj-=n。
alive2=1。
if(count>
=5>
return5。
elseif(alive1&
alive2>
returncount。
elseif((alive1||alive2>
&
count!
=1>
returncount+4。
return0。
函数的参数i,j是下棋的位置,m,n表示扫描的方向。
比如从左下角往右上角扫描,坐标上是x+1,y-1,对应的n=1,m=-1<
x对应的是j,n,y对应的x,m)。
m,n与方向的对应关系:
//m,n
//1,0从上到下
//0,1从左到右
//1,1左上到右下
//1,-1右上到左下
还有count的值,活2、3、4还有5都直接返回值,死的返回count+4
//返回8死4:
deadfour
//返回7死3:
deadthree
//返回6死2:
deadtwo
//返回5成5:
five
//返回4活4:
alivefour
//返回3活3:
alivethree
//返回2活2:
alivetwo
//返回0,
完成搜索函数search后,在iswin函数中调用,如果search返回的是5,赢了:
BOOLCMyDlg:
iswin(inti,intj>
if(search(i,j,0,1>
==5||search(i,j,1,0>
==5
||search(i,j,1,1>
==5||search(i,j,1,-1>
==5>
returnTRUE。
returnFALSE。
判断胜负功能完成
五、人工智能
/////////////////////////////AIplay函数//////////////////////////////////////
添加成员函数AIplay,电脑下棋函数
每次人下完后,电脑就下。
所以把AIplay放进OnLButtonUp函数的if(0==chess[i][j]>
下面:
if(0==chess[i][j]>
AIplay(>
此时的AIplay函数还是个空函数。
电脑下棋应该找到最有利的位置,不仅要找电脑有利的位置,还要找人有利的位置,然后比较谁更有利。
如果电脑有利,电脑进攻;
如果人有利,电脑防守。
为了寻找这个有利位置,添加成员函数searchvalue。
searchvalue(int&
best_i,int&
best_j,intcolor>
best_i,best_j是最有利的位置,注意这里用的是&
best_i,&
best_j,即“引用参数”,引用参数可以在函数中改变参数数据,普通参数不行。
color是棋子的颜色,1为黑,-1为白。
在AIplay函数中调用searchvalue函数:
AIplay(>
//白棋和黑棋的分数
intwhite_value,black_value。
//白棋和黑棋的有利位置
intwi,wj,bi,bj。
//得到分数和有利位置
white_value=searchvalue(wi,wj,-1>
black_value=searchvalue(bi,bj,1>
//准备画棋
CDC*pDC=GetDC(>
intx,y。
//如果黑棋更有利,电脑防守
if(white_value<
black_value>
x=bj*34。
y=bi*34。
Draw(x,y,IDB_WHITE,pDC>
chess[bi][bj]=-1。
if(iswin(bi,bj>
电脑胜利,结束战斗!
//如果白棋更有利,电脑进攻
x=wj*34。
y=wi*34。
chess[wi][wj]=-1。
if(iswin(wi,wj>
为了测试,我们先为这个researchvalue函数设置一些值:
//白棋,有利位置设为(1,1>
,返回值设为0。
if(-1==color>
best_i=1。
best_j=1。
return0。
//黑棋,有利位置设为(2,2>
,返回值设为0.
if(1==color>
best_i=2。
best_j=2。
return1。
因为黑棋的分数高,所以白棋防守,应该下到(2,2>
位置。
/////////////////////////////getscore函数/////////////////////////////////////
为了得到黑棋和白棋的分数,添加成员函数getscore:
这个函数的代码非常多,我分开讲。
首先是声明变量,这个是根据实际情况变的:
//状态
intdeadfour=0,deadthree=0,deadtwo=0。
intfive=0,alivefour=0,alivethree=0,alivetwo=0。
intstatus[4],score。
然后,在这个位置下个棋,用search函数判断这个棋子各个方向的状态。
判断结束后记得把chess[i][j]变回0。
chess[i][j]=color。
//从左到右
status[0]=search(i,j,0,1>
//从上到下
status[1]=search(i,j,1,0>
//从左上到右下
status[2]=search(i,j,1,1>
//从左下到右上
status[3]=search(i,j,1,-1>
chess[i][j]=0
统计各种情况的数目<
活2、3、4,死2、3、4,成5)
for(intn=0。
n<
4。
n++>
switch(status[n]>
case8:
deadfour++。
break。
case7:
deadthree++。
case6:
deadtwo++。
case5:
five=1。
case4:
alivefour=1。
case3:
alivethree++。
case2:
alivetwo++。
给这个位置打分,并在最后记得返回分数score。
//成5
if(five>
score=100000。
//活4
elseif(alivefour>
score=10000。
//双死4
elseif(deadfour>
=2>
//死4活3
elseif(deadfour&
alivethree>
score=10000。
//双活3
elseif(alivethree>
score=5000。
//活3双活2
elseif(alivethree&
alivetwo>
score=5000。
//活3死3
deadthree>
score=1000。
//单死4
elseif(1==deadfour>
score=500。
//单活3
elseif(1==alivethree>
score=200。
//双活2
elseif(alivetwo>
score=100。
//双死3
elseif(deadthree>
score=50。
//单活2
elseif(1==alivetwo>
score=10。
//单死3
elseif(1==deadthree>
score=5。
returnscore。
getscore函数完成
/////////////////////////////searchvalue函数//////////////////////////////////
然后修改searchvalue函数:
intCMy1Dlg:
intmaxvalue=0,value。
for(inti=1。
14。
for(intj=1。
if(color==chess[i][j]>
for(intm=i-1。
m<
=i+1。
m++>
for(intn=j-1。
=j+1。
{
if(0==chess[m][n]>
{
value=getscore(m,n,color>
if(maxvalue<
value>
{
maxvalue=value。
best_i=m。
best_j=n。
}
}
}//结束for
}//结束for
returnmaxvalue。
现在解释一下这最后完成的函数。
先搜索黑棋最佳位置,整个棋盘从左往右从上到下扫描,当遇到黑棋时,分析黑棋周围8个位置是否为空,如果是空则判断这个空位的分数。
找到分数最高的位置,并返回这个分数。
五子棋人机对战完成
六、附加功能
存档、读档功能
我们可以把对话框上的两个按钮改造成存档按钮和读档按钮,注意修改ID。
在OnInitDialog函数中,重新调整对话框大小,和按钮位置
//TODO:
MoveWindow(0,0,520,560>
GetDlgItem(IDC_SAVE>
MoveWindow(10,500,50,20>
GetDlgItem(IDC_OPEN>
MoveWindow(70,500,50,20>
查看->
建立类向导
给两个按钮添加关联函数
////////////////