人工智能实习报告_第1页
人工智能实习报告_第2页
人工智能实习报告_第3页
人工智能实习报告_第4页
人工智能实习报告_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

人工智能课程设计报告姓名:课程设计报告班级:191092学号:-指导老师:2011年10月经过近一周的奋战,我终于初步实现了我的“四子棋”游戏。起初,我选择这个游戏是因为它很独特、少有人做,而且我想当然的认为四子比五子少一子,实现起来应该相对简单,但是面对一个个头疼的错误,遭受一次又一次失败的打击,我才发现自己想错了,四子棋人机对战比五子棋更让人纠结。暑期我和班上两名同学xxx,xx一起完成过五子棋和黑白棋的编程。当时,五子棋我们是参照别人的模板完成的,黑白棋是在透彻研究过五子棋的基础上不断讨论、优化算法最终完成的。现在,他们一个继续做五子棋一个做黑白棋,将其完善作为人工智能的课程设计课题。我只得另陌新路,关注起了四子棋。界面的搭建以及二人对战的实现只用了我两个晚上的时间,当然我的界面构建的相对简单。二人对战实现关键一步就是输赢判断函数judge()的编写。人机对战整整折腾了自己四五天的时间,我本来对于博弈过程中alpha—beta剪枝算法很理解的,但是具体要自己编程实现时我觉得自己只了解到其皮毛。所以,我不断看五子棋程序、单步跟踪、寻问同学,终于算是把其递归和剪枝过程搞得明明白白。花了两天完成了alpha—beta剪枝算法我本以为核心都解决了,却不知更让人头疼的是评估函数的确定还有统计当前棋局状况的函数Calculate()的编写。难题终究是可以被客服的,我花了整整一天写完了我的Calculate()函数。直至10月27日上午我依然没有让机器足够智能,或者说它目前很呆板。我一直在找原因,自己的模块设计算法思路都是对的,请教过同学她也觉得设计思路没有错。现在,我整体的想了一下,觉得应该是我的静态评估函数设计的不够合理,或者说目前的设计方法对于五子棋很合理但是对于规则不同的四子棋就可能存在缺陷。既然目前时间不允许没能实现足够智能,那就只能把这粗略的程序提交。我一定会继续完善、改进算法,重新设计评估函数,争取让机器达到理想的“智能”。一、课题选则题目概述利用VC++实现四子棋游戏,要求提供可视化界面以及二人对战、人机对战的功能。四子棋游戏规则如下:四子棋的棋盘共有7行7列,棋盘是垂直摆放,每名玩者只能左右控制落子的位置。两名玩者轮流每次把一只棋子放进棋盘任何未全满的一行中,棋子会估据一行中最底未被估据的位置。两名玩者任何一方先以四只棋子在横,竖或斜方向联成一条直线,便可获胜,游戏亦结束。假如棋盘已完全被棋子填满,但仍未有任何一方成功把四只棋子成一直线,则成为和局。选题缘由四子棋游戏规则独特,所以其规则的设计将有别于五子棋等通常的棋类游戏。四子棋人机对战功能的实现需要用到博弈树搜索中的核心算法:。与五子棋不同,因为四子棋的独特性网络中根本没有某某“热心”网友的游戏源码,我找了很久也只找到一个二人对战的四子棋,他还是用C语言在dos下实现的,一点也不人性化。综合以上种种我决定挑战一下自己,用VC实现“我的四子棋”游戏。二、需求分析功能需求1.1提供合理的人机交互界面1.2提供二人对战功能1.3提供人机对战功能数据结构2.1棋盘数据采用一二维数组grid[7][7]存储三、模块设计整体思路通过对需求的分析,我认为我的设计工作主要可以分为三大块,第一块是主界面的搭建和棋盘的绘制,第二块是落子和输赢判断的实现,第三块就是二人对战和人机对战的实现。对于第一块,因为有图形学和图像处理的上机经验,我将果断的选择单文档作为界面窗口,用绘制的方式搭建棋盘。对于第二块,对于落子其实就是在响应鼠标左键按下的消息后记录下所需放子的坐标,然后改变内部存储数据,调用绘图函数ondraw()进行绘制。输赢判断就是对于每下一颗棋子搜索其横向竖向交叉斜向的棋子,如若有四颗连成一线的就判为赢。对于第三模块,这是整个游戏的核心,二人对战相对好实现。人机对战需要用到博弈树搜索需要用到alphabeta剪枝算法,还需要设定以静态评估函数,这两方面是最困难也是最重要的部分。具体设计主界面设计1.1单文档窗口大小的限定我们知道通过VC++工程创建的单文档都有默认的大小,那个大小太大不适合作为游戏的界面窗口大小。所以我希望自定义其大小,而且在此游戏中不需要用工具栏,所以我希望通过人为操作去掉工具栏。具体实现:在Frame框架类里面的PreCreateWindow()创建窗口函数总添加以下代码cs.style&=〜WS_MAXIMIZEBOX;〃禁止对文档最大化操作cs.style&=〜WS_THICKFRAME;cs.cx=700;//自定义文档大小cs.cy=500;cs.y=180;cs.x=300;cs.style&=〜FWS_ADDTOTITLE;//将FWS_ADDTOTITLE去掉cs.lpszName="我的四子棋”;〃改变文档标题将OnCreate(LPCREATESTRUCTlpCreateStruct函数中以下代码屏蔽即可去掉工具栏/*if(!m_wndToolBar.CreateEx(this,TBSTYLE_FLAT,WS_CHILDIWS_VISIBLEICBRS_TOPICBRS_GRIPPERICBRS_TOOLTIPSICBRS_FLYBYICBRS_SIZE_DYNAMIC)II!m_wndToolBar.LoadToolBar(IDR_MAINFRAME)){TRACE0("Failedtocreatetoolbar\n");return-1;//failtocreate}*/1.2棋盘绘制棋盘绘制在view类里面的ondraw函数里实现,创建画笔蓝色画笔通过MoveTo、LineTo函数画出棋格,创建天蓝色画刷绘制棋格背景色。创建红色、黄色画刷用于绘制双方棋手棋子。具体代码如下:CRectrect;CBrush*brush,*brush1,*brush2,*brush3;brush=newCBrush;brush1=newCBrush;brush2=newCBrush;brush3=newCBrush;brush->CreateSolidBrush(RGB(150,205,205));_brush1->CreateSolidBrush(RGB(255,0,0));//红色画刷brush2->CreateSolidBrush(RGB(255,255,0));//黄色画刷brush3->CreateSolidBrush(RGB(128,128,28));//bai色画刷this->GetClientRect(&rect);pDC->FillRect(&rect,brush);CClientDCdc(this);CPenm_pen(PS_SOLID,1,RGB(0,0,255));〃将画笔选入设备列表CPen*pOldPen=dc.SelectObject(&m_pen);inti;for(i=0;i<8;i++){dc.MoveTo(bx,by+width*i);dc.LineTo(bx+width*7,by+width*i);}for(i=0;i<8;i++){dc.MoveTo(bx+width*i,by);dc.LineTo(bx+width*i,by+width*7);}for(i=0;i<7;i++){for(intj=0;j<7;j++){switch(grid[i][j]){case1:pDC->SelectObject(brush1);pDC->Ellipse(bx+width*(i+0.5)-r,by+width*(j+0.5)-r,bx+width*(i+0.5)+r,by+width*(j+0.5)+r);break;case2://pDC->SelectStockObject(WHITE_PEN);pDC->SelectObject(brush2);pDC->Ellipse(bx+width*(i+0.5)-r,by+width*(j+0.5)-r,bx+width*(i+0.5)+r,by+width*(j+0.5)+r);//pDC->SelectStockObject(BLACK_PEN);break;case3:pDC->SelectObject(brush3);pDC->Ellipse(bx+width*(i+0.5)-(r+5),by+width*(j+0.5)-(r+5),bx+width*(i+0.5)+(r+5),by+width*(j+0.5)+(r+5));break;default:break;}}}模式选择设计2.1更改菜单按钮在菜单栏中添加模式按钮“二人对战”和“人机对战”「箱室莅';游裁规则QJ入机对战2.2响应选择按钮消息通过MFC类向导在View类中响应“二人对战”和“人机对战”的按钮消息,实现方式如下:voidCMYSIZIQIView::OnPvsp(){//TODO:Addyourcommandhandlercodeherereset();pvsp=true;Invalidate();//是当前窗口无效,需要重绘}voidCMYSIZIQIView::OnPvsc(){//TODO:Addyourcommandhandlercodeherereset();pvsc=true;RorY=true;pvsp=false;Invalidate();//是当前窗口无效,需要重绘}3.Onleftbuttondown()函数设计3.1根据鼠标坐标转换出在存储数组中对应的下标intpx=(point.x-bx)/width;intpy=(point.y-by)/width;3.2限定落子只可以在该列最下面一个空格for(intj=6;j>=0;j--){if(grid[px][j]==0){py=j;break;}}3.3二人对战、人机对战程序响应if(0<=px&&px<7&&0<=py&&py<7&&grid[px][py]==0){if(!pvsp&&!pvsc)MessageBox("请选择对战模式");elseif(pvsp)〃二人对战{grid[px][py]=c;//根据该点颜色选择对应的画笔color=!color;//换位另一颜色r.SetRect(point.x-8*width,point.y-8*width,point.x+8*width,point.y+8*width);InvalidateRect(&r);judge(c,px,py);//判断有没有取胜}elseif(pvsc)//人机对战模式{color=!color;if(!judge(c,px,py))//ifplayerdoesn'twin{alphabeta(LONG_MIN,LONG_MAX,depth,false);//computerputsthecolor=!color;InvalidateRect(&r);judge(c,bestmove[0],bestmove[1]);}}}4.judge()函数设计4.0每一个方向定义一个变量用以记录当前棋子个数:HWIN——横方向SWIN——竖方向LRWIN——左上右下方向RLWIN——右上左下方向4.1横向判断是否四子连线intHWIN=1;for(i=x-1;i>=0;i--)//&&&&&&&&&棋子左边{//MessageBox("红赢”);if(grid[i][y]==color)HWIN++;if(grid[i][y]!=color)break;}if(HWIN>=4&&color==1)//左边可能已经三个棋子{for(i=x-1;i>=0;i--)//一方赢了则改变其颜色if(grid[i][y]==color){grid[i][y]=3;}grid[x][y]=3;MessageBox("红赢”);reset();Invalidate();returntrue;}elseif(HWIN>=4&&color==2){for(i=x-1;i>=0;i--)//一方赢了则改变其颜色if(grid[i][y]==color){grid[i][y]=3;}grid[x][y]=3;MessageBox("黄赢”);reset();Invalidate();returntrue;}t0=HWIN;for(i=x+1;i<=6;i++)//&&&&&&&&&&&&棋子右边{//MessageBox("红赢”);if(grid[i][y]==color)HWIN++;if(grid[i][y]!=color)break;}if(HWIN>=4&&color==1){for(i=x+1;i<=6;i++)//一方赢了则改变其颜色if(grid[i][y]==color){grid[i][y]=3;}grid[x][y]=3;if(HWIN-t0==2)grid[x-1][y]=3;if(HWIN-t0==1){grid[x-1][y]=3;grid[x-2][y]=3;}MessageBox("红赢");reset();Invalidate();returntrue;}elseif(HWIN>=4&&color==2){for(i=x+1;i<=6;i++)//一方赢了则改变其颜色if(grid[i][y]==color){grid[i][y]=3;}grid[x][y]=3;if(HWIN-t0==2)grid[x-1][y]=3;if(HWIN-t0==1){grid[x-1][y]=3;grid[x-2][y]=3;}MessageBox("黄赢”);reset();Invalidate();returntrue;}具体判断细节见注释4.2竖向判断是否四子连线〃***********************竖向判断***************************//intSWIN=1;for(j=y+1;j<=6;j++)//&&&&&&&&&棋子下边{//MessageBox("红赢”);if(grid[x][j]==color)SWIN++;if(grid[x][j]!=color)break;}if(SWIN>=4&&color==1)//T边可能已经三个棋子{for(j=y+1;j<=6;j++)//一方赢了则改变其颜色if(grid[x][j]==color&&j-y<=3){grid[x][j]=3;}grid[x][y]=3;MessageBox("红赢”);reset();Invalidate();returntrue;}elseif(SWIN>=4&&color==2){for(j=y+1;j<=6;j++)//一方赢了则改变其颜色if(grid[x][j]==color&&j-y<=3){grid[x][j]=3;}grid[x][y]=3;MessageBox("黄赢”);reset();Invalidate();returntrue;}具体判断细节见注释4.3左上右下方向判断是否四子连线,2““““““““““““““““““““L—Z——r-^t—————————————————————————////**********************左上右下力向*************************//intLRWIN=1;for(i=x-1,j=y-1;i>=0&&j>=0;i--,j--)//&&&&&&&&&棋子左上方{//MessageBox("红赢”);if(grid[i][j]==color)LRWIN++;if(grid[i][j]!=color)break;}if(LRWIN>=4&&color==1)//左上可能已经三个棋子{for(i=x-1,j=y-1;i>=0&&j>=0;i--,j--)〃一方赢了则改变其颜色if(grid[i][j]==color&&(y-j<=3)){grid[i][j]=3;}grid[x][y]=3;MessageBox("红赢”);reset();Invalidate();returntrue;}elseif(LRWIN>=4&&color==2){for(i=x-1,j=y-1;i>=0&&j>=0;i--,j--)〃一方赢了则改变其颜色if(grid[i][j]==color&&(y-j<=3)){grid[i][j]=3;}grid[x][y]=3;MessageBox("黄赢”);reset();Invalidate();returntrue;}t0=LRWIN;for(i=x+1,j=y+1;i<=6&&j<=6;i++,j++)//&&&&&&&&&&&&棋子右下边{if(grid[i][j]==color)LRWIN++;if(grid[i][j]!=color)break;}if(LRWIN>=4&&color==1){for(i=x+1,j=y+1;i<=6&&j<=6;i++,j++)〃一方赢了则改变其颜色if(grid[i][j]==color&&(j-y)<=3){grid[i][j]=3;}grid[x][y]=3;if(LRWIN-t0==2)grid[x-1][y-1]=3;if(LRWIN-t0==1){grid[x-1][y-1]=3;grid[x-2][y-2]=3;}MessageBox("红赢”);reset();Invalidate();returntrue;}elseif(LRWIN>=4&&color==2){for(i=x+1,j=y+1;i<=6&&j<=6;i++,j++)〃一方赢了则改变其颜色if(grid[i][j]==color&&(j-y)<=3){grid[i][j]=3;}grid[x][y]=3;if(LRWIN-t0==2)grid[x-1][y-1]=3;if(LRWIN-t0==1){grid[x-1][y-1]=3;grid[x-2][y-2]=3;}MessageBox("黄赢”);reset();Invalidate();returntrue;}具体判断细节见注释4.4右上左下方向判断是否四子连线,2““““““““““““““““““““““―L-f—""nr"tt——————————————————————————////***********************右上左下力向**************************//intRLWIN=1;for(i=x+1,j=y-1;i<=6&&j>=0;i++,j--)//&&&&&&&&&棋子右上方{if(grid[i][j]==color)RLWIN++;if(grid[i][j]!=color)break;}if(RLWIN>=4&&color==1)//可能已经三个棋子{for(i=x+1,j=y-1;i<=6&&j>=0;i++,j--)〃一方赢了则改变其颜色if(grid[i][j]==color&&(y-j<=3)){grid[i][j]=3;}MessageBox("红赢”);reset();Invalidate();returntrue;}elseif(RLWIN>=4&&color==2){for(i=x+1,j=y-1;i<=6&&j>=0;i++,j--)〃一方赢了则改变其颜色if(grid[i][j]==color&&(y-j<=3)){grid[i][j]=3;}grid[x][y]=3;MessageBox("黄赢”);reset();Invalidate();returntrue;}t0=RLWIN;for(i=x-1,j=y+1;i>=0&&j<=6;i--,j++)//&&&&&&&&&&&&棋子左下边{if(grid[i][j]==color)RLWIN++;if(grid[i][j]!=color)break;}if(RLWIN>=4&&color==1){for(i=x-1,j=y+1;i>=0&&j<=6;i--,j++)〃一方赢了则改变其颜色if(grid[i][j]==color&&(j-y<=3)){grid[i][j]=3;}grid[x][y]=3;if(RLWIN-t0==2)grid[x+1][y-1]=3;if(RLWIN-t0==1){grid[x+1][y-1]=3;grid[x+2][y-2]=3;}MessageBox("红赢”);reset();Invalidate();returntrue;}elseif(RLWIN>=4&&color==2){for(i=x-1,j=y+1;i>=0&&j<=6;i--,j++)〃一方赢了则改变其颜色if(grid[i][j]==color&&(j-y<=3)){grid[i][j]=3;}grid[x][y]=3;if(RLWIN-t0==2)grid[x+1][y-1]=3;if(RLWIN-t0==1){grid[x+1][y-1]=3;grid[x+2][y-2]=3;}MessageBox("黄赢”);reset();Invalidate();returntrue;}具体判断细节见注释5.reset()函数设计该函数很简单,即是将数组存储空间归零化。起作用就是让二人对战、人机对战函数调用,达到初始化数据的目的:for(inti=0;i<7;i++)for(intj=0;j<7;j++)grid[i][j]=0;6.alphabeta()函数设计6.1极大极小值分析法6.1.1其基本思想或算法是:设博弈的双方中一方为MAX,另一方为MIN。然后为其中的一方(例如MAX)寻找一个最优行动方案。为了找到当前的最优行动方案,需要对各个可能的方案所产生的后果进行比较,具体地说,就是要考虑每一方案实施后对方可能采取的所有行动,并计算可能得为计算得分,需要根据问题的特性信息定义一个估价函数,用来估算当前博弈树端节点的得分。此时估算出来的得分称为静态估值。当端节点的估值计算出来后,再推算出父节点的得分,推算的方法是:对“或”节点,选其子节点中一个最大的得分作为父节点的得分,这是为了使自己在可供选择的方案中选一个对自己最有利的方案;对“与”节点,选其子节点中一个最小的得分作为父节点的得分,这是为了立足于最坏的情况。这样计算出的父节点的得分称为倒推值如果一个行动方案能获得较大的倒推值,则它就是当前最好的行动方案。6.2a-B剪枝技术6.2.1算法思想:intAlphaBeta(intdepth,intalpha,intbeta){if(depth==0)//叶子节点;returnEvaluate();//返回当前盘面的估值;GenerateLegalMoves();//产生所有合理走法;while(MovesLeft()){//如果还有未分析的走法;MakeNextMove();//试走一步;val=-AlphaBeta(depth-1,-beta,-alpha);//返回当前节点//的估值,加负号是为了消除双方的差别,使程序简洁;UnmakeMove();//退回到没走前的状态;if(val>=beta)//剪枝;returnbeta;if(val>alpha)//存当前值;alpha=val;}returnalpha;//返回当前值;}6.2.2关键点与具体设计:alphabeta()函数设计的关键点包括递归调用、剪枝操作。其中递归的深度由参数depth限定,剪枝操作跟递归返回的evalue评估函数值息息相关。longCMYSIZIQIView::alphabeta(longalpha,longbeta,intd,boolm){inti,j;if(d==0)//thedepthofsearchisenoughreturnevalue();for(i=0;i<7;i++)//searchfortheblank{for(j=0;j<7;j++)if(grid[i][j]==0)break;if(grid[i][j]==0)break;}if(i==7&&j==7)//noplacetomovereturnevalue();longa=alpha,b=beta;longcurrent,best;boolfirst=true,choose=!m;//choose--todecidethelayer,maxorminintset_color;for(i=0;i<=6;i++)for(j=6;j>=0;j--)//怎么限制只找一个{if(grid[i][j]!=0)continue;set_color=(RorY==true&&choose==true||RorY==false&&choose==false)?1:2;grid[i][j]=set_color;memset(sttblack,0,sizeof(sttblack));memset(sttwhite,0,sizeof(sttwhite));Calculate(false,sttblack[0],sttblack[1],sttblack[2],sttblack[3],sttblack[4],sttblack[5],sttblack[6]);//分别计算当前状态Calculate(true,sttwhite[0],sttwhite[1],sttwhite[2],sttwhite[3],sttwhite[4],sttwhite[5],sttwhite[6]);current=alphabeta(a,b,d-1,choose);grid[i][j]=0;memset(sttblack,0,sizeof(sttblack));memset(sttwhite,0,sizeof(sttwhite));Calculate(false,sttblack[0],sttblack[1],sttblack[2],sttblack[3],sttblack[4],sttblack[5],sttblack[6]);//分别计算当前状态Calculate(true,sttwhite[0],sttwhite[1],sttwhite[2],sttwhite[3],sttwhite[4],sttwhite[5],sttwhite[6]);if(first){best=current;first=false;if(d==depth){bestmove[0]=i;bestmove[1]=j;}}if(choose)//MAXNODE{if(current>=b)//betacutreturncurrent;if(best<current){best=current;a=current;if(d==depth){bestmove[0]=i;bestmove[1]=j;}}}else{//MINNODEif(current<=a)//alphacutreturncurrent;if(best>current){best=current;b=current;if(d==depth){bestmove[0]=i;bestmove[1]=j;}}}break;}if(d==depth){return1;}returnbest;}7.Calculate()函数设计7.1该函数主要是统计当前棋局的状态,包括一个棋子一边被堵d1c、一个棋子两边都没被堵a1c、两个棋子连在一起一边被堵d2c、两个棋子连在一起两边都未堵堵d2c7.2可以从横竖左斜右斜四个方面分析统计例如横着方向实现如下://************************横有方向*************************//for(y=6;y>=0;y--){for(x=0;x<=6;x++){if(grid[x][y]==c){if(x==0)//左边边界for(i=x+1;i<=6;i++){if(grid[i][y]==c)continue;if(grid[i][y]==c0)break;if(grid[i][y]==0){if(i-x==1)d1c++;if(i-x==2)d2c++;if(i-x==3)d3c++;if(i-x==4)d4c++;break;}}elseif(x!=0&&x<6)//左右都不是边界{for(i=x+1;i<=6;i++){if(grid[i][y]==c)continue;if(grid[i][y]==c0)//右边为对手{if(grid[x-1][y]==0){if(i-x==1)d1c++;if(i-x==2)d2c++;if(i-x==3)d3c++;if(i-x==4)d4c++;break;}elsebreak;}if(grid[i][y]==0)//右边为0{if(grid[x-1][y]==0)//在右边为0左边为0{if(i-x==1)a1c++;if(i-x==2)a2c++;if(i-x==3)a3c++;if(i-x==4)d4c++;break;}elseif(grid[x-1][y]==c0)//在右边为0左边为对手{if(i-x==1)d1c++;if(i-x==2)d2c++;if(i-x==3)d3c++;if(i-x==4)d4c++;break;}}}elseif(x==6){if(grid[x-1][y]==c0)break;elsed1c++;break;}x=i;}}}8.evalue()函数设计所谓评估函数,就是要建立一个函数,对棋盘上每一个空点进行计算,得出哪个点才是最优的。这是一个看似简单,其实很难的问题。参照五子棋评估函数,我将棋局分类如下:活一、死一、活二、死二、活三、死三、死四。活二是指当前棋局两个子连在一起且两端都是活路的总数,死二是指当前棋局两个子连在一起且有一端是死路的总数,以此类推。然后给每种棋型一个权重,棋型乘以权重加和的结果就是棋型的分数。我的权重分配改变过很多次,目前依然没找到最合理的分配。#defineFOUR20000000#defineALIVE_31000000TOC\o"1-5"\h\z#defineDEAD_310000#defineALIVE_28000#defineDEAD_250#defineALIVE_130#defineDEAD_110评估函数如下:longCMYSIZIQIView::evalue(){longvalue=sttblack[0]*FOUR+sttblack[1]*ALIVE_3+sttblack[2]*DEAD_3+sttblack[3]*ALIVE_2+sttblack[4]*DEAD_2+sttblack[5]*ALIVE_1+sttblack[6]*DEAD_1-sttwhite[0]*FOUR-sttwhite[1]*ALIVE_3-sttwhite[2]*DEAD_3-sttwhite[3]*ALIVE_2-sttwhite[4]*DEAD_2-sttwhite[5]*ALIVE_1-sttwhite[6]*DEAD_1;//if(bkorwt)value=value*(-1);returnvalue;}四、调试分析界面调试1.1棋盘背景和区域绘制出错

