博弈三连棋alpha_beta

来源:互联网 发布:甘肃 预约挂号软件 编辑:程序博客网 时间:2024/05/24 05:11

三连棋作为计算机博弈的入门棋,运用alpha-beta来产生最优招法

除了发现当前局面已经必输,输出最靠前的招法,其它局面都输出必胜或者必平招法。

O

..X

.O.

X..

0 1

三连棋的估值比较直接简单,只有胜,负,平3中结局,我定义为了10,0,-10。而某一玩家发现当前局面已经胜负已出,也就说明当前玩家已经输了(color交换的问题,不可能出现对手下了一子后发现自己莫名奇妙的赢了~_~),alpha-beta模拟过程中,估值由负极大值搜索返回到父亲节点时,该节点的alpha也就是当前局面下,当前执子的玩家的局面估值,如果局面已输就估值赋值为-10;

因此估值问题只有两种局面,判断GameOver 是平局则当前color估值为0,否则当前color的局面估值就是-10;

估值问题解决好了,剩下就是要处理先后手的问题了,通过SearchGoodMove查询到先手玩家的最优招法后,如果你作为后手玩家,在此局面上再次调用一次SearchGoodMove就能够解决。还有就是必输的局面,如果发现通过alpha-beta返回的val是-10,也就是当前局面该玩家必输,则输出棋局上最靠前的招法。

#define _CRT_SECURE_NO_WARNINGS#include <cstdio>#include <cstring>#define INF 999999char map[5][5], player,enermy;int fbx[10],fby[10],k;int checkwin()//判断在未填满棋局时游戏是否已经结束{if (map[0][0] == map[0][1] && map[0][1] == map[0][2]){if (map[0][0] == player)return 1;else if (map[0][0] == enermy)return 1;}if (map[1][0] == map[1][1] && map[1][1] == map[1][2]){if (map[1][0] == player)return 1;else if (map[1][0] == enermy)return 1;}if (map[2][0] == map[2][1] && map[2][1] == map[2][2]){if (map[2][0] == player)return 1;else if (map[2][0] == enermy)return 1;}if (map[0][0] == map[1][0] && map[1][0] == map[2][0]){if (map[0][0] == player)return 1;else if (map[0][0] == enermy)return 1;}if (map[0][1] == map[1][1] && map[1][1] == map[2][1]){if (map[0][1] == player)return 1;else if (map[0][1] == enermy)return 1;}if (map[0][2] == map[1][2] && map[1][2] == map[2][2]){if (map[0][2] == player)return 1;else if (map[0][2] == enermy)return 1;}if (map[0][0] == map[1][1] && map[1][1] == map[2][2]){if (map[0][0] == player)return 1;else if (map[0][0] == enermy)return 1;}if (map[0][2] == map[1][1] && map[1][1] == map[2][0]){if (map[0][2] == player)return 1;else if (map[0][2] == enermy)return 1;}return 0;}int Evaluate()//估值:只要胜负已出,当前玩家面对的都是输的局面,因此不是平局则估值为-10{if (checkwin() == 1){return -10;}else{return 0;}}void MakeNextMove(int x, int y,int turn)//执行下棋{if (turn)map[x][y] = player;elsemap[x][y] = enermy;}void UnMakeMove(int x, int y)//恢复棋盘{map[x][y] = '.';}int AlphaBeta(int depth, int alpha, int beta, int turn)//alpha-beta搜索{int i, j, val;if (depth == 0){return Evaluate();}if (checkwin() == 1){return -10;}for (i = 0; i < 3; i++){for (j = 0; j < 3; j++){if (map[i][j] == '.'){MakeNextMove(i, j, turn);val = -AlphaBeta(depth - 1, -beta, -alpha, 1 - turn);UnMakeMove(i, j);if (val >= beta){return val;}if (val > alpha){alpha = val;}}}}return alpha;}void SearchGoodMove(int depth, int alpha, int beta, int turn)//寻找当前局面最佳招法并打印{int i, j, val;for (i = 0; i < 3; i++){for (j = 0; j < 3; j++){if (map[i][j] == '.'){MakeNextMove(i, j, turn);val = -AlphaBeta(depth - 1, -beta, -alpha, 1 - turn);UnMakeMove(i, j);if (val == 10 && turn==1)//先手且必胜则输出最前招法{printf("%d %d\n", i, j);return ;}if (turn == 0 && val==10)//后手必败则递归调用一次将后手处理为先手{MakeNextMove(i, j, turn);SearchGoodMove(depth - 1, -beta, -alpha, 1 - turn);UnMakeMove(i, j);return ;}if (val >= 0 && turn==1)//先手必平则先保存起必平招法{fbx[k] = i;fby[k++] = j;}}}}if (turn == 0){for (i = 0; i < 3; i++){for (j = 0; j < 3; j++){if (map[i][j] == '.'){MakeNextMove(i, j, turn);val = -AlphaBeta(depth - 1, -INF, INF, 1 - turn);SearchGoodMove(depth - 1, -INF, INF, 1 - turn);//后手没有必输则递归一次转换为先手return;}}}}if (k == 0)//没有比平招法就必输,输出第一个招法{for (i = 0; i < 3; i++){for (j = 0; j < 3; j++){if (map[i][j] == '.'){printf("%d %d\n", i, j);return ;}}}}else//有必平招法则输出第一个printf("%d %d\n", fbx[0], fby[0]);}int main(){int i, j, dep, t;while (~scanf("%c", &player)){while(player == '\n')player = getchar();if (player == '.')return 0;if (player == 'O')enermy = 'X';if (player == 'X')enermy = 'O';dep = 0;k = 0;for (i = 0; i < 3; i++){scanf("%s", map[i]);}for (i = 0; i < 3; i++){for (j = 0; j < 3; j++){if (map[i][j] == '.')dep++;}}if (dep % 2 && player == 'X')t = 1;else if (dep % 2 == 0 && player == 'O')t = 1;elset = 0;memset(fbx, 0, sizeof(fbx));memset(fby, 0, sizeof(fby));SearchGoodMove(dep, -INF, INF, t);}return 0;}

0 0