五子棋算法问题(求解)

Posted jankin-lee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了五子棋算法问题(求解)相关的知识,希望对你有一定的参考价值。

各位大佬。。。。我在解决老师布置的作业——Qt实现五子棋游戏中,出现了一些问题,我独自想了几天也无法解决,便求助一下大神们。
因为我们都是新手,也才接触Qt,于是老师让我们一个小组完成一个任务,我们的任务也就是五子棋。
在我负责写的AI算法的中,出现了一些问题。我先简单介绍一下我大概的想法:
  因为是采用打分制,首先获取每个空位周围的棋子信息,并存入信息数组(ChessInfo)。
  通过空位周围的信息,判断预落子在这个空位后,能够构成多少个同样的子相连,记为count数。
  通过count数和周围子的信息,判断棋型(5连啊,活4啊,活3啊那些),并根据棋型给分。
  根据给分的高低下棋。
之后便是我没能实现的地方,我有两个分值数组,分别是当前棋子和敌对棋子形势下的分数(myScore和hisScore)。我想通过这两个存储分值的数组判断是进攻还是防守:(其中用到了一个myMaxScore和hisMaxScore变量,分别代表两个数组中的最大值)
  如果myMaxScore>=hisMaxScore,则进攻,下我方形势最大值myMaxScore对应的位置;如果有多个myMaxScore相等,则下这几个对应位置上hisMaxScore最大的位置。
  否则,防守,下敌方形势最大值hisMaxScore对应的位置。如果有多个hisMaxScore相等,则下这几个对应位置上myMaxScore最大的位置。
我的AI在连成4颗子后,被堵住一方,就不再在另一方能连成5颗子的地方下,另外,我的AI也无法堵住对方的棋子,下在奇奇怪怪的地方,,,,
请哪位大神指教一下,,,或者哪里有我没想到的错误,请提醒我一下,小弟在此先谢过了。。。。(因为老师给我们的绿色版的Qt,没有调试器,我之前尝试配置调试器的时候还把电脑弄出问题,结果重新装了系统。而我们这个项目又是在老师给的这个绿色版的Qt上运行的,所以我下载新的Qt之后,或者时候Visual Studio 2017的时候还是没法调试,最后我。。。只能一句一句的写qDebug()来调试了,真的。。。心塞。)

下面是我的代码:
#define NO_THREAT 0
#define BLANK 0
#define WIN5 100000
#define ALIVE4 20000
#define ALIVE3 4500
#define DIE4 1000
#define ALIVE2 200
#define DIE3 45
#define DIE2 10
(分值我是随意定的。。。只考虑了不让四个方向上的分数加起来超过更高一级的棋型。。)
//电脑下棋---------------------------------------------------------------------------
void Chessboard::computerPlaceChess(int myChess){
    //判断棋型(暂时考虑五连,活四,冲四,活三,死三,活二,死二,其他棋型以及禁手后续扩充)
    int dx[8]={-1,-1,0,1,1,1,0,-1};
    int dy[8]={0,1,1,1,0,-1,-1,-1};
    int myScore[15][15];//创建两个数组存储对应两方的空位分数
    int hisScore[15][15];
    int countOfBlank=0;
    int hisChess=myChess==1?2:1;//黑子为1,白子为2
    //循环查找每一位
    for(int i=0;i<15;i++){
        for(int j=0;j<15;j++){
            if(chess[i+1][j+1]==0){//空位个数++
                countOfBlank++;
            }else{//非空给分,并直接跳
                hisScore[i][j]=0;
                myScore[i][j]=0;
                continue;
            }
            //在4条直线方向,获取预置点两端的8个点的棋盘信息
            int step1=0,r=1,c=1;//step代表直线,r,c分别代表加上偏移量之后的坐标
            int count=1,myCount=1,hisCount=1;//与中心点相连接的同色子的个数,myCount用于myScore,hisCount用于hisScore
            int chessInfo[4][8];//定义4个存储每条直线上的,预置点附近点的信息.一维0,1,2,3分别为竖直,k=1,水平,k=-1的直线.
            //存储方式:如水平线上,预置点左边3个与右边3个,如chess[2][8]={左3,左2,左1,左,右,右1,右2,右3},其他同理.
            //存储信息并判断分数

            for(step1;step1<4;step1++) {


                for(int changeCount=0;changeCount!=2;changeCount++){
                    int dir = step1;
                    int nowChess;
                    if(changeCount==0){//改变判断棋子的角度
                        nowChess=myChess;
                        //qDebug()<<__LINE__<<nowChess;
                    }else{
                        nowChess=myChess==1?2:1;
                        //qDebug()<<__LINE__<<nowChess;
                    }
                    for(int k=0;k<4;k++){//每个方向取4个子的信息
                        if(k==0){
                            switch(step1){//前四个方向的初始值,通过基点位置加偏移量的方式,获取附近的点信息
                            case 0:
                                r=i-1+1;//由于写框架的同学使用的16*16的棋盘,而我的i,j都从0开始,并且有15*15的计分数组使用了i,j,所以在使用到期盼的地方都添加+1操作
                                c=j+1;
                            case 1:
                                r=i-1+1;
                                c=j+1+1;
                            case 2:
                                r=i+1;
                                c=j+1+1;
                            case 3:
                                r=i+1+1;
                                c=j+1+1;
                            }
                        }
                        if(r<1 || c<1 || r>15  || c>15){
                            break;
                        }
                        if(chess[r][c]==nowChess){
                            count++;
                            chessInfo[step1][3-k]=nowChess;
                            r+=dx[dir];
                            c+=dy[dir];
                        }else{
                            chessInfo[step1][3-k]=chess[r][c];
                        }
                    }
                    for(int k=0;k<4;k++){
                        dir+=4;
                        if(k==0){
                            switch(step1){//后四个方向的初始值
                            case 0:
                                r=i+1+1;
                                c=j+1;
                            case 1:
                                r=i+1+1;
                                c=j-1+1;
                            case 2:
                                r=i+1;
                                c=j-1+1;
                            case 3:
                                r=i-1+1;
                                c=j-1+1;
                            }
                        }
                        if(r<1 || c<1 || r>15  || c>15){
                            break;
                        }
                        if(chess[r][c]==nowChess){
                            count++;
                            chessInfo[step1][k+4]=nowChess;
                            r+=dx[dir];
                            c+=dy[dir];
                        }else{
                            chessInfo[step1][k+4]=chess[r][c];
                        }
                    }

                    myCount=changeCount==0?count:myCount;
                    hisCount=changeCount==1?count:hisCount;
                }
                //判断分数
                //qDebug()<<__LINE__<<"my"<<myCount;
                //qDebug()<<__LINE__<<"his"<<hisCount;
                myScore[i][j]=evaluateBlank(valueOfBlank(chessInfo[0],myCount,myChess)+valueOfBlank(chessInfo[1],myCount,myChess)+valueOfBlank(chessInfo[2],myCount,myChess)+valueOfBlank(chessInfo[3],myCount,myChess));
                //qDebug()<<__LINE__<<"my"<<myCount;
                hisScore[i][j]=evaluateBlank(valueOfBlank(chessInfo[0],hisCount,hisChess)+valueOfBlank(chessInfo[1],hisCount,hisChess)+valueOfBlank(chessInfo[2],hisCount,hisChess)+valueOfBlank(chessInfo[3],hisCount,hisChess));
                //qDebug()<<__LINE__<<"his"<<hisCount;
            }
        }
    }
    /*for(int i=0;i<15;i++){
        for(int j=0;j<15;j++){
            qDebug()<<__LINE__<<myScore[i][j];
        }
    }*/
    //下棋
    int myMaxScore=0,hisMaxScore=0;
    QList<int> row1;//存储myScore中最高分坐标(一个或多个)
    QList<int> col1;
    QList<int> row2;//存储hisScore中最高分坐标(一个或多个)
    QList<int> col2;
    int countOfMax1=0;//myScore最大值个数
    int countOfMax2=0;//hisScore最大值个数
    int myTemp=0,hisTemp=0;//存储对应一方分数最高时,另一方的最高分
    int myIndex=0,hisIndex=0;//应该落子的最大值所在的索引
    myMaxScore=max(myScore[0],row1,col1,countOfMax1);
    hisMaxScore=max(hisScore[0],row2,col2,countOfMax2);
    if(countOfBlank==225){//场上没有棋子
        placeChess(8,8);//下在中间
    }else if(myMaxScore>hisMaxScore){//如果自己最高分数大于对方,则进攻
        qDebug()<<__LINE__<<"JinGong";
        if(countOfMax1>1){//如果自己最高分数不止一个,选择对方分数最高的地方下
            qDebug()<<__LINE__<<"DiYi";
            for(int i=1;i!=countOfMax1;i++){
                if(hisTemp<=hisScore[row1.at(i-1)][col1.at(i-1)]){
                    hisTemp=hisScore[row1.at(i-1)][col1.at(i-1)];
                    myIndex=i-1;
                }
            }
            //qDebug()<<__LINE__<<row1.at(myIndex)+1<<col1.at(myIndex);//代码没有运行到这儿,条件判断有问题?
            placeChess(row1.at(myIndex)+1,col1.at(myIndex)+1);
        }else{
            qDebug()<<__LINE__<<"DiEr";
            placeChess(row1.at(0)+1,col1.at(0)+1);
        }
    }else{//如果自己最高分数小于对方,则防守
        qDebug()<<__LINE__<<"FangShou";
        if(countOfMax2>1){//如果对方最高分数不止一个,选择我方分数最高的地方下
            qDebug()<<__LINE__<<"DiYi";
            for(int j=1;j!=countOfMax2;j++){
                if(myTemp<=myScore[row2.at(j-1)][col2.at(j-1)]){
                    myTemp=myScore[row2.at(j-1)][col2.at(j-1)];
                    hisIndex=j-1;
                }
            }
            //qDebug()<<__LINE__<<row2.at(hisIndex)+1<<col2.at(hisIndex)+1;//代码没有运行到这儿,条件判断有问题?
            placeChess(row2.at(hisIndex)+1,col2.at(hisIndex)+1);
        }else{
            qDebug()<<__LINE__<<"DiEr";
            placeChess(row2.at(0)+1,col2.at(0)+1);
        }
    }
}
int Chessboard::evaluateBlank(int value) {//给出最终分数
    //qDebug()<<"evaluateBlank";
    if(value/100000>=1){//有一个及以上的五连
        return Level_1;
    }else if(value/20000>1){//有一个以上的活四
        return Level_2;
    }else if(value/20000==1){//有一个活四
        return Level_3;
    }else if(value/4500>2){//有两个以上的活三
        return Level_4;
    }else if(value/4500==2){//有两个活三
        return Level_5;
    }else if(value/4500==1){//有一个活三
        return Level_6;
    }else if(value/1000>2){//有两个以上的死四
        return Level_7;
    }else if(value/1000==2){//有两个死四
        return Level_8;
    }else if(value/1000==1){//有一个死四
        return Level_9;
    }else if(value/200>2){//有两个以上的活二
        return Level_10;
    }else if(value/200==2){//有两个活二
        return Level_11;
    }else if(value/200==1){//有一个活二
        return Level_12;
    }else if(value/45>2){//有两个以的死三
        return Level_13;
    }else if(value/45==2){//有两个死三
        return Level_14;
    }else if(value/45==1){//有一个死三
        return Level_15;
    }else if(value/10>1){//有一个以上的死二
        return Level_16;
    }else if(value/10==1){//有一个死二
        return Level_17;
    }else {
        return Level_18;
    }
    return 0;
}

