MFC-C++五子棋课程设计报告(共23页).doc
精选优质文档-倾情为你奉上课程设计成绩考勤成绩( %)指导老师评语报告及程序成绩( %)总评成绩指导老师签名: windows程序设计课 程 设 计 报 告 学院(系): 计算机科学与技术系 班 级: 计专2班 学生姓名: 梁剑 学号 指导教师: 时间: 从 2010 年1 月18 日 到2010 年1 月22 日专心-专注-专业摘要1. 能够利用所学的基本知识, 设计一个简单的五子棋序,具有以下功能:数据结构的设计;五子棋棋盘的绘制。人机下棋时,计算机下棋算法的设计。人机下棋时,判断任一方获胜的算法的设计。关键字: MFC编程,Windows程序目录项目一: 五子棋五子棋1 设计目的与要求1.1 设计目的1、进一步掌握利用Visual C+进行程序设计的能力;2、进一步理解和运用面向对象程序设计的思想和方法;3、初步掌握开发一个小型实用系统的基本方法;4、学会调试一个较长程序的基本方法;5、理解Windows程序的运行过程;1.2 设计内容能够利用所学的基本知识, 设计一个简单的学生信息管理应用程序,具有以下功能:数据结构的设计;五子棋棋盘的绘制。人机下棋时,计算机下棋算法的设计。人机下棋时,判断任一方获胜的算法的设计。1.3 设计要求整个程序使用winsocket 函数做网络数据收发,基于UDP协议。可以直接使用Windows API编程(SDK编程),也可以使用MFC编程。本课程设计中我们使用的是MFC编程。其具体的设计要求如下所述:2 设计环境或器材、原理与说明2.1 设计环境与器材因为选择使用MFC编程Windows环境与Microsoft Visual C+ 6.0 编译环境;2.2 原理与说明首先利用mfc建立单文档窗口,在窗口上画19*19的表格,把每个表格点的信息存在二维数组wzq1919中。白棋为1,黑棋为-1,这样当要测是否结束时只要查看4个方向的绝对值是否等于5,就可以判定输赢了如图:3.1设计过程及程序代码3.11界面设计: 图3-2 五子棋界面设计打开应用程序时游戏会自动开始。显示表格代码:CMyDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);/ TODO: add draw code for native data here CBrush mybrush1; mybrush1.CreateSolidBrush(RGB(192,192,192); CRect myrect1(0,0,1200,800); pDC->FillRect(myrect1,&mybrush1); /画棋盘框线 CPen mypen; CPen*myoldPen; mypen.CreatePen(PS_SOLID,1,RGB(0,0,0); myoldPen=pDC->SelectObject(&mypen); for(int i=0;i<19;i+) pDC->MoveTo(40,40+i*20); pDC->LineTo(400,40+i*20); pDC->MoveTo(40+i*20,40); pDC->LineTo(40+i*20,400); /重画时显示存在的棋子 CDC Dc; if(Dc.CreateCompatibleDC(pDC)=FALSE) AfxMessageBox("Can't create DC"); for(int n=0;n<19;n+) for(int m=0;m<19;m+) if(wzqnm=1 /显示白棋 Dc.SelectObject(m_bmwhite); pDC->BitBlt(n*20+32,m*20+32,160,160,&Dc,0,0,SRCCOPY); else if(wzqnm=-1) /显示黑棋 Dc.SelectObject(m_bmblack); pDC->BitBlt(n*20+32,m*20+32,160,160,&Dc,0,0,SRCCOPY); 3.1.2 人下棋设计涉及到OnLButtonDown(UINT nFlags, CPoint point)和OnLButtonUp(UINT nFlags, CPoint point)两个函数了。要用哪一个或用两个?用Down函数时是在鼠标按下时放下棋子,可是,要是我们按下后意识到按错了怎么办;那就改用Up函数,表示当鼠标键松开时放下棋子。这样子当鼠标点下并放开时界面上会显示相应的棋子。并调用over函数判断输赢。代码如下:void CMy3_1View:OnLButtonUp(UINT nFlags, CPoint point) / TODO: Add your message handler code here and/or call defaultCDC *pDC=GetDC();CDC Dc;if(Dc.CreateCompatibleDC(pDC)=FALSE) AfxMessageBox("Can't create DC"); /是否在棋盘内 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&&wzqpxpy=0)Dc.SelectObject(m_bmwhite);pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);/表示存在白棋wzqpxpy=1;/检查是否结束over(point);/换黑棋下colorwhite=false;else if(wzqpxpy=0)Dc.SelectObject(m_bmblack); pDC->BitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);wzqpxpy=-1;over(point);colorwhite=true;CView:OnLButtonUp(nFlags, point);3.1.3 电脑下棋:计算机要下棋?就要定位。即搜索棋盘,找出一个最佳点,放下黑棋。实现的方法是:全盘搜索,并把搜索到的位置,保存在变量。由于有多种情况,我们定义变量如下:CPoint bpointcan4,/这个位置空,它旁边有四个黑棋wpointcan4,/这个位置空,它旁边有四个白棋bpointcan3,/这个位置空,它的旁边有三个黑棋wpointcan3,/这个位置空,它的旁边有三个白棋bpointcan2,/这个位置空,它的旁边有两个黑棋wpointcan2,/这个位置空,它的旁边有两个白棋bpointcan1;/不是以上情况,这个位置空3.1.4搜索最优下棋点并在搜索之前都赋值为(-1,-1),然后,进行搜索,并把相应的值保存在相应变量里面,而如果前面已经对变量赋值,我们依然赋值,用新值代替旧值。注意:我们只保存最后一个值,这样的一个好处是,避免了每次都从左上角开始,并且它的随机性比随机函数还随机。全盘搜索完之后,由于上面的变量中至少有一个已经被赋值,即不是(-1,-1),我们可以采用多数优先的方法,让已经有多个同色棋子的位置先下棋。其原理是,如果已经有四个黑棋,计算机再下一个黑棋就赢了;否则,如果人已经有四个白棋,那么计算机就必须放下一个黑棋,阻止白棋下一步赢;如果已经有三个黑棋,再下一个黑棋,变成四个;否则,如果已经有三个白棋,下一个黑棋,破坏它;两个棋子的同理;否则,在刚才白棋下的地方,顺便找一个位置,下棋。computerdown()函数如下: /轮到计算机下棋void CMy3_1View: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);/判断放在哪里/棋多的位置优先/黑白一样多时黑先 /不是-1就表示已经被赋值!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;上面又有两个新函数,分别定义为空函数,如下: /搜索最佳位置void bestputdown(int i,int j);/放下黑棋void putdown(CPoint point);现在,我们就必须对上面两个空函数进行定义了。 在指定位置下棋: 由于putdown(CPoint point)函数的原理非常简单,我们先说明如下: /黑棋下void CMy3_1View: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); wzqpoint.xpoint.y=-1;/由于原来我们检查是否结束时用的是鼠标点下的坐标,而现在/putdown(CPoint point)函数用的是数组棋盘的坐标,所以必须转换CPoint overpoint;overpoint.x=point.x*20+30;overpoint.y=point.y*20+30;over(overpoint);colorwhite=true; 搜索最佳落棋点: 现在就剩下void bestputdown(int i,int j)函数没有定义了(虽然前面的变量函数已经说明了,当时我们这里是用程序扩展的思路进行的,故如此说明)。它的实现原理是:在四个方向上,各自计算那个方向上棋子的状态,我们的思路是利用原来定义的白棋为1,黑棋为-1,的思想,让同个方向上的五个棋子的值相加,取绝对值并赋值给为这个方向定义的局部变量numi。 为什么要用五个棋子的值相加呢?因为,如果几个棋子是同色的,无论黑白,它的绝对值必然大,而对于几个棋子中有黑棋和白棋的,其值必然相加而抵消变小。所以我们可以利用这种方法来寻找旁边有多个同色棋子的空位置(前面已经具体说明)。在每一个棋盘位置,计算以它为起点的四个方向(横、竖、撇、捺),再比较这四个方向中哪个值最大,然后在这个方向上寻找落棋点。/检查四个方向,各算出五个棋子的和并赋值void CMy3_1View:bestputdown(int i,int j) /四个方向的值int num4;int a,k; / num0 ->a=0; if(i<15)for(k=0;k<5;k+)a=a+wzqi+kj;num0=abs(a); / num1 "|"a=0;if(j<15)for(k=0;k<5;k+)a=a+wzqij+k;num1=abs(a);/ num2 ""a=0;if(i<15&&j<15)for(k=0;k<5;k+)a=a+wzqi+kj+k;num2=abs(a);/ num3 "/"a=0;if(i>4)&&(j<15)for(k=0;k<5;k+)a=a+wzqi-kj+k;num3=abs(a); /比较哪个方向同色棋最多/由于我们搜索落棋点时用到最大值和方向,我们可以定义一个Cpoint类变量,/让它返回两个值。可以说,这也是一种巧妙的想法,因为这样你就不用去写/内联函数了CPoint numbig;/numbig.x表示方向 /numbig.y表示最大值numbig=maxnum(num0,num1,num2,num3);/在得到最大值和方向上寻找落棋点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); 同样的方法,我们必须为上面还没有定义的函数添加空函数。 /其中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); 最大值函数的实现: 现在先介绍CPoint maxnum(int a,int b,int c,int d)函数,它只是四个整数的比较: CPoint CMy3_1View:maxnum(int a, int b, int c, int d)/point.x为方向值/point.y为最大值CPoint point;if(a>=b)point.x=0;point.y=a;elsepoint.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 searchcandown4(int i,int j,int n)函数: 如果最大值是四,它必然有一个空位置;我们可以这样计算,如果第一个是空,那我们把它赋值给相应变量;否则,先找那个空位置,然后判断第一个棋子的颜色,并赋相应的值。 /由于相似,下面代码只解释第一个方向/有四个同色棋void CMy3_1View:searchcandown4(int i, int j, int n)int k; / num0 "-"if(n=0)for(k=0;k<5;k+)/如果第一个是空if(wzqij=0)/如果下面有白棋if(wzqi+1j=1)/下面位置可以下棋,已经有四个白棋wpointcan4.x=i;wpointcan4.y=j;break;else/下面位置可以下棋,已经有四个黑棋bpointcan4.x=i;bpointcan4.y=j;break;/如果找到下棋位置,一定能找到!else if(wzqi+kj=0) /如果第一个是白棋if(wzqjj=1)wpointcan4.x=i+k;wpointcan4.y=j;break;/否则第一个是黑棋elsebpointcan4.x=i+k;bpointcan4.y=j;break; / num1 "|"if(n=1)for(k=0;k<5;k+)if(wzqij=0)if(wzqij+1=1)wpointcan4.x=i;wpointcan4.y=j;break;elsebpointcan4.x=i;bpointcan4.y=j;break;else if(wzqij+k=0) if(wzqij=1)wpointcan4.x=i;wpointcan4.y=j+k;break;elsebpointcan4.x=i;bpointcan4.y=j+k;break;/ num2 "" if(n=2)for(k=0;k<5;k+)if(wzqij=0)if(wzqi+1j+1=1)wpointcan4.x=i;wpointcan4.y=j;break;elsebpointcan4.x=i;bpointcan4.y=j;break;else if(wzqi+kj+k=0) if(wzqij=1)wpointcan4.x=i+k;wpointcan4.y=j+k;break;elsebpointcan4.x=i+k;bpointcan4.y=j+k;break;/ num3 "/" if(n=3)for(k=0;k<5;k+)if(wzqij=0)if(wzqi-1j+1=1)wpointcan4.x=i;wpointcan4.y=j;break;elsebpointcan4.x=i;bpointcan4.y=j;break;else if(wzqi-kj+k=0) if(wzqij=1)wpointcan4.x=i-k;wpointcan4.y=j+k;break;elsebpointcan4.x=i-k;bpointcan4.y=j+k;break;void searchcandown3(int i,int j,int n)函数: 如果最大值是三,它有两种情况,一种是三个同色和两个空;一种是四个同色和一个异色。前一种必定能找到一个空位置,赋值;后一种必定找不到空位置,不赋值。所以我们的想法很简单,先找到空位置,证明有三个同色,这对于玩五子棋来说三个同色是很重要的,再判断是哪种颜色,赋相应的值。 /最多有三个同色void CMy3_1View:searchcandown3(int i, int j, int n)int k=0; / num0 "-"if(n=0)for(k=0;k<5;k+)/找到位置if(wzqi+kj=0) /下一个是白棋 if(wzqi+k+1j=1)/下面位置可以下棋,已经有三个白棋 wpointcan3.x=i+k;wpointcan3.y=j; /下一个是黑棋else if(wzqi+k+1j=-1) bpointcan3.x=i+k;bpointcan3.y=j; / num1 "|"if(n=1)for(k=0;k<5;k+)if(wzqij+k=0) if(wzqij+k-1=1) wpointcan3.x=i;wpointcan3.y=j+k;else if(wzqij+k+1=-1)bpointcan3.x=i;bpointcan3.y=j+k;/ num2 "" if(n=2)for(k=0;k<5;k+)if(wzqi+kj+k=0) if(wzqi+k+1j+k+1=1)wpointcan3.x=i+k;wpointcan3.y=j+k;else if(wzqi+k+1j+k+1=-1)bpointcan3.x=i+k;bpointcan3.y=j+k;/ num3 "/" if(n=3)for(k=0;k<5;k+)if(wzqi-kj+k=0)if(wzqi-k-1j+k+1=1) wpointcan3.x=i-k;wpointcan3.y=j+k;else if(wzqi-k-1j+k+1=-1)bpointcan3.x=i-k;bpointcan3.y=j+k; void searchcandown2(int i,int j,int n)函数: 如果最大值是二,也有两种情况:一种是有两个同色和三个空位置;一种是有三个同色和一个异色和一个空位置,并且只算三个同色不连在一起的情况(因为如果有三个连续的情况,重全盘搜索的角度看,必然会被另外的情况所代替)。分两种算法:一种是有一个空位置,一种是有三个空位置。前者先找到空位置,再判断它下面两个是否同色,同色则赋值给相应变量,异色则不赋值,因为意义不大;后者只要找到一个空位置就行了。 /最多有两个同色void CMy3_1View:searchcandown2(int i, int j, int n)int k=0,m=0,a=0,b=0; / num0 "-"if(n=0)/判断有多少个空位置for(k=0;k<5;k+)if(wzqi+kj=0) m+=1;/如果只有一个空位置if(m=1)for(a=0;a<5;a+)/找到空位置if(wzqi+aj=0)/下面两个棋子值的和b=wzqi+a+1j+wzqi+a+2j;/都是黑棋if(b=-2)/下面位置可以下棋,旁边有两个黑棋bpointcan2.x=i+a;bpointcan2.y=j;/都是白棋if(b=2)wpointcan2.x=i+a;wpointcan2.y=j;/如果有三个空位置,说明另外两个同色if(m=3)for(a=0;a<5;a+)/如果两个是黑棋if(wzqi+aj=-1)for(b=0;b<5;b+)/如果找到空位置if(wzqi+bj=0)/下面位置可以下棋,旁边有两个黑棋bpointcan2.x=i+b;bpointcan2.y=j;break;else /如果两个是白棋if(wzqi+aj=1)for(b=0;b<5;b+)if(wzqi+bj=0)wpointcan2.x=i+b;wpointcan2.y=j;break;/ num1 "|"m=0;if(n=1)for(k=0;k<5;k+)if(wzqij+k=0) m+;if(m=1)for(a=0;a<5;a+)if(wzqij+a=0)b=wzqij+a+1+wzqij+a+2;if(b=-2)bpointcan2.x=i;bpointcan2.y=j+a;if(b=2)wpointcan2.x=i;wpointcan2.y=j+a;if(m=3)for(a=0;a<5;a+)if(wzqij+a=-1)for(b=0;b<5;b+)if(wzqij+b=0)bpointcan2.x=i;bpointcan2.y=j+b;break;else if(wzqij+a=1)for(b=0;b<5;b+)if(wzqij+b=0)wpointcan2.x=i;wpointcan2.y=j+b;break;/ num2 ""m=0; if(n=2)for(k=0;k<5;k+)if(wzqi+kj+k=0) m+;if(m=1)for(a=0;a<5;a+)if(wzqi+aj+a=0)b=wzqi+a+1j+a+1+wzqi+a+2j+a+2;if(b=-2)bpointcan2.x=i+a;bpointcan2.y=j+a;if(b=2)wpointcan2.x=i+a;wpointcan2.y=j+a;if(m=3)for(a=0;a<5;a+)if(wzqi+aj+a=-1)for(b=0;b<5;b+)if(wzqi+bj+b=0)bpointcan2.x=i+b;bpointcan2.y=j+b;break;else if(wzqi+aj+a=1)for(b=0;b<5;b+)if(wzqi+bj+b=0)wpointcan2.x=i+