五子棋 初级和中级Ai的实现和扫棋方式

今天我们来完成五子棋初级和中级AI的实现,以及讲解一下扫棋的方式,先来讲一下基本的原理,AI(电脑)是怎么下棋的呢?

首先,我们来了解一下五子棋 的基本棋型

最常见的基本棋型大体有以下几种:连五,活四,冲四,活三,眠三,活二,眠二

①连五:顾名思义,五颗同色棋子连在一起,不需要多讲。
图2-1  

②活四:有两个连五点(即有两个点可以形成五),图中白点即为连五点。
稍微思考一下就能发现活四出现的时候,如果对方单纯过来防守的话,是已经无法阻止自己连五了。
图2-2  


③冲四:有一个连五点,如下面三图,均为冲四棋型。图中白点为连五点。
相对比活四来说,冲四的威胁性就小了很多,因为这个时候,对方只要跟着防守在那个唯一的连五点上,冲四就没法形成连五。
图2-3  图2-4  图2-5  


④活三:可以形成活四的三,如下图,代表两种最基本的活三棋型。图中白点为活四点。
活三棋型是我们进攻中最常见的一种,因为活三之后,如果对方不以理会,将可以下一手将活三变成活四,而我们知道活四是已经无法单纯防守住了。所以,当我们面对活三的时候,需要非常谨慎对待。在自己没有更好的进攻手段的情况下,需要对其进行防守,以防止其形成可怕的活四棋型。
图2-6  图2-7 

其中图2-7中间跳着一格的活三,也可以叫做跳活三

⑤眠三:只能够形成冲四的三,如下各图,分别代表最基础的六种眠三形状。图中白点代表冲四点。眠三的棋型与活三的棋型相比,危险系数下降不少,因为眠三棋型即使不去防守,下一手它也只能形成冲四,而对于单纯的冲四棋型,我们知道,是可以防守住的。
图2-8  图2-9  图2-10 

2-11 图2-12 图2-13 

如上所示,眠三的形状是很丰富的。对于初学者,在下棋过程中,很容易忽略不常见的眠三形状,例如图2-13所示的眠三。

有新手学了活三眠三后,会提出疑问,说活三也可以形成冲四啊,那岂不是也可以叫眠三?
会提出这个问题,说明对眠三定义看得不够仔细:眠三的的定义是,只能够形成冲四的三。而活三可以形成眠三,但也能够形成活四。

此外,在五子棋中,活四棋型比冲四棋型具有更大的优势,所以,我们在既能够形成活四又能够形成冲四时,会选择形成活四。

温馨提示:学会判断一个三到底是活三还是眠三是非常重要的。所以,需要好好体会。
后边禁手判断的时候也会有所应用。 

⑥活二:能够形成活三的二,如下图,是三种基本的活二棋型。图中白点为活三点。
活二棋型看起来似乎很无害,因为他下一手棋才能形成活三,等形成活三,我们再防守也不迟。但其实活二棋型是非常重要的,尤其是在开局阶段,我们形成较多的活二棋型的话,当我们将活二变成活三时,才能够令自己的活三绵绵不绝微风里,让对手防不胜防。
图2-14  图2-15  图2-16 

⑦眠二:能够形成眠三的二。图中四个为最基本的眠二棋型,细心且喜欢思考的同学会根据眠三介绍中的图2-13找到与下列四个基本眠二棋型都不一样的眠二。图中白点为眠三点。
图2-17 图2-18  
图2-19  图2-20 

根据之前的扫棋方式,电脑会进去判定下一步将下的子,形成的棋谱,然后根据棋谱的各种棋型进行打分

下面我把根据这几种图谱的自己做的打分表给大家看一下

