VC++MFC单机和网络版五子棋

上传人:hao****021 文档编号:121623196 上传时间:2022-07-19 格式:DOC 页数:32 大小:655.01KB
返回 下载 相关 举报
VC++MFC单机和网络版五子棋_第1页
第1页 / 共32页
VC++MFC单机和网络版五子棋_第2页
第2页 / 共32页
VC++MFC单机和网络版五子棋_第3页
第3页 / 共32页
点击查看更多>>
资源描述
五子棋五子棋是一种很受人们喜爱的游戏,它的规则简单,但玩法变化多端,富有趣味性,适合人们消遣。这里我们就来设计一个五子棋游戏。(一) (一) 人对人游戏 1. 1. 游戏实现人对人游戏,其实只是对游戏规则的实现,我们只是利用五子棋游戏的规则来编程,至于真正的游戏实现计算机的“智能”算法,我们将在后面讲述。五子棋的规则很简单:1,判断是否能放下棋子(是否已经有了棋子);2,判断是哪种颜色下棋;3,判断是否已经结束(是谁赢?)。这些规则,我们将用相应的函数来实现。其它,我们还将介绍其它一些功能的实现。如鼠标的更换,工具栏和状态栏的编辑,类与类之间的相互调用。新建工程3_1,选择单文档,在Step 4 of 6中先中Windows Sockets复选框。如下图: 图3-1-12. 2. 资源编辑由于我们这个程序出现的关于资源编辑的内容太多,我们具体介绍如下:见下图3-1-2,我们需要添加的有: 图3-1-2 黑白位图Bitmap以表示棋盘上面的棋子:IDB_BLACK IDB_WHITE黑白鼠标Cursor以替换当前鼠标:IDC_CURSOR1黑棋子IDC_CURSOR2白棋子说明:由于下棋时我们必须把鼠标热点设置在中间,点击下图(图3-1-3)最右边按扭,然后把鼠标移动到图像中你想设置为热点的地方,按下鼠标左键。 图3-1-3黑白图标Icon以显示在状态栏供以提示:IDI_BLACKIDI_WHITE 说明: 由于我们的图标支持256色,按下下图(图3-1-4)最右边按扭,选择Device里面显示的选项。 图3-1-4菜单以供操作:开始:ID_START保存:ID_SAVE打开:ID_OPEN工具栏:如上图所示。说明:工具栏一般都是根据菜单选项而产生的,它的ID一般都能从菜单的ID 中找到。3. 3. 变量函数首先,为了实现状态栏的应用,我们必须更改它的变量:在MainFrm.h文件里面,把CStatusBar m_wndStatusBar 为public接着是在3_1View.h文件里面添加变量函数: /两个鼠标 HCURSOR hcursorwhite;HCURSOR hcursorblack; /棋盘数组int wzq1919; / colorwhite TRUE时白棋下,否则黑棋下bool colorwhite; /棋子位图CBitmap m_bmblack;CBitmap m_bmwhite;/保存文件void Save();/检查是否结束void over(CPoint point);/鼠标操作afx_msg void OnLButtonUp(UINT nFlags, CPoint point);/鼠标图形更换afx_msg BOOL OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message);/菜单的开始afx_msg void OnStart();/菜单的保存afx_msg void OnSave();/菜单的打开afx_msg void OnOpen();4. 4. 具体实现棋盘大小设置:由于我们的游戏的棋盘大小是一定的,不能改变大小的,是应该符合要求的。在如下函数添加设置窗口大小的语句:BOOL CMainFrame:PreCreateWindow(CREATESTRUCT& cs)if( !CFrameWnd:PreCreateWindow(cs) )return FALSE;/ TODO: Modify the Window class or styles here by modifying/ the CREATESTRUCT cscs.dwExStyle=cs.dwExStyle|WS_EX_TOPMOST;/cs.style=WS_SYSMENU|WS_OVERLAPPED|WS_MINIMIZEBOX;/; /设置窗口大小:400*340cs.cx=450;cs.cy=500; return TRUE;初始化变量:在构造函数里添加初始代码:CMy3_1View:CMy3_1View()/ TODO: add construction code here/Load鼠标图像和棋子位图hcursorblack=AfxGetApp()-LoadCursor(IDC_CURSOR1);hcursorwhite=AfxGetApp()-LoadCursor(IDC_CURSOR2);m_bmwhite.LoadBitmap(IDB_WHITE);m_bmblack.LoadBitmap(IDB_BLACK);/清理棋盘 /数组值为0表示没有棋子for(int i=0;i19;i+)for(int j=0;jFillRect(myrect1,&mybrush1);/画棋盘框线CPen mypen;CPen*myoldPen;mypen.CreatePen(PS_SOLID,1,RGB(0,0,0); myoldPen=pDC-SelectObject(&mypen); for(int i=0;iMoveTo(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(Cant create DC);for(int n=0;n19;n+)for(int m=0;mBitBlt(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);设置鼠标:棋盘画好了,接下来就是下棋了。但鼠标并没有像我们上面说的那样变成白棋,加函数如下:BOOL CMy3_1View:OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message) / TODO: Add your message handler code here and/or call defaultif(nHitTest=HTCLIENT)/白棋下,显示白棋鼠标if(colorwhite) /调用主框架里面的状态栏CMainFrame*pFrm=(CMainFrame*)AfxGetApp()-m_pMainWnd;CStatusBar*pStatus=&pFrm-m_wndStatusBar;if(pStatus)pStatus-GetStatusBarCtrl().SetIcon(0,AfxGetApp()-LoadIcon(IDI_WHITE);pStatus-SetPaneText(0,白棋下);SetCursor(hcursorwhite);/显示黑棋鼠标elseSetCursor(hcursorblack);CMainFrame*pFrm=(CMainFrame*)AfxGetApp()-m_pMainWnd;CStatusBar*pStatus=&pFrm-m_wndStatusBar;if(pStatus)/显示图像pStatus-GetStatusBarCtrl().SetIcon(0,AfxGetApp()-LoadIcon(IDI_BLACK);/显示文字pStatus-SetPaneText(0,黑棋下);return 1;return CView:OnSetCursor(pWnd, nHitTest, message);现在运行程序,怎样,鼠标变成白棋了,而且下面的状态栏也能够显示鼠标状态了,真是一举两得。可是,又该怎样把棋子放在棋盘上呢?下棋操作:这就涉及到OnLButtonDown(UINT nFlags, CPoint point)和OnLButtonUp(UINT nFlags, CPoint point)两个函数了。要用哪一个或用两个?用Down函数时是在鼠标按下时放下棋子,可是,要是我们按下后意识到按错了怎么办;那就改用Up函数,表示当鼠标键松开时放下棋子。OK!添加函数如下: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(Cant create DC); /是否在棋盘内 if(point.x30&point.x30&point.yBitBlt(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);由上面可以看出,当鼠标键松开时判断,如果那个位置没有棋子,则放下,并把棋盘数组赋相应的值:1 或-1。是否结束:接着是用一个over()函数判断是否结束,是则结束并重新开始;否则,接着把鼠标变成对方棋子,表示对方下棋。那over()函数又是怎样的呢? 此函数是利用刚下棋的位置为中心,检查它各个方向上的连续五个棋子是否同色,是则结束并重新开始。然而,我们又是怎样判断一个方向上的五个棋子的同色的?这就涉及地为什么我要把五子棋数组赋值为1和-1的问题。因为这样有一个好处:利用连续五个棋子的值相加,如果它们的值的绝对值等于5,则说明是同色。当然,这只是这样赋值的一点作用,真正的作用将在后面介绍。添加如下:void CMy3_1View:over(CPoint point)/获取鼠标指向数组位置,即中心位置int x=(point.x-30)/20;int y=(point.y-30)/20;/计算开始判断的坐标 xx,yyint xx,yy; if(x4)xx=0;elsexx=x-4;if(y4)yy=0;elseyy=y-4;int i,j,a;/横向判断for(i=xx;i15;i+) a=0;for(j=i;ji+5;j+)a=a+wzqjy;/五个都是白棋 if(a=5)AfxMessageBox(白棋胜!);/重新开始OnStart();return;/五个都是黑棋if(a=-5)AfxMessageBox(黑棋胜!);OnStart();return;/竖向判断for(i=yy;i15;i+) a=0;for(j=i;ji+5;j+)a=a+wzqxj; if(a=5)AfxMessageBox(白棋胜!);OnStart();return;if(a=-5)AfxMessageBox(黑棋胜!);OnStart();return;/向右下角/判断起点位置if(xy)if(xx=0)yy=y-x;elseif(yy=0)xx=x-y;/参数over=1时退出循环int over=0;doa=0;for(i=0;i5;i+)if(xx+i)19|(yy+i)(18-x)if(x13)yy=y-(18-x);xx=18;elseyy=y-4;xx=x+4;elseif(y5)xx=x+y;yy=0;elseyy=y-4;xx=x+4; over=0;doa=0;for(i=0;i=0|(yy+i)19)a=a+wzqxx-iyy+i;if(a=5)AfxMessageBox(白棋胜!);OnStart();return;if(a=-5)AfxMessageBox(黑棋胜!);OnStart();return;/到了边界elseover=1; xx-=1;yy+=1;while(over=0); 现在,我们的人对人游戏就完成了。下面介绍附加内容。5. 5. 附加内容理论上,这个游戏并无须保存,因为它确实太小了。事实上,这个游戏有保存的功能,由于我们学习的需要。这个游戏的保存,与其说是学习文件的保存,不如说是我们学习字符串的操作。另外,这个附加的内容并不是为了当前的学习而添加的,而是为了后面的学习和应用而铺垫的。 保存文件:保存文件函数是一个菜单选项。它的作用就是保存当前游戏的状态。首先,我们应该为我们自己的文件定义一个后缀名:.wzq;接着是打开保存文件的公共对话框,如果确定,则表示保存,那么就先获取文件名,然后按照一定的顺序保存各个点的数组的值,最后保存当前是哪种颜色下棋。void CMy3_1View:OnSave() / TODO: Add your command handler code here/设置保存的文件,后缀名wzqCFileDialog dlg(FALSE,wzq,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,(*.WZQ)|*.wzq|All Files|*.*|,this);/如果公共类对话框为确定if(dlg.DoModal()=IDOK) /获取文件名dlg.GetFileName();/否则,退出elsereturn;/字符串变量CString str;int i,j; CStdioFile file;/如果有问题,退出if(file.Open(dlg.GetFileName(),CFile:modeCreate|CFile:modeWrite|CFile:typeText)=0)AfxMessageBox(save error!);return;/循环把棋盘数组的值写进文件for(i=0;i19;i+)for(j=0;j19;j+)if(wzqij=-1)file.WriteString(-1n);if(wzqij=0)file.WriteString(0n);if(wzqij=1)file.WriteString(1n);/保存当前下棋颜色if(colorwhite=true)file.WriteString(1n);elsefile.WriteString(0n);/关闭文件 file.Close();读取文件:读文件就是把我们以前保存的文件打开,读取当前打开文件的内容,并给数组赋值使和文件内容相同,然后可以继续进行游戏。/与保存文件相反void CMy3_1View:OnOpen() / TODO: Add your command handler code hereCFileDialog dlg(TRUE,wzq,NULL,OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,(*.WZQ)|*.wzq|All Files|*.*|,this);if(dlg.DoModal()=IDOK) dlg.GetFileName();elsereturn;CString str;int i,j,m;CStdioFile file;if(file.Open(dlg.GetFileName(),CFile:modeRead)=0)AfxMessageBox(save error!);return;CArchive ar(&file,CArchive:load);for(i=0;i19;i+)for(j=0;j30&point.x30&point.yBitBlt(px*20+32,py*20+32,160,160,&Dc,0,0,SRCCOPY);wzqpxpy=1;over(point);colorwhite=false;/保存白棋位置vspoint=point;/计算机下棋computerdown(); /人对人if(vscomputer=2)if(point.x30&point.x30&point.yBitBlt(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);由上面可知,我们对人对机游戏的方法是采用:人下完了之后,检查是否胜利,是则结束游戏,重新开始,并改为黑棋(即计算机)先下;如果人没有胜利,也改为计算机下。而对于计算机怎么下棋,我们只是用了一个函数computerdown()表示。但是现在我们并不能运行程序,因为有一个没有定义的函数,怎么办呢?l l 程序扩展思想:添加一个空函数!仔细看程序的话,你还会发现一个变量Cpoint vspoint,这是后来添加的它的用处是保存刚才白棋下的位置,有利于黑棋下棋时的定位。接着,我们的主要问题就是实现computerdown()函数,让计算机能够自动下棋!计算机下棋:计算机是怎样下棋?这就是定位的问题了。即搜索棋盘,找出一个最佳点,放下黑棋。我们实现的方法是:全盘搜索,并把搜索到的位置,保存在变量。由于有多种情况,我们定义变量如下:CPoint bpointcan4,/这个位置空,它旁边有四个黑棋wpointcan4,/这个位置空,它旁边有四个白棋bpointcan3,/这个位置空,它的旁边有三个黑棋wpointcan3,/这个位置空,它的旁边有三个白棋bpointcan2,/这个位置空,它的旁边有两个黑棋wpointcan2,/这个位置空,它的旁边有两个白棋bpointcan1;/不是以上情况,这个位置空并在搜索之前都赋值为(-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;i19;i+)for(int j=0;jBitBlt(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(i15)for(k=0;k5;k+)a=a+wzqi+kj;num0=abs(a); / num1 |a=0;if(j15)for(k=0;k5;k+)a=a+wzqij+k;num1=abs(a);/ num2 a=0;if(i15&j15)for(k=0;k4)&(j15)for(k=0;k=b)point.x=0;point.y=a;elsepoint.x=1;point.y=b;if(cpoint.y)point.x=2;point.y=c;if(dpoint.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;k5;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)w
展开阅读全文
相关资源
相关搜索

最新文档


当前位置:首页 > 机械制造 > 电气技术


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!