之前做了最基础的井字棋游戏,就是人人模式,双方互相下,判断输赢,现在把功能稍微的提升了一下,使用Max-Min算法得到电脑下棋的最优点。
这里我就简单的介绍一下Max-Min算法和在我写的这个井字棋游戏之中他是如何使用的。
井字棋游戏和五子棋等等都是博弈游戏,而博弈游戏的算法都秉持着一个目的:最大化自己的利益,最小画别人的利益。
自己对Max-Min算法的理解
这个算法又叫做极大极小值算法,根据我粗略的研究就是将所有可能的方案按照树形结构画出开,这种博弈类游戏有先后手,对应的就是Max和Min,你将所有的可能列举出来,然后从最好的结果一路往上推,就会得到对你最有利的一条路径,也可以说是走棋的方法。
怎么判断如何选择对你有利,就需要设计一个估算的函数,类似于五子棋和井字棋,就可以是计算出当前所有的空格放上玩家的棋子,得到的练成五或者三的数目。
每次比较所有可能算法的复杂度太高,所以呢会用到剪枝算法,就是将不影响结果的那些可能全部砍掉,这样就会减少计算。
游戏代码:
估算函数:
int Widget::CalEvalute(){
//判断此时是否以及结束
int win_people = GetWinPeople();
if(win_people == -1) return N;//玩家2或者电脑赢 返回最大
if(win_people == 1) return -N;//玩家1胜利 返回最小
//胜负未分
int value = 0;
int new_chess[3][3];//临时棋盘
//计算机估值
//遍历棋盘所有的格子
for(int i=0;i<3;i++){
for(int j=0;j<3;j++){
if(chess[i][j] == 0){
new_chess[i][j] == -1;//没有下棋的地方赋值为-1
}
else {
new_chess[i][j] = chess[i][j];//下了的地方保持
}
}
}//这一步 电脑所有能走的地方变成了-1 玩家下了的地方变成了1
//value 加上 练成三线的个数 和不等于三或者-3 /3一定为0
for(int i = 0;i<3;i++){
value = value - (new_chess[i][0] + new_chess[i][1] + new_chess[i][2])/3;
}
for(int i = 0; i < 3; ++ i) //对于垂直方向
value = value - (new_chess[0][i] + new_chess[1][i] + new_chess[2][i])/3;
value = value - (new_chess[0][0] + new_chess[1][1] + new_chess[2][2])/3;//主对角线
value = value - (new_chess[2][0] + new_chess[1][1] + new_chess[0][2])/3;//副对角线
//人类
for(int i = 0; i < 3; ++ i){
for(int j = 0; j < 3; ++ j){
if(chess[i][j] == 0) new_chess[i][j] = 1;
else new_chess[i][j] = chess[i][j];
}
}
for(int i = 0; i < 3; ++ i) //对于水平方向
value = value - (new_chess[i][0] + new_chess[i][1] + new_chess[i][2])/3;
for(int i = 0; i < 3; ++ i) //对于垂直方向
value = value - (new_chess[0][i] + new_chess[1][i] + new_chess[2][i])/3;
value = value - (new_chess[0][0] + new_chess[1][1] + new_chess[2][2])/3;//主对角线
value = value - (new_chess[2][0] + new_chess[1][1] + new_chess[0][2])/3;//副对角线
return value;
}
Min-Max
int Widget::MinMaxSolution(int depth, int alpha, int beta)
{
int cur_value = 0, best_value = 0, cnt = 0;
pair<int, int> location[10];
int win_people = GetWinPeople();
if(win_people == -1 || win_people == 1 ||depth == 0) {
// qDebug() << CalEvalute()<<endl;
return CalEvalute();
}
//深度未耗尽,给定初始值
if(cur_player == -1) best_value = -N;
else if(cur_player == 1) best_value = N;
//获取棋盘上剩余的位置
for(int i = 0; i < 3; ++ i){
for(int j = 0; j < 3; ++ j){
if(chess[i][j] == 0){
//qDebug()<<i<<","<<j<<endl;
location[cnt].first = i;
location[cnt++].second = j;
}
}
}
if(cnt < 1) {
best_point = location[0];
return best_value;
}
for(int i = 0; i < cnt; ++ i){
pair<int, int> cur_pos = location[i];
int x = cur_pos.first, y = cur_pos.second;
chess[x][y] = cur_player; //当前点下一个棋
cur_player = (cur_player == 1) ? -1 : 1;//换下一个棋手
cur_value = MinMaxSolution(depth - 1, alpha, beta); //向下递归
chess[x][y] = 0; //取消下棋
cur_player = (cur_player == 1) ? -1 : 1;
if(cur_player == -1){ // 当前玩家是Max节点
if(cur_value > best_value){
best_value = cur_value;
if(depth == cur_depth) best_point = cur_pos;
alpha = best_value;
}
if(beta <= alpha) return beta; // Max中上界小于下界 返回较小值
}
else if(cur_player == 1){ // 当前是Min节点
if(cur_value < best_value){
best_value = cur_value;
if(depth == cur_depth) best_point = cur_pos;
beta = best_value;
}
if(beta <= alpha) return alpha; // Min中上界小于下界 返回较大值
}
}
return best_value;
}
这里推荐一下关于这个算法讲解的地址和博客,希望对大家有帮助