基于MFC的五子棋应用(四)实践
来源:互联网 发布:淘宝双11怎么修改价格 编辑:程序博客网 时间:2024/05/20 19:33
基于MFC的五子棋应用(四)实践
在第一部分已经提前在构造函数中写好了相应的内容,但是人有需要添加的内容,所以我们在(一)的基础上添加:
//AI //保存计算机落子时白棋位置 CPoint vspoint; int vscomputer; //vscomputer=1 为人机对战 2为人人 //用作判断落子位置 CPoint bpointcan4,//这个位置空,它旁边有四个黑棋 wpointcan4,//这个位置空,它旁边有四个白棋 bpointcan3,//这个位置空,它的旁边有三个黑棋 wpointcan3,//这个位置空,它的旁边有三个白棋 bpointcan2,//这个位置空,它的旁边有两个黑棋 wpointcan2,//这个位置空,它的旁边有两个白棋 bpointcan1;//不是以上情况,这个位置空 //在得到最大值和方向上寻找落棋点 //其中i、j表示搜索起点,n表示方向 void searchcandown1(int i,int j,int n); void searchcandown2(int i,int j,int n); void searchcandown3(int i,int j,int n); void searchcandown4(int i,int j,int n); //计算最大值及方向 CPoint maxnum(int a,int b,int c,int d); //最好落棋点 void bestputdown(int i,int j); //计算机下棋 void computerdown(); //在位置point放下棋子 void putdown(CPoint point);
由于我们添加了菜单项 VS 人 和 VS AI需要在WuZiQiView.cpp中添加函数
//VS 人void CWuZiQiView::OnPlayer(){ vscomputer=2; OnStart();}//VS 机器void CWuZiQiView::OnComputer(){ vscomputer=1; OnStart();}
接下来是将其中一方变为计算机落子,即在OnButtonUp()函数中添加语句:
//人机对战 if(vscomputer==1) { if(point.x>30&&point.x<=410&&point.y>30&&point.y<410) { int px=(point.x-30)/20; int py=(point.y-30)/20; //是否已经有棋子 if(colorwhite&&wzq[px][py]==0) { //记录当前棋子坐标 wprex=px; wprey=py; 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(); } } }
计算机是怎样下棋?这就是定位的问题了。即搜索棋盘,找出一个最佳点,放下黑棋。我们实现的方法是:全盘搜索,并把搜索到的位置,保存在变量。由于有多种情况,我们定义变量如下: CPoint bpointcan4, //这个位置空,它旁边有四个黑棋 wpointcan4, //这个位置空,它旁边有四个白棋 bpointcan3, //这个位置空,它的旁边有三个黑棋 wpointcan3, //这个位置空,它的旁边有三个白棋 bpointcan2, //这个位置空,它的旁边有两个黑棋 wpointcan2, //这个位置空,它的旁边有两个白棋 bpointcan1; //不是以上情况,这个位置空 在搜索前,先将所有值赋为(-1,-1),然后进行搜索,并保存对应情况下的变量值,如果操作时已经对变量进行过赋值,那么就用新值代替旧值。为了避免每次搜索棋盘都从左上角开始,我们只保存最后的一个值。 全盘搜索完之后,由于上面的变量中至少有一个已经被赋值,即不是(-1,-1),我们可以采用多数优先的方法,让已经有多个同色棋子的位置先下棋。其原理是,如果已经有四个黑棋,计算机再下一个黑棋就赢了;否则,如果人已经有四个白棋,那么计算机就必须放下一个黑棋,阻止白棋下一步赢;如果已经有三个黑棋,再下一个黑棋,变成四个;否则,如果已经有三个白棋,下一个黑棋,破坏它;两个棋子的同理;否则,在刚才白棋下的地方,找一个位置,落子。 computer()如下:
//计算机下棋//原则:搜索棋盘,找出一个最佳点,落子void CWuZiQiView::computerdown(){ //把各种情形赋值为如下 bpointcan4=(-1,-1); wpointcan4=(-1,-1); bpointcan3=(-1,-1); wpointcan3=(-1,-1); bpointcan2=(-1,-1); wpointcan2=(-1,-1); bpointcan1=(-1,-1); //搜索最好的落棋点 for(int i=0;i<19;i++) for(int j=0;j<19;j++) bestputdown(i,j); //判断放在哪里 //棋多的位置优先 //黑白一样多时黑先 if(bpointcan4.x!=-1) { putdown(bpointcan4); return; } else if(wpointcan4.x!=-1) { putdown(wpointcan4); return; } else if(bpointcan3.x!=-1) { putdown(bpointcan3); return; } else if(wpointcan3.x!=-1) { putdown(wpointcan3); return; } else if(bpointcan2.x!=-1) { putdown(bpointcan2); return; } else if(wpointcan2.x!=-1) { putdown(wpointcan2); return; } else { putdown(bpointcan1); return; }}
上面用到了putdown()函数即计算机落子,原理很简单直接贴代码:
//计算机落黑子void CWuZiQiView::putdown(CPoint point){ CDC *pDC=GetDC(); CDC Dc; if(Dc.CreateCompatibleDC(pDC)==FALSE) AfxMessageBox("Can't create DC"); Dc.SelectObject(m_bmblack); pDC->BitBlt(point.x*20+32,point.y*20+32,160,160,&Dc,0,0,SRCCOPY); wzq[point.x][point.y]=-1; //悔棋坐标 bprex=point.x; bprey=point.y; //变数组坐标为棋盘坐标 CPoint overpoint; overpoint.x=point.x*20+30; overpoint.y=point.y*20+30; over(overpoint); colorwhite=true;}
接下去就是寻找最佳落子位置即bestputdown()函数,它的实现原理是:在四个方向上,各自计算那个方向上棋子的状态,让同个方向上的五个棋子的值相加,取绝对值并赋值给为这个方向定义的局部变量num[i]。 寻找多子同色区域(前面已经具体说明)。 在每一个棋盘位置,计算以它为起点的四个方向(-、|、\、/),比较这四个方向中哪个值最大,然后在这个方向上寻找落棋点。
//检查四个方向,各算出五个棋子的和并赋值/*在四个方向上,各自计算那个方向上棋子的状态,我们的思路是利用原来定义的白棋为1,黑棋为-1的思想让同个方向上的五个棋子的值相加,取绝对值并赋值给为这个方向定义的局部变量num[i]。如果几个棋子是同色的,无论黑白,它的绝对值必然大,而对于几个棋子中有黑棋和白棋的,其值必然相加而抵消变小。所以我们可以利用这种方法来寻找旁边有多个同色棋子的空位置*/void CWuZiQiView::bestputdown(int i,int j){ int num[4]; CPoint numbig; int a,k; /////////////////////////////// num[0] "-" a=0; if(i>=0&&i<15) for(k=0;k<5;k++) a=a+wzq[i+k][j]; num[0]=abs(a); ////////////////////////////// num[1] "|" a=0; if(j>=0&&i<15) for(k=0;k<5;k++) a=a+wzq[i][j+k]; num[1]=abs(a); /////////////////////////////// num[2] "\" a=0; if((i>=0&&i<15)&&(j>=0&&i<1)) for(k=0;k<5;k++) a=a+wzq[i+k][j+k]; num[2]=abs(a); ////////////////////////////// num[3] "/" a=0; if((i<19&&i>4)&&(j>=0&&j<15)) for(k=0;k<5;k++) a=a+wzq[i-k][j+k]; num[3]=abs(a); //比较哪个方向同色棋最多 numbig=maxnum(num[0],num[1],num[2],num[3]); //在得到最大值和方向上寻找落棋点 switch(numbig.y) { case 4: searchcandown4(i,j,numbig.x);break; case 3: searchcandown3(i,j,numbig.x);break; case 2: searchcandown2(i,j,numbig.x);break; default: searchcandown1(i,j,numbig.x); }}
//用来比较四个数的最大值//来获得最优落子点CPoint CWuZiQiView::maxnum(int a, int b, int c, int d){ //point.x为方向值 //point.y为最大值 CPoint point; 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.y) { point.x=3; point.y=d; } return point;}
接下来就是不同情况下寻找不同方向上的最佳落子点了,由于具有相似性这里只贴出一段,详细的可以在下方的资源连接处下载:
//有四个同色棋 void CWuZiQiView::searchcandown4(int i, int j, int n) { //CPoint point; int k; ///////////////////////////// num[0] "--" if(n==0) for(k=0;k<5;k++) //如果第一个是空 if(wzq[i][j]==0) { //如果下面有白棋 if(wzq[i+1][j]==1) { //下面位置可以下棋,已经有四个白棋 wpointcan4.x=i; wpointcan4.y=j; break; } else { //下面位置可以下棋,已经有四个黑棋 bpointcan4.x=i; bpointcan4.y=j; break; } } //如果找到下棋位置,一定能找到! else if(wzq[i+k][j]==0) { //如果第一个是白棋 if(wzq[j][j]==1) { wpointcan4.x=i+k; wpointcan4.y=j; break; } //否则第一个是黑棋 else { bpointcan4.x=i+k; bpointcan4.y=j; break; } } //////////////////////////// num[1] "|" if(n==1) for(k=0;k<5;k++) { if(wzq[i][j]==0) if(wzq[i][j+1]==1) { wpointcan4.x=i; wpointcan4.y=j; break; } else { bpointcan4.x=i; bpointcan4.y=j; break; } else if(wzq[i][j+k]==0) { if(wzq[i][j]==1) { wpointcan4.x=i; wpointcan4.y=j+k; break; } else { bpointcan4.x=i; bpointcan4.y=j+k; break; } } } /////////////////////////////// num[2] "\" if(n==2) for(k=0;k<5;k++) { if(wzq[i][j]==0) if(wzq[i+1][j+1]==1) { wpointcan4.x=i; wpointcan4.y=j; break; } else { bpointcan4.x=i; bpointcan4.y=j; break; } else if(wzq[i+k][j+k]==0) { if(wzq[i][j]==1) { wpointcan4.x=i+k; wpointcan4.y=j+k; break; } else { bpointcan4.x=i+k; bpointcan4.y=j+k; break; } } } ////////////////////////////// num[3] "/" if(n==3) for(k=0;k<5;k++) { if(wzq[i][j]==0) if(wzq[i-1][j+1]==1) { wpointcan4.x=i; wpointcan4.y=j; break; } else { bpointcan4.x=i; bpointcan4.y=j; break; } else if(wzq[i-k][j+k]==0) { if(wzq[i][j]==1) { wpointcan4.x=i-k; wpointcan4.y=j+k; break; } else { bpointcan4.x=i-k; bpointcan4.y=j+k; break; } } }}
总结:
void searchcandown4(int i,int j,int n)函数:
如果最大值是4,它必然有1个空位置;我们可以这样计算,如果第1个是空,那我们把它赋值给相应变量;否则,先找那个空位置,然后判断第1个棋子的颜色,并赋相应的值。
void searchcandown3(int i,int j,int n)函数:
如果最大值是3,它有两种情况,一种是3个同色和2个空;一种是4个同色和1个异色。前一种必定能找到1个空位置,赋值;后一种必定找不到空位置,不赋值。所以,先找到空位置,证明有3个同色,对于五子棋来说3个同色是很重要的,再判断是哪种颜色,赋相应的值
void searchcandown2(int i,int j,int n)函数:
如果最大值是2,也有两种情况:一种是有2个同色和3个空位置;一种是有3个同色和1个异色和1个空位置,并且只算3个同色不连在一起的情况(因为如果有三个连续的情况,从全盘来看,必然会被另外的情况所代替)。
分两种算法:一种是有1个空位置,一种是有3个空位置。前者先找到空位置,再判断它下面2个是否同色,同色则赋值给相应变量,异色则不赋值,因为意义不大;后者只要找到1个空位置就行了。
void searchcandown1(int i,int j,int n)函数:
这个函数是为了预防没有前面四种可能通过保存上一步白棋的位置,让计算机随意在其周围落子(基本上只在开局时调用)。
//如果五个位置的和是一void CWuZiQiView::searchcandown1(int i, int j, int n){ //计算刚才白棋落棋点 int ii=(vspoint.x-30)/20; int jj=(vspoint.y-30)/20; int a; for(a=0;a<5;a++) //如果不到边界 if(ii+a<19) { //向右,如果有空位置 if(wzq[ii+a][jj]==0) { //在这个位置下黑棋 bpointcan1.x=ii+a; bpointcan1.y=jj; return; } } //到了边界 else //向左,如果有空位置 if(wzq[ii-1][jj]==0) { bpointcan1.x=ii-a; bpointcan1.y=jj; return; }}
至此基于MFC的五子棋应用所有功能均已实现
详情代码请参照:这里
第一次写那么多篇的博客,请多多指正。
- 基于MFC的五子棋应用(四)实践
- 基于MFC的五子棋应用(四)理论
- 基于MFC的五子棋应用(一)
- 基于MFC的五子棋应用(二)
- 基于MFC的五子棋应用(三)
- [MFC] 入门级demo:基于MFC的五子棋程序
- 基于opencv与MFC实现的双人五子棋
- C++五子棋的实现(MFC)
- 基于MFC五子棋学习第一天
- Maven实战(四)基于Maven的持续集成实践
- (四)基于myeclipse的RocketMQ--Demo实践
- Webservice实践(四)基于AXIS2的服务端开发
- java 实践( 一 )---五子棋
- ZooKeeper系列(四) Zookeeper的应用实践
- 基于Ajax的五子棋演示
- 基于java的五子棋小游戏
- 基于HTML5的五子棋游戏
- 基于html5的五子棋游戏
- webstrom下运行gulp初试
- bzoj5045: 打砖块 (粗制滥造)
- jQuery操作表格(table)的常用方法、技巧汇总
- Java 中的方法内部类
- Python篇(一):ubuntu14.04下Python的环境配置
- 基于MFC的五子棋应用(四)实践
- dpdk vhost研究 (三)
- Palindrome Partitioning
- ConcurrentHashMap
- 《图解HTTP》读书笔记(1)之第一章了解Web及网络基础(关键词:计算机网络/HTTP/Web/网络基础)
- setTimeout与console.log之间执行先后顺序
- Linux中线程栈测试
- 【Java】实现浮点数组的并集以及整型数组的交集和两个字符串数组的逆序排序
- servlet实现秒数跳转操作的源码