这个函数用来匹配每一个数组中句型的个数。使用strstr方法,寻找到匹配字符串的位置后count++,然后将寻找的起始字符串向后移,继续寻找,直到strstr返回为NULL
代码如下:
int find_count(const char* temp,const char*partern) { int count1 = 0; int cur = 0; char *now = NULL; while((now = strstr(temp+cur,partern))!=NULL) { cur = now - temp + strlen(partern); count1++; } return count1; } chess_board::chess_board(void)这个是类的构造函数,用于构造这72个数组,行列的数组还比较好构造,但是斜边的数组就不那么好构造了,因为有的斜边少于5个点可以落子,这种斜边不能考虑,如下图红圈部分,四个角都是:这也就是为什么斜边的数组只有21个而不是27个
因此,需要确定坐标系,叫左下角作为坐标原点,使用\(l+r = b\)中的\(b\)来区分不同的数组(这里很难讲清楚,自己体会)
代码如下:
chess_board::chess_board(void) { for(int r=0;r<15;r++) { for(int l=0;l<15;l++) { board[r][l] = \'0\'; rows[r][l] = \'0\'; cols[r][l] = \'0\'; } board[r][15] = \'\0\'; rows[r][15] = \'\0\'; cols[r][15] = \'\0\'; } for(int b=0;b<21;b++)//左下向右上 for(int l=0;l<15;l++) { int r = l+b-10;//b = r+10-l if(l>=0&&l<15&&r>=0&&r<15) edge_lr[b][l] = \'0\'; else edge_lr[b][l] = \'\0\'; } for(int b=0;b<21;b++)//右下向左上 for(int l=0;l<15;l++) { ····· } algorithm_opt = 1; depth = 4;//先遍历两步看看吧 } int chess_board::cal_score(bool is_att)终于来到计算分数的函数了,在这个函数中,由于斜边对应的数组每个的起始不同,需要计算(这里有点复杂),就是要根据\(b\)来推出数组的起始位置,这个每个人的实现方法不同对应的算法也不同,自己想。
该函数遍历每一个数组,找出每一个数组中存在的句型,如果这个句型在我方中存在,那就在总分上加上\(句型的分值*句型的数量\),如果这个句型在对方句型库中存在,那就在总分上减去\(句型的分值*句型的数量\),符合零和游戏的特点。
int chess_board::cal_score(bool is_att) { int score = 0; for(int r=0;r<15;r++)//每一行 { string temp = rows[r]; if(pd_null(temp))//判断是否为空,没有棋子就不要查了 continue; for(int i = 0;i<type_count;i++)//查找每一种棋型 { int count1 = find_count(temp.c_str(),chess_type_all_my[i].ptn.c_str()); if(i==8) count1-= find_count(temp.c_str(),chess_type_all_my[7].ptn.c_str()); if(i==19) count1-= find_count(temp.c_str(),chess_type_all_my[18].ptn.c_str()); score+=count1*chess_type_all_my[i].my_score; int count2 = find_count(temp.c_str(),chess_type_all_opt[i].ptn.c_str()); if(i==8) count2-= find_count(temp.c_str(),chess_type_all_opt[7].ptn.c_str()); if(i==19) count2-= find_count(temp.c_str(),chess_type_all_opt[18].ptn.c_str()); score-=count2*chess_type_all_my[i].opt_score; } } for(int l =0;l<15;l++)//每一列 { ···· } for(int b = 0;b<=20;b++)//左下到右上的斜行 { ···· } for(int b = 0;b<=20;b++)//左上到右下的斜行 { ···· } return score; } 遇到的一些坑:写计算句型数量的函数的时候,每次找到一个句型之后没有跳过匹配的句型,只是简单的将寻找的起始位置加1,这样会遇到重复计算的情况,如:"00AAA0"和"0AAA00"就会重复计算,这样会导致明明只有一个三活却读出了两个。
写计算分值的时候,因为定义的句型中间"00AAA0"和"0AAA00"有时会重复计算,为了避免这个事情发生。以这个为例,在计算"00AAA0"的时候,会先数一下"0AAA00"是否已经被计算过了,如果计算过了,就会将"00AAA0"的数量减一。
一开始将72个数组的更新放在了一个临时的数组当中,每次预测的时候都要复制一遍72个数组,这样降低了遍历的速度,利用深度优先搜索可回溯的特点,动态更新,可以加快搜索的速度。
在对每一个棋型的分值进行评估的时候,将自己的五连珠和对手的五连珠的分值设置的太过接近,导致遍历的时候AI不去管对面即将五连珠的棋型,反而专注于自己的五连珠(但是自己始终比对手慢一步),导致防守失败。因此对手五连珠的分值应该设为自己五连珠分值的10倍
alpha-beta剪枝优化尽管alpha-beta剪枝对速度有所优化,但是在实际问题中,我们还是很少使用朴素的alpha-beta剪枝算法,因为还是太慢了。
朴素的alpha-beta剪枝缺陷:还是慢。。。