首页 > 代码库 > 人机博弈-吃子棋游戏(三)走法生成

人机博弈-吃子棋游戏(三)走法生成

我们可以根据吃子棋的规则,创建走法生成器,主要的逻辑是,如果己方存在一气的棋串,则可以无需紧对方的气,也就是可以不用贴着对方的棋子落子。其他情况下,必须贴

着对方的棋子落子,考虑到某些特殊情况,当己方能提对方棋子时,己方就可以下到没有气的地方,并且一般的吃子棋,先提子者胜,无法形成劫争。所以走法生成器就相对非

常简单。

对于如何确定己方是不是存在一气的棋串,可以利用上一节介绍的算气算法。

int CMoveGenerator::CreatePossibleMove(BYTE position[GRID_NUM][GRID_NUM], int nPly, int nSide)
{
    m_nMoveCount = 0;
    
    BYTE antiSide = (nSide + 1) % 2;
    cleanGlobal();
    setGo(position);

    
    
    //检测己方是否有一气的棋窜,有则输出相应走法。
    
    for (int i = 0; i < GRID_NUM; i++)
    for (int j = 0; j < GRID_NUM; j++){
        
        if(go[i][j]==nSide&&g_gozi[i][j]==0){
           str_lib(i,j,go[i][j]);
         
           if (goqi==1)
                {
                
                for (int k = 0; k < GRID_NUM; k++)
                for (int w = 0; w < GRID_NUM; w++){

                if (gokong[k][w] == 1){
                AddMove(k, w, nPly);

                }


                }

                
                }
        
        }
       
    }
    64     
    //正常情况下,寻找敌方棋子周边的空位,紧其气

            for (int i = 0; i < GRID_NUM; i++)
            for (int j = 0; j < GRID_NUM; j++)
            {
            

            if (go[i][j] == antiSide)
            {
                
                if (i > 0 && go[i - 1][j] == NOSTONE){
                    
                    AddMove(i - 1, j, nPly);
                    
                    
                }
                    
                if (i < GRID_NUM - 1 && go[i + 1][j] == NOSTONE){
                  
                    AddMove(i + 1, j, nPly);
                    
                }
                    
                if (j>0 && go[i][j - 1] == NOSTONE){
                  
                    AddMove(i, j - 1, nPly);
                    
                    
                }
                    
                if (j < GRID_NUM - 1 && go[i][j + 1] == NOSTONE){
                  
                    AddMove(i, j + 1, nPly);
                   
                }
                    
                
            }
            
            

        }


    
    return m_nMoveCount;
}

可以优化此算法,以后方便后续的搜索引擎进行剪枝。给走法设定一个分数,能够提子则此步设为30+提子数。能够打吃则为20+打吃棋子数。能够长气,则为10+长气的棋子数。其他暂时设计为0。可以用一个额定长度的优先队列,保留几个分数最佳的走法。或是必要时进行排序。

搜索时优先优先搜索得分较高的走法,这样大幅度提高搜索算法剪枝的效率。

这段是走法启发的代码

伪码如下:

for(int i=0;i<Grid_Num;i++)
for(int j=0;j<Grid_Num;j++)
{
    cleanupGlobal();
    if(go[i][j]==NOSTONE)
    {
    bool isVilid=false;
    if(i>0&&go[i-1][j]==antiSide)
    {

       go[i][j]=nSide;

       if(g_gozi[i-1][j]==0)
       {
          isVilid=true;
          str_lib(i-1,j,antiSide);
          switch(goqi){case 0:case 1:....}

       }

    }
     if(i<Grid_Num-1&&go[i+1][j]==antiSide)
     {


       go[i][j]=nSide;


       if(g_gozi[i-1][j]==0)
       {
          isValid=true;
          str_lib(i-1,j,antiSide);
          switch(goqi){case 0:case 1:....}
       }


    }...
         if(isValid)
         {
          sumScore(i,j,nSide);
          addMove(i,j,nSide);
          }
44 


    }
}
std::sort();

这个算法是走法启发,学名为静态启发,而不是历史启发,历史启发并不需要棋类的知识属于动态启发,而走法启发则与棋类知识相关联。



人机博弈-吃子棋游戏(三)走法生成