基于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的五子棋应用所有功能均已实现

详情代码请参照:这里

第一次写那么多篇的博客,请多多指正。