Dictionary<string, float> toScore = new Dictionary<string, float>(); //定义一个字典存储打分表

 void Start()
    {
        toScore.Add("aa___", 100);                      //眠二
        toScore.Add("a_a__", 100);
        toScore.Add("___aa", 100);
        toScore.Add("__a_a", 100);
        toScore.Add("a__a_", 100);
        toScore.Add("_a__a", 100);
        toScore.Add("a___a", 100);


        toScore.Add("__aa__", 500);                     //活二 "_aa___"
        toScore.Add("_a_a_", 500);
        toScore.Add("_a__a_", 500);

        toScore.Add("_aa__", 500);
        toScore.Add("__aa_", 500);


        toScore.Add("a_a_a", 1000);                     // bool lfirst = true, lstop,rstop = false  int AllNum = 1
        toScore.Add("aa__a", 1000);
        toScore.Add("_aa_a", 1000);
        toScore.Add("a_aa_", 1000);
        toScore.Add("_a_aa", 1000);
        toScore.Add("aa_a_", 1000);
        toScore.Add("aaa__", 1000);                     //眠三

        toScore.Add("_aa_a_", 9000);                    //跳活三
        toScore.Add("_a_aa_", 9000);

        toScore.Add("_aaa_", 10000);                    //活三      


        toScore.Add("a_aaa", 15000);                    //冲四
        toScore.Add("aaa_a", 15000);                    //冲四
        toScore.Add("_aaaa", 15000);                    //冲四
        toScore.Add("aaaa_", 15000);                    //冲四
        toScore.Add("aa_aa", 15000);                    //冲四        


        toScore.Add("_aaaa_", 1000000);                 //活四

        toScore.Add("aaaaa", float.MaxValue);           //连五

}

//下面是具体的扫棋方式

public override void CheckOneLine(int[]pos,int[]offset,int chess)

{

    int Allnum = 1;

    bool lfrist  true,lstop = false,rstop;

    string str ="a";

    int li =-offset[0],lj=-offset[1];

    int ri = offset[0],rj =offset[1];

    While(Allnum<7||!lstop||!rstop)

    {    
        if(lfirst)
        {
            //左边
            if((pos[0]+li>=0&&pos[0]+li<15)&&

            pos[0]+lj>=0&&pos[0]+lj<15)

            {

                if(ChessBoard.intance.grid [pos[0]+li,pos[0]+lj]== chess)

                {

                Allnum++;

                str+="a";

                }

                else if(ChessBoard.intance.grid[pos[0]+li,pos[0]+lj ==0)

                {

                Allnum++;

                str +="_";

                if(!rstop) lfirst = false;

                }

                else
                {

                lstop = true;

                if(!rstop) lfirst = false;

                }
                li -=offset[0];lj-=offset[1];
            
            }
            else
            {
                lstop = true;

                if(!rstop) lfirst = false;
            }
        }
        else
        {
        //右边
            if((pos[0]+li>=0&&pos[0]+li<15)&&

            pos[0]+lj>=0&&pos[0]+lj<15)

            {

                if(ChessBoard.intance.grid [pos[0]+ri,pos[0]+rj]== chess)

                {

                Allnum++;

                str+="a";

                }

                else if(ChessBoard.intance.grid[pos[0]+ri,pos[0]+rj ==0)

                {

                Allnum++;

                str +="_";

                if(!lstop) lfirst = true;

                }

                else
                {

                rstop = true;

                if(!lstop) lfirst = true;

                }
                ri +=offset[0];rj+=offset[1];
            
            }
            else
            {
                rstop = true;

                if(!lstop) lfirst = true;
            }
        }
        

    }
    string cmpStr ="";
    foreach(var keyInfo in toScore)
    {
        if(str.Contains(keyInfo.key))
        {
            if(cmpStr!=null)
            {
                if(toScore[keyInfo.key]>cmpStr[keyInfo.key])
                {
                    cmpStr[keyInfo.key] = toScore[keyInfo.key];
                }
            }
            else
            {
                cmpStr = keyInfo.key;
            }
        }
    }
    if(cmpStr!=null)
    {
     toScore[pos[i],pos[j]]+=toScore[cmpStr];
    }
    
}

猜你喜欢

转载自blog.csdn.net/weixin_41765385/article/details/80742023