int Chessboard::max(const int *score,QList<int> &row,QList<int> &col,int &countOfMax){//获取二位数组最大值并存储落子坐标,修改最大值个数
    //qDebug()<<"max";
    int temp=*score;
    row.append(0);
    col.append(0);
    for(int i=0;i<15;i++){
        for(int j=0;j<15;j++){
            if(temp<=*(score+(i*15+j))){
                if(temp==*(score+(i*15+j))){
                    countOfMax++;
                }else{
                    countOfMax=1;//若最大值改变,最大值个数更新为1
                    while ( !row.isEmpty() )//若最大值改变,容器清空
                        row.removeFirst();
                    while ( !col.isEmpty() )
                        col.removeFirst();
                }
                temp=*(score+(i*15+j));
                row.append(i);
                col.append(j);

            }
        }
    }
    return temp;
}

//对一条直线上的空位计算分数,变量名以左,右为例(传入相应数组时,变量代表其他反方向)(待改)
int Chessboard::valueOfBlank(const int *chessBoard,int count,int myChess) {
    //qDebug()<<"valueOfBlank";
    //qDebug()<<__LINE__<<count;
    int hisChess=myChess==1?2:1;
    int left=3;
    int right=4;
    int leftChess=*(chessBoard+left);
    int rightChess=*(chessBoard+right);
    if (count >= 5){//中心线5连
        //qDebug()<<__LINE__<<"WIN5";
        return WIN5;//5连珠
    }

    if (count == 4){//中心线4连
        //qDebug()<<__LINE__<<"count==4";
        if (leftChess == BLANK && rightChess == BLANK)//两边断开位置均空
            return ALIVE4;//活四
        else if (leftChess == hisChess && rightChess == hisChess)//两边断开位置均非空
            return NO_THREAT;//没有威胁
        else if (leftChess == BLANK || rightChess == BLANK)//两边断开位置只有一个空
            return DIE4;//死四
    }

    if (count == 3) {//中心线3连
        //qDebug()<<__LINE__<<"count==3";
        int leftChess1 = *(chessBoard+left-1);
        int rightChess1 = *(chessBoard+right+1);

        if (leftChess == BLANK && rightChess == BLANK)//两边断开位置均空
        {

            if (leftChess1 == hisChess && rightChess1 == hisChess)//均为对手棋子
                return DIE3;
            else if (leftChess1 == myChess || rightChess1 == myChess)//只要一个为自己的棋子
                return DIE4;
            else if (leftChess1 == BLANK || rightChess1 == BLANK)//只要有一个空
                return ALIVE3;

        }
        else if (leftChess == hisChess && rightChess == hisChess)//两边断开位置均非空
        {
            return NO_THREAT;//没有威胁
        }
        else if (leftChess == BLANK || rightChess == BLANK)//两边断开位置只有一个空
        {

            if (leftChess == hisChess) {//左边被对方堵住
                if (rightChess1 == hisChess)//右边也被对方堵住
                    return NO_THREAT;
                if (rightChess1 == BLANK)//右边均空
                    return DIE3;
                if (rightChess1 == myChess)
                    return DIE4;

            }
            if (rightChess == hisChess) {//右边被对方堵住
                if (leftChess1 == hisChess)//左边也被对方堵住
                    return NO_THREAT;
                if (leftChess1 == BLANK)//左边均空
                    return DIE3;
                if (leftChess1 == myChess)//左边还有自己的棋子
                    return DIE4;
            }
        }
    }

    if (count == 2) {//中心线2连
        //qDebug()<<__LINE__<<"count==2";
        int leftChess1 = *(chessBoard+left-1);
        int rightChess1 = *(chessBoard+right+1);
        int leftChess2 = *(chessBoard+left-2);
        int rightChess2 = *(chessBoard+right+2);

        if (leftChess == BLANK && rightChess == BLANK)//两边断开位置均空
        {
            if ((rightChess1 == BLANK && rightChess2 == myChess) ||
                    (leftChess1 == BLANK && leftChess2 == myChess))
                return DIE3;//死3
            else if (leftChess1 == BLANK && rightChess1 == BLANK)
                return ALIVE2;//活2

            if ((rightChess1 == myChess && rightChess2 == hisChess) ||
                    (leftChess1 == myChess && leftChess2 == hisChess))
                return DIE3;//死3

            if ((rightChess1 == myChess && rightChess2 == myChess) ||
                    (leftChess1 == myChess && leftChess2 == myChess))
                return DIE4;//死4

            if ((rightChess1 == myChess && rightChess2 == BLANK) ||
                    (leftChess1 == myChess && leftChess2 == BLANK))
                return ALIVE3;//活3
            //其他情况在下边返回NO_THREAT
        }
        else if (leftChess == hisChess && rightChess == hisChess)//两边断开位置均非空
        {
            return NO_THREAT;
        }
        else if (leftChess == BLANK || rightChess == BLANK)//两边断开位置只有一个空
        {
            if (leftChess == hisChess) {//左边被对方堵住
                if (rightChess1 == hisChess || rightChess2 == hisChess) {//只要有对方的一个棋子
                    return NO_THREAT;//没有威胁
                }
                else if (rightChess1 == BLANK && rightChess2 == BLANK) {//均空
                    return DIE2;//死2
                }
                else if (rightChess1 == myChess && rightChess2 == myChess) {//均为自己的棋子
                    return DIE4;//死4
                }
                else if (rightChess1 == myChess || rightChess2 == myChess) {//只有一个自己的棋子
                    return DIE3;//死3
                }
            }
            if (rightChess == hisChess) {//右边被对方堵住
                if (leftChess1 == hisChess || leftChess2 == hisChess) {//只要有对方的一个棋子
                    return NO_THREAT;//没有威胁
                }
                else if (leftChess1 == BLANK && leftChess2 == BLANK) {//均空
                    return DIE2;//死2
                }
                else if (leftChess1 == myChess && leftChess2 == myChess) {//均为自己的棋子
                    return DIE4;//死4
                }
                else if (leftChess1 == myChess || leftChess2 == myChess) {//只有一个自己的棋子
                    return DIE3;//死3
                }
            }
        }
    }

    if (count == 1) {//中心线1连
        //qDebug()<<__LINE__<<"count==1";
        int leftChess1 = *(chessBoard+left-1);
        int rightChess1 = *(chessBoard+right+1);
        int leftChess2 = *(chessBoard+left-2);
        int rightChess2 = *(chessBoard+right+2);
        int leftChess3 = *(chessBoard+left-3);
        int rightChess3 = *(chessBoard+right+3);

        if (leftChess == BLANK && leftChess1 == myChess &&
                leftChess2 == myChess && leftChess3 == myChess)
            return DIE4;
        if (rightChess == BLANK && rightChess1 == myChess &&
                rightChess2 == myChess && rightChess3 == myChess)
            return DIE4;

        if (leftChess == BLANK && leftChess1 == myChess &&
                leftChess2 == myChess && leftChess3 == BLANK && rightChess == BLANK)
            return ALIVE3;
        if (rightChess == BLANK && rightChess1 == myChess &&
                rightChess2 == myChess && rightChess3 == BLANK && leftChess == BLANK)
            return ALIVE3;

        if (leftChess == BLANK && leftChess1 == myChess &&
                leftChess2 == myChess && leftChess3 == hisChess && rightChess == BLANK)
            return DIE3;
        if (rightChess == BLANK && rightChess1 == myChess &&
                rightChess2 == myChess && rightChess3 == hisChess && leftChess == BLANK)
            return DIE3;

        if (leftChess == BLANK && leftChess1 == BLANK &&
                leftChess2 == myChess && leftChess3 == myChess)
            return DIE3;
        if (rightChess == BLANK && rightChess1 == BLANK &&
                rightChess2 == myChess && rightChess3 == myChess)
            return DIE3;

        if (leftChess == BLANK && leftChess1 == myChess &&
                leftChess2 == BLANK && leftChess3 == myChess)
            return DIE3;
        if (rightChess == BLANK && rightChess1 == myChess &&
                rightChess2 == BLANK && rightChess3 == myChess)
            return DIE3;

        if (leftChess == BLANK && leftChess1 == myChess &&
                leftChess2 == BLANK && leftChess3 == BLANK && rightChess == BLANK)
            return ALIVE2;
        if (rightChess == BLANK && rightChess1 == myChess &&
                rightChess2 == BLANK && rightChess3 == BLANK && leftChess == BLANK)
            return ALIVE2;

        if (leftChess == BLANK && leftChess1 == BLANK &&
                leftChess2 == myChess && leftChess3 == BLANK && rightChess == BLANK)
            return ALIVE2;
        if (rightChess == BLANK && rightChess1 == BLANK &&
                rightChess2 == myChess && rightChess3 == BLANK && leftChess == BLANK)
            return ALIVE2;

        //其余在下边返回没有威胁

    }
    //qDebug()<<__LINE__<<"NoThreat";
    return NO_THREAT;//返回没有威胁

}
//电脑下棋-----------------------------------------------------------------------

 

下面是我运行时的一些截图:

技术分享图片技术分享图片
















以上是关于五子棋算法问题(求解)的主要内容,如果未能解决你的问题,请参考以下文章

五子棋AI算法-Zobrist

五子棋AI算法-重构代码

五子棋游戏(简单易懂,入门都能学)

[教你做小游戏] 《五子棋》怎么判断输赢?你能5分钟交出代码吗?

详解 C 语言开发五子棋游戏以及游戏中的重要算法与思路

AI五子棋第二篇-运用极大极小值算法书写AI三子棋,可拓展到五子棋(建议收藏)