各位大佬。。。。我在解决老师布置的作业——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;//返回没有威胁 } //电脑下棋-----------------------------------------------------------------------
下面是我运行时的一些截图: