




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
五子棋人机对战,AI很低,做参考用,仅仅为大家提供一下思路。开发环境:VisualC++6.0游戏界面:C语言游戏2-五子棋(人机对战)TOC\o"1-2"\h\z\uHYPERLINK一、开始工作 PAGEREF_Toc339626683\h2HYPERLINK二、画图 PAGEREF_Toc339626684\h5HYPERLINK三、下棋 PAGEREF_Toc339626685\h8HYPERLINK四、判断胜负 PAGEREF_Toc339626686\h10HYPERLINK五、人工智能 PAGEREF_Toc339626687\h13HYPERLINK六、附加功能 PAGEREF_Toc339626688\h17一、开始工作新建工程,选MFCAppWizard(exe),添上工程名,确定。选基于对话框,完成,确定。插入位图网上的源码一般都是将棋盘和棋子用画图程序画出来,但我不会弄。我的方法是直接贴图。先插入位图(BMP格式),以下是我用的位图,当然你也可以用自己的位图:插入位图流程:有时会弹出下面这个窗口,这是完全没有问题的:位图插入后会自动赋予ID值,我们可以修改一下:二、画图///////////////////////////Draw函数/////////////////////////////////////////添加成员函数Draw:Draw(intx,inty,UINTbitmap,CDC*pDC)解释一下:x,y是画图的坐标bitmap是图片ID,比如我的黑棋图片ID就是IDB_BLACKpDC是显示图片窗口的句柄我的画图函数是下面这样的,其中要注意两个函数BitBlt和TransparentBlt,程序后有解释:voidCMyDlg::Draw(intx,inty,UINTbitmap,CDC*pDC){ //装载图片 CBitmapm_bmp; m_bmp.LoadBitmap(bitmap); //创建画布,比如要在窗口显示,则pDC为窗口句柄 CDCdc; dc.CreateCompatibleDC(pDC); //将位图选到dc中,顺便保存画刷到pOldbmp //保存画刷、恢复画刷为规范操作,但可以不用 CBitmap*pOldbmp=dc.SelectObject(&m_bmp); //创建bm,用来获取图片信息,这里是为了获取图片尺寸 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->BitBlt(x,y,bm.bmWidth,bm.bmHeight,&dc,0,0,SRCCOPY);功能是贴图:将dc中的位图,截取大小bm.bmWidth,bm.bmHeight,粘贴到pDC所指的设备,贴图坐标x,y。最后一个参数为粘贴方式,我们是直接粘贴,所以是SRCCOPYTransparentBlt(pDC->m_hDC,x,y,w,h,dc.m_hDC,0,0,w,h,RGB(255,255,255));功能也是贴图,但图片背景透明:将dc中的位图(dc.m_hDC是dc的句柄),截取大小w,h,粘贴到pDC所指的设备,贴图坐标x,y,贴图大小为w,h,如果图片大小不符则拉伸或压缩图片。最后一项是背景色,可以将图片背景透明化。使用TransparentBlt必须包含头文件和类库,否则编译错误:#include<wingdi.h>#pragmacomment(lib,"msimg32.lib")函数弄好后就调用这个函数画图了。先在OnInitDialog函数中加入以下代码,调整对话框大小,并隐藏按钮: //TODO:Addextrainitializationhere MoveWindow(0,0,520,540);//窗口定位 CenterWindow(); //居中窗口 GetDlgItem(IDOK)->ShowWindow(SW_HIDE); GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE);或者直接在资源窗口中调整对话框:然后在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,也就是当鼠标左键抬起来的时候,函数响应:以下是函数代码:voidCMyDlg::OnLButtonUp(UINTnFlags,CPointpoint){ //TODO:Addyourmessagehandlercodehereand/orcalldefault if(point.x>0&&point.x<480&&point.y>0&&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<15;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。如果赢了弹出窗口"我赢了,结束战斗!",初始化棋盘:voidCMyDlg::OnLButtonUp(UINTnFlags,CPointpoint){ //TODO:Addyourmessagehandlercodehereand/orcalldefault if(point.x>0&&point.x<480&&point.y>0&&point.y<480) { intj=point.x/34; inti=point.y/34; intx=j*34; inty=i*34; CDC*pDC=GetDC();//获取当前设备句柄 if(0==chess[i][j]) { Draw(x,y,IDB_BLACK,pDC); chess[i][j]=1; } //判断输赢 if(iswin(i,j)) { MessageBox("我赢了,结束战斗!","提示",MB_OK); Invalidate(FALSE); } } CDialog::OnLButtonUp(nFlags,point);}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>0&&tempj<15&& chess[tempi][tempj]==chess[i][j]) { tempi+=m;tempj+=n; count++; } if(chess[tempi][tempj]==0)alive1=1; //第二次扫描 tempi=i;tempj=j; while(tempi>0&&tempi<15&& tempj>0&&tempj<15&& chess[tempi][tempj]==chess[i][j]) { tempi-=m;tempj-=n; count++; } if(chess[tempi][tempj]==0)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]) { Draw(x,y,IDB_BLACK,pDC); chess[i][j]=1; AIplay(); }此时的AIplay函数还是个空函数。电脑下棋应该找到最有利的位置,不仅要找电脑有利的位置,还要找人有利的位置,然后比较谁更有利。如果电脑有利,电脑进攻;如果人有利,电脑防守。为了寻找这个有利位置,添加成员函数searchvalue。searchvalue(int&best_i,int&best_j,intcolor)best_i,best_j是最有利的位置,注意这里用的是&best_i,&best_j,即“引用参数”,引用参数可以在函数中改变参数数据,普通参数不行。color是棋子的颜色,1为黑,-1为白。在AIplay函数中调用searchvalue函数:voidCMyDlg::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)) { MessageBox("电脑胜利,结束战斗!","提示",MB_OK); Invalidate(FALSE); } } //如果白棋更有利,电脑进攻 else { x=wj*34;y=wi*34; Draw(x,y,IDB_WHITE,pDC); chess[wi][wj]=-1; if(iswin(wi,wj)) { MessageBox("电脑胜利,结束战斗!","提示",MB_OK); Invalidate(FALSE); } }}为了测试,我们先为这个researchvalue函数设置一些值:intCMyDlg::searchvalue(int&best_i,int&best_j,intcolor){ //白棋,有利位置设为(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:这个函数的代码非常多,我分开讲。首先是声明变量,这个是根据实际情况变的://状态 //返回8死4:deadfour //返回7死3:deadthree //返回6死2:deadtwo //返回5成5:five //返回4活4:alivefour //返回3活3:alivethree //返回2活2:alivetwo //返回0, 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++;break; case6: deadtwo++;break; case5: five=1;break; case4: alivefour=1;break; case3: alivethree++;break; case2: alivetwo++;break; } }给这个位置打分,并在最后记得返回分数score。//成5 if(five)score=100000; //活4 elseif(alivefour)score=10000; //双死4 elseif(deadfour>=2)score=10000; //死4活3 elseif(deadfour&&alivethree) score=10000; //双活3 elseif(alivethree>=2)score=5000; //活3双活2 elseif(alivethree&&alivetwo>=2) score=5000; //活3死3 elseif(alivethree&&deadthree) score=1000; //单死4 elseif(1==deadfour)score=500; //单活3 elseif(1==alivethree)score=200; //双活2 elseif(alivetwo>=2)score=100; //双死3 elseif(deadthree>=2)score=50; //单活2 elseif(1==alivetwo)score=10; //单死3 elseif(1==deadthree)score=5; returnscore;getscore函数完成/////////////////////////////searchvalue函数//////////////////////////////////然后修改searchvalue函数:intCMy1Dlg::searchvalue(int&best_i,int&best_j,intcolor){ intmaxvalue=0,value; for(inti=1;i<14;i++) for(intj=1;j<14;j++) { if(color==chess[i][j]) { for(intm=i-1;m<=i+1;m++) for(intn=j-1;n<=j+1;n++) { 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:Addextrainitializationhere MoveWindow(0,0,520,560);//窗口定位 CenterWindow(); //居中窗口 GetDlgItem(IDC_SAVE)->MoveWindow(10,500,50,20); GetDlgItem(IDC_OPEN)->MoveWindow(70,500,50,20);查看->建立类向导给两个按钮添加关联函数/////////////////////////////OnSave函数//////////////////////////////////存档函数:voidCMyDlg::OnSave(){ //TODO:Addyourcontrolnotificationhandlercodehere //设置保存的文件,后缀名为.wzq CFileDialogdlg(FALSE,"wzq", NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, "(*.WZQ)|*.wzq|AllFiles|*.*||",this); //如果公共类对话框为确定 if(IDOK==dlg.DoModal()) dlg.GetFileName(); //否则退出 elsereturn; //字符串变量 CStringstr; CStdioFilefile; //如果有问题,退出 if(file.Open(dlg.GetFileName(), CFile::modeCreate|CFile::modeWrite|CFile::typeText)==0) { AfxMessageBox("saveerror!"); return; } //把数组值写进文件 for(inti=0;i<15;i++){ for(intj=0;j<15;j++){ if(-1==chess[i][j]) file.WriteString("-1"); if(0==chess[i][j]) file.WriteString("00"); if(1==
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 农业职业经理人考试的心得与体验分享试题及答案
- 分析游戏面试题目及答案
- 构建2024年园艺师考试的自由学习机制试题及答案
- 2024年农业职业经理人考试考点预测试题及答案
- 柏景设计面试题及答案
- 各高校招聘导师辅导员模拟考试试题及答案
- 复习园艺师考试的高效方法试题及答案
- 提升园艺师考试通过率的技巧试题及答案
- 农业职业经理人考试文化素养提升试题及答案
- 高校辅导员在人才培养中的作用及试题及答案
- 工会经费收支管理培训
- 人教版七年级地理(下)全册复习教案(含教学反思)
- JJF 1603-2016(0.1~2.5)THz太赫兹光谱仪校准规范
- 医药卫生病原微生物检测技术知识与技能比武竞赛题库
- 《民法典》-第二编 物权编-案例分析,解读-3
- 膜片钳常见问题汇总(人人都会膜片钳)
- 讲故事技能培训
- 海岸动力学全册配套完整课件
- 工作面防飞矸封闭式管理规定
- 干部人事档案管理岗位培训的讲义课件
- 财务人员廉政谈话记录 财务个人谈话记录3篇
评论
0/150
提交评论