分析出错原因:我在opaint函数中画线在ondraw函数中画背景,由于opaint函数会先调用ondraw函数,所以画完再画棋格原来的背景被刷新了。棋格绘制出错原因很简单,范围限制的不正确,再次计算做了调整棋盘显示正常如下:1.2刷新函数出错HB-C•OO•

出错原因分析:由于四子棋规则是每个棋子只能落在当前一列的最下面一个空白处,而刷新函数我设置的的是鼠标获取位置的小范围内。修改方案:只要将刷新范围调到足以包含整个棋盘就好。果然调试后结果如下:二人对战调试2.1judge()函数的纠结根据设计思路我开始着手该函数的编写,但是编写过程中发现每一个方向并不能只沿一个方向讨论。例如,横方向上当落下一个棋子时既要判断其左边同类棋子数又要讨论其右边同类棋子数,而且需要用一个变量进行累加。这就需要有多重if语句。刚开始考虑不周全出现误判情况:黄子应该赢了但是没有判赢。原因分析:对于一颗棋子没有从两个的方向考虑的同类棋子个数。经过修改完善后解决问题。

2.2为了醒目,我想实现当赢棋时赢家的四颗棋子特别显示。思想就是当四颗连在一起时将其grid[i][j]值付为3,就是用另一种画刷来画,而且将其大小绘大。即是以下效果:2.3实现2.2过程中出现错误:当内部记录四子连线时,界面没有实现将四颗棋子都给于醒目处理。原因分析:

还是记录出错,例如上面那种情况最后一颗放得是第二颗棋子但是标志位只记录了其右边的两颗棋子。该问题虽然花了很长时间来解决,但是它没有太多的技术含量,只要不断调试优化即可。问题解决:人机对战调试3.1alphabeta()函数的纠结在如何实现剪枝这一处我是绞尽了脑汁,程序调试了有上百遍错误千奇百怪离奇不堪,还好我坚持住了。在饶芳同学的帮助下,最终完成了针对四子棋的剪枝函数。以下列举部分错误:ICADucuincitlL.EidEel103I695MHVSI2[QI\HVSI2[QlUieiJ.cpp(|J|9);LF^rnint]CADuluuil'iiLl.diidSirlLiiiijbyfidiiiiii^LrdLurV^^.20091B01695MHVSIZ[QIM1VSIZ[[HUiuiJ.upp(6Q2);urrurC26D1c:\ooc"血mt5andsettingsinuMnistrator、桌面\fe>£2O0?ie(nfi95\iws[ZiQi\fflS[Ziqiuiew.cpp(7e5):errorC2oeil::\l)riciimFnrGnnrtSnrrinq-n\Ar1nini/rljI/rI]IUipij.nnnU:f.it.HprrrihErrorexecutiogcl.eMB.CreatinghraifseimFoFile„..BSCMAKE:errorHKTE06:cannotopenFile1^Mebug^lWSl^IQlUiew^sbr-:Ho>suchFileordirectoryErrDrexecutiiragbsciridkE?aexeatwslzlQl.exe-耳errcr(s)ff12rarninofs)Lew.cpp(149)iew,cpp(1ii9)Lew.cpp(149)iew,cpp(1ii9)iew.cpp(149)iei>i.cpp(fi09)■truk■i-L■ir-T■u■u■n-〜vv*l■■■■<u■■■uu>u■kv:earningC4^244:1argumentB:conversionFrom'double'to'intB,二earningC牛24耳二'argunent1:ccnversionfron'double'to'intB,:uamiriyC424^:"argument1:conversionfromBdoubleBto'intF,trrorC24U7:missingFunctionHeader(oia-stylefornallist?]|JU_>_M_LBJpO551bpO551bpU551b其中有很多LINK错误是以前没有见过的,就是解决了我也还不知道如何就不再出错的。总之,只要我每一步写的合情合理错误就不会那么奇怪,倘若稀里糊涂写出来,出错的可能性将很大。3.2Calculate()函数的纠结Calculate()函数的编写与调试整整花了一天的时间,刚开始真的无从下手,连思路都没有。后来想到自己judge()函数的编写突然有了想法。那就是依然从四个方向进行统计棋局。但是每一个方向又会有多宗情况:例如横方向,搜索时要分a.连在一起的棋子左端是边界的情况,在a情况下在考虑其右边是对手棋子还是空位;b.左端右端都不是边界的情况,在b情况下要讨论四种情

况;c.右端是边界的情况,在c情况下要讨论左端两种情况。虽然很复杂,但是最难的是第一步,当完成了一个方向的之后其他方向的就迎刃而解了。alphabeta()和Calculate()函数完成后运行出错(黄色是机器):机器只会从第一列走棋,这显然是alphabeta()函数运行出错,调试分析后发现错误原因是:在alphabeta()中每假设放完一个棋子也需要统计棋局情况,即也需要调用Calculate。函数。调用后机器不在放错:.OOO••O.O••O五、总结与收获通过此次课程设计,我的收获很大。不但对博弈树搜索中alphabeta。剪枝算法掌握的更加透彻,而且动手编程的能力得到大大提高,尤其是独立编写算法的能力。在此次实践过程中,我遇到了很多的困难,很多次都让我痛苦不堪,但是不管结果是不是最好,我是自己一步一步实现了我的四子棋游戏。掌握了每一个细节才是我的目的,掌握统筹思想,掌握发现问题解决问题的处事方式才是我最大的收获。这次动手让我明白,在我们专业领域要勇于挑战,坚信自己一定能够将所学知识运用到实践。要有恒心和毅力,每一次尝试都可能会遇到困难,但是要坚持到底,走不下去可以去问人,但是不可以半途而废。我相信这次编程遇到的困难整体的流程将会给我在以后的编程中很大的帮助。对于目前该程序的缺陷,例如,想绘出几个人性化按钮还没有加上去,评估函数的设计很不合理导致机器搜索很差,但是有了框架和思路我将会不断完善下去。六、后期展望网络对战二人对战我将会实现网络化,之前已经看过一些网络编程的资料,觉得花时间可以实现,我自己也期待着亲手实现“我的四子棋”的网络化。优化算法人机对弈算法有待优化,而且每种算法对于不同棋类都会有特例,根据特例进行优化将可能大大节约空间提高效率。七、重要源程序下面贴出害死我很多脑细胞的三个函数源程序清单,但是还得感谢它们让我收获很大:voidCMYSIZIQIView::Calculate(boolcolor,int&d4c,int&a3c,int&d3c,int&a2c,int&d2c,int&a1c,int&d1c){intc=(color==true)?2:1;intc0=(color==true)?1:2;d4c=0;a3c=0;d3c=0;a2c=0;d2c=0;a1c=0;d1c=0intx,y,i,j;vt"-+<!?-•—-r_*1.1II//************************横有方向*************************//for(y=6;y>=0;y--){for(x=0;x<=6;x++){if(grid[x][y]==c){if(x==0)//左边边界for(i=x+1;i<=6;i++){if(grid[i][y]==c)continue;if(grid[i][y]==c0){break;}if(grid[i][y]==0){if(i-x==1)d1c++;if(i-x==2)d2c++;if(i-x==3)d3c++;if(i-x==4)d4c++;break;}}elseif(x!=0&&x<6)//左右都不是边界{for(i=x+1;i<=6;i++){if(grid[i][y]==c)continue;if(grid[i][y]==c0)//右边为对手{if(grid[x-1][y]==0){if(i-x==1)d1c++;if(i-x==2)d2c++;if(i-x==3)d3c++;if(i-x==4)d4c++;break;}elsebreak;}if(grid[i][y]==0)//右边为0{if(grid[x-1][y]==0)//在右边为0左边为0{if(i-x==1)a1c++;if(i-x==2)a2c++;if(i-x==3)a3c++;if(i-x==4)d4c++;break;}elseif(grid[x-1][y]==c0)//在右边为0左边为对手{if(i-x==1)d1c++;if(i-x==2)d2c++;if(i-x==3)d3c++;if(i-x==4)d4c++;break;}}}}elseif(x==6){if(grid[x-1][y]==c0)break;else{d1c++;break;}}x=i;}"r^—t—————————————————————————————//************************竖有力向******************************//for(x=0;x<7;x++)for(y=6;y>=0;y--){if(grid[x][y]==c){if(y==6){for(j=j-1;j>=0;j--){if(grid[x][j]==c)continue;if(grid[x][j]==c0){break;}if(grid[x][j]==0){if(y-j==1)d1c++;if(y-j==2)d2c++;if(y-j==3)d3c++;if(y-j==4)d4c++;break;}}}elseif(y!=6&&y!=0)//非上下边界{for(j=y-1;j>=0;j--){if(grid[x][j]==c)continue;if(grid[x][j]==c0)〃上边为对手break;if(grid[x][j]==0)//右边为0{if(y-j==1)d1c++;if(y-j==2)d2c++;if(y-j==3)d3c++;if(y-j==4)d4c++;break;}}y=j;}}〃************************左下-右上斜着方向******************************〃for(x=0;x<=6;x++)for(y=6;y>=0;y--){if(grid[x][y]==c){if(y==6||x==0)//左下方有边界{for(i=x+1,j=y-1;j>=0,i<=6;i++,j--){if(grid[i][j]==c)continue;if(grid[i][j]==c0)//右上方是对方breakif(grid[i][j]==0)//右上方是0{if(y-j==1)d1c++;if(y-j==2)d2c++;if(y-j==3)d3c++;if(y-j==4)d4c++;break;}}}if(y!=6&&y!=0&&x!=0&&x!=6)//非上下边界{for(i=x+1,j=y-1;j>=0,i<=6;i++,j--){if(grid[i][j]==c)continue;if(grid[i][j]==c0)〃上边为对手{if(grid[x-1][y-1]==0)//右上边为对手时左下方为o{if(y-j==1)d1c++;if(y-j==2)d2c++;if(y-j==3)d3c++;if(y-j==4)d4c++;break;}elseif(grid[x-1][y-1]==c0)break;//右上边为对手时左下方为对手}if(grid[i][j]==0)//右上边为0{if(grid[x-1][y-1]==0)//右上边为0时左下方为0{if(y-j==1)a1c++;if(y-j==2)a2c++;if(y-j==3)a3c++;if(y-j==4)d4c++;break;}elseif(grid[x-1][y-1]==c0)//右上边为0时左下方为对手{if(y-j==1)d1c++;if(y-j==2)d2c++;if(y-j==3)d3c++;if(y-j==4)d4c++;break;}}}}elseif(y==0||x==6){if(grid[x-1][y-1]==0){d1c++;break;}elsebreak;}x=i;y=j;}}〃***********************右下-左上斜着方向******************************//for(x=6;x>=0;x--)for(y=6;y>=0;y--){if(grid[x][y]==c){if(y==6||x==6)//右下方有边界{for(i=x-1,j=y-1;j>=0,i>=0;i--,j--){if(grid[i][j]==c)continue;if(grid[i][j]==c0)//左上方是对方{break;}if(grid[i][j]==0)//左上方是0{if(y-j==1)d1c++;if(y-j==2)d2c++;if(y-j==3)d3c++;if(y-j==4)d4c++;break;}}}if(y!=6&&y!=0&&x!=0&&x!=6)//非上下边界{for(i=x-1,j=y-1;j>=0,i>=0;i--,j--){if(grid[i][j]==c)continue;if(grid[i][j]==c0)〃上边为对手{if(grid[x-1][y-1]==0)//左上方为对手时右下方为0{if(y-j==1)d1c++;if(y-j==2)d2c++;if(y-j==3)d3c++;if(y-j==4)d4c++;break;}elseif(grid[x-1][y-1]==c0)break;//左上方为对手时右下方为对手}if(grid[i][j]==0)//左上边为0{if(grid[x-1][y-1]==0)//左上边为0时右下方为0{if(y-j==1)a1c++;if(y-j==2)a2c++;if(y-j==3)a3c++;if(y-j==4)d4c++;break;}elseif(grid[x-1][y-1]==c0)//左上边为0时右下方为对手{if(y-j==1)d1c++;if(y-j==2)d2c++;if(y-j==3)d3c++;if(y-j==4)d4c++;break;}}}}elseif(y==0||x==0)//左上方为边界{if(grid[x-1][y-1]==0)/佐上方为边界时右下方为0{d1c++;break;}elsebreak;}x=i;y=j;}}longCMYSIZIQIView::alphabeta(longalpha,longbeta,intd,boolm){inti,j;if(d==0)//thedepthofsearchisenoughreturnevalue();for(i=0;i<7;i++)//searchfortheblank{for(j=0;j<7;j++)if(grid[i][j]==0)break;if(grid[i][j]==0)break;}if(i==7&&j==7)//noplacetomovereturnevalue();longa=alpha,b=beta;longcurrent,best;boolfirst=true,choose=!m;//choose--todecidethelayer,maxorminintset_color;for(i=0;i<=6;i++)for(j=6;j>=0;j--)//怎么限制只找一个{if(grid[i][j]!=0)continue;set_color=(RorY==true&&choose==truellRorY==false&&choose==false)?1:2;grid[i][j]=set_color;memset(sttblack,0,sizeof(sttblack));memset(sttwhite,0,sizeof(sttwhite));Calculate(false,sttblack[0],sttblack[1],sttblack[2],sttblack[3],sttblack[4],sttblack[5],sttblack[6]);//分别计算当前状态Calculate(true,sttwhite[0],sttwhite[1],sttwhite[2],sttwhite[3],sttwhite[4],sttwhite[5],sttwhite[6]);current=alphabeta(a,b,d-1,choose);grid[i][j]=0;memset(sttblack,0,sizeof(sttblack));memset(sttwhite,0,sizeof(sttwhite));Calculate(false,sttblack[0],sttblack[1],sttblack[2],sttblack[3],sttblack[4],sttblack[5],sttblack[6]);//分别计算当前状态Calculate(true,sttwhite[0],sttwhite[1],sttwhite[2],sttwhite[3],sttwhite[4],sttwhite[5],sttwhite[6]);if(first){best=current;first=false;if(d==depth){bestmove[0]=i;bestmove[1]=j;}}if(choose)//MAXNODE{if(current>=b)//betacutreturncurrent;if(best<current){best=current;a=current;if(d==depth){bestmove[0]=i;bestmove[1]=j;}}}else{//MINNODEif(current<=a)//alphacutreturncurrent;if(best>current){best=current;b=current;if(d==depth){bestmove[0]=i;bestmove[1]=j;}}}break;}if(d==depth){return1;}returnbest;

boolCMYSIZIQIView::judge(intcolor,intx,inty){inti,j;intt0;〃**********************横向判断赢**************************〃intHWIN=1;for(i=x-1;i>=0;i--)//&&&&&&&&&棋子左边{if(grid[i][y]==color)HWIN++;if(grid[i][y]!=color)break;}if(HWIN>=4&&color==1)//左边可能已经三个棋子{for(i=x-1;i>=0;i--)//一方赢了则改变其颜色if(grid[i][y]==color){grid[i][y]=3;}grid[x][y]=3;MessageBox("红赢");reset();Invalidate();returntrue;}elseif(HWIN>=4&&color==2){for(i=x-1;i>=0;i--)//一方赢了则改变其颜色if(grid[i][y]==color){grid[i][y]=3;}grid[x][y]=3;MessageBox("黄赢");reset();Invalidate();returntrue;}t0=HWIN;for(i=x+1;i<=6;i++)//&&&&&&&&&&&&棋子右边{//MessageBox("红赢");if(grid[i][y]==color)HWIN++;}if(HWIN>=4&&color==1){for(i=x+1;i<=6;i++)//一方赢了则改变其颜色if(grid[i][y]==color){grid[i][y]=3;}grid[x][y]=3;if(HWIN-t0==2)grid[x-1][y]=3;if(HWIN-t0==1){grid[x-1][y]=3;grid[x-2][y]=3;}MessageBox("红赢");reset();Invalidate();returntrue;}elseif(HWIN>=4&&color==2)if(grid[i][y]!=color)break;

{for(i=x+1;i<=6;i++)//一方赢了则改变其颜色if(grid[i][y]==color){grid[i][y]=3;}grid[x][y]=3;if(HWIN-t0==2)grid[x-1][y]=3;if(HWIN-t0==1){grid[x-1][y]=3;grid[x-2][y]=3;}MessageBox("黄赢”);reset();Invalidate();//elsereturnfalse;returntrue;〃***********************竖向判断***************************//intSWIN=1;for(j=y+1;j<=6;j++)//&&&&&&&&&棋子下边{//MessageBox("红赢”);if(grid[x][j]==color)SWIN++;if(grid[x][j]!=color)break;}if(SWIN>=4&&color==1)//下边可能已经三个棋子{for(j=y+1;j<=6;j++)//一方赢了则改变其颜色if(grid[x][j]==color&&j-y<=3){grid[x][j]=3;}grid[x][y]=3;MessageBox("红赢”);reset();Invalidate();returntrue;}elseif(SWIN>=4&&color==2){for(j=y+1;j<=6;j++)//一方赢了则改变其颜色if(grid[x][j]==color&&j-y<=3){grid[x][j]=3;}grid[x][y]=3;MessageBox("黄赢”);reset();Invalidate();returntrue;}}if(HWIN>=4&&color==1){for(i=x+1;i<=6;i++)//一方赢了则改变其颜色if(grid[i][y]==color){grid[i][y]=3;}grid[x][y]=3;if(HWIN-t0==2)grid[x-1][y]=3;if(HWIN-t0==1){grid[x-1][y]=3;grid[x-2][y]=3;}MessageBox("红赢");reset();Invalidate();returntrue;}elseif(HWIN>=4&&color==2)//elsereturnfalse;//////////////下面屏蔽掉是因为棋子当下一个棋子时其上方是不会有棋子的/*for(j=y-1;y>=0;j--)//&&&&&&&&&&&&棋子上边{//MessageBox("红赢”);if(grid[x][j]==color)SWIN++;if(grid[x][j]!=color)break;}if(SWIN>=4&&color==1){for(j=y-1;y>=0;j--)//一方赢了则改变其颜色if(grid[x][j]==color&&lj-yl<=3)

grid[x][j]=3;}grid[x][y]=3;MessageBox("红赢”);reset();Invalidate();returntrue;}elseif(SWIN>=4&&color==2){MessageBox("黄赢”);reset();

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论