【随机】【加壳】【alpha-bet剪枝】五子棋AI
2014-10-02 19:11
537 查看
//五子棋AI算法 //加入alpha-bet剪枝 //------------------------------------------------------ //头文件包含 #include "stdio.h" #include <cstdlib> #include <ctime> //------------------------------------------------------ //定义黑白棋手 #define white 1 #define black -1 //------------------------------------------------------ //定义数据类型 typedef struct Space//位置 { int row; int list; }Space; typedef struct Limit { int up; int down; int left; int right; }Limit; typedef int Bool; //------------------------------------------------------ //函数声明 int max(int x1, int x2); int min(int x1, int x2); Bool lineWin(int player, int a[], int n = 9);//单线五子判断 Bool win(int x[][15], int row, int list);//五子判断 int getLinePower(int player, int a[], int n = 9);//单线势分析 int getLeafPower(int x[][15], int row, int list);//计算叶子的势 Limit getLimit(int x[][15], int width);//划定搜索范围 int start(int x[][15], int player, int depth = 4/*搜索深度*/, int width = 1/*搜索宽度*/, int maxUnclePower = 1000);//分析棋盘,返回最优势的值 Space ai(int x[][15], int player, int depth = 4/*搜索深度*/, int width = 1/*搜索宽度*/);//算法入口,返回下棋位置 //------------------------------------------------------ //函数定义 int max(int x1, int x2){ return x1>x2 ? x1 : x2; } int min(int x1, int x2){ return x1>x2 ? x2 : x1; } //单线五子判断 Bool lineWin(int player, int a[], int n) { int num = 0; for (int i = 0; i<n - (5 - 1); i++) { num = 0; for (int j = 0; j<5; j++) if (a[i + j] == player)num++; if (num == 5)return 1; } return 0; } //五子判断 Bool win(int x[][15], int row, int list) { int line[9] = { 0 }; int player = x[row][list]; // │ 从上到下赋值 int begin = max(0, row - 4);//对应所选取数组的横坐标的开始 int end = min(14, row + 4);//对应所选取数组的横坐标的结束 for (int i = begin, j = 0/*数组下标*/; i <= end; i++, j++) line[j] = x[i][list]; if (lineWin(player, line, end - begin + 1))return 1; // ╲ 从左上到右下赋值 begin = max(max(list - 4, 0), max(list - row, 0)); end = min(min(list + 4, 14), min(list + (14 - row), 14)); for (int i = begin, j = 0; i <= end; i++, j++) line[j] = x[row - (list - begin) + j][i]; if (lineWin(player, line, end - begin + 1))return 1; // ─ 从左到右赋值 begin = max(0, list - 4); end = min(14, list + 4); for (int i = begin, j = 0; i <= end; i++, j++) line[j] = x[row][i]; if (lineWin(player, line, end - begin + 1))return 1; // ╱ 从左下到右上赋值 begin = max(max(list - 4, 0), max(list - (14 - row), 0)); end = min(min(list + 4, 14), min(list + row, 14)); for (int i = begin, j = 0; i <= end; i++, j++) line[j] = x[row + (list - begin) - j][i]; if (lineWin(player, line, end - begin + 1))return 1; return 0; } //单线势分析 int getLinePower(int player, int a[], int n/*数组长度*/) { int num = 0; int num2 = 0; //***** 绝对优势:5 for (int i = 0; i<n - (5 - 1); i++) { num = 0; for (int j = 0; j<5; j++) if (a[i + j] == player)num++; if (num == 5)return 5; } //_****_ 大优势:4 for (int i = 0; i<n - (6 - 1); i++) { num = 0; for (int j = 0; j<6; j++) if (a[i + j] == player)num++; if (num == 4 && (a[i] == 0 && a[i + 5] == 0))return 4; } //****_、**_** 优势:3 for (int i = 0; i<n - (5 - 1); i++) { num = 0; num2 = 0; for (int j = 0; j<5; j++) { if (a[i + j] == player)num++; if (a[i + j] == 0)num2++; } if (num == 4 && num2 == 1)return 3; } //_*_**_、__***_ 优势:2 for (int i = 0; i<n - (6 - 1); i++) { num = 0; num2 = 0; for (int j = 0; j<6; j++) { if (a[i + j] == player)num++; if (a[i + j] == 0)num2++; } if (num == 3 && num2 == 3 && a[i] == 0 && a[i + 5] == 0)return 2; } //***_ 小优势:1 for (int i = 0; i<n - (4 - 1); i++) { num = 0; num2 = 0; for (int j = 0; j<4; j++) { if (a[i + j] == player)num++; if (a[i + j] == 0)num2++; } if (num == 3 && num2 == 1)return 1; } //_*_*_ 小优势:1 for (int i = 0; i<n - (5 - 1); i++) { num = 0; for (int j = 0; j<5; j++) if (a[i + j] == player)num++; if (num == 2 && (a[i] == 0 && a[i + 4] == 0) && a[i + 2] == 0)return 1; } //_**_ 小优势:1 for (int i = 0; i<n - (4 - 1); i++) if (a[i] == 0 && a[i + 1] == player&&a[i + 2] == player&&a[i + 3] == 0)return 1; return 0; } //计算叶子的势 int getLeafPower(int x[][15], int row, int list) { int power; int a[4] = { 0 };//多线势记录 int line[9] = { 0 }; int player = x[row][list]; int begin = max(0, row - 4); int end = min(14, row + 4); for (int i = begin, j = 0; i <= end; i++, j++)// │ line[j] = x[i][list]; a[0] = getLinePower(player, line, end - begin + 1); begin = max(max(list - 4, 0), max(list - row, 0)); end = min(min(list + 4, 14), min(list + (14 - row), 14)); for (int i = begin, j = 0; i <= end; i++, j++)// ╲ line[j] = x[row - (list - begin) + j][i]; a[1] = getLinePower(player, line, end - begin + 1); begin = max(0, list - 4); end = min(14, list + 4); for (int i = begin, j = 0; i <= end; i++, j++)// ─ line[j] = x[row][i]; a[2] = getLinePower(player, line, end - begin + 1); begin = begin = max(max(list - 4, 0), max(list - (14 - row), 0)); end = min(min(list + 4, 14), min(list + row, 14)); for (int i = begin, j = 0; i <= end; i++, j++)// ╱ line[j] = x[row + (list - begin) - j][i]; a[3] = getLinePower(player, line, end - begin + 1); for (int i = 0; i<3; i++)//从大到小冒泡排序 for (int j = 0; j<3 - i; j++) if (a[j]<a[j + 1]) { a[j] = a[j] ^ a[j + 1]; a[j + 1] = a[j] ^ a[j + 1]; a[j] = a[j] ^ a[j + 1]; } if (a[0] == 5)power = 4 * (4 * 4 * 4); else if (a[0] == 4)power = 3 * (4 * 4 * 4) + 3 * (4 * 4); else { power = a[0] * 4 * 4 * 4 + a[1] * 4 * 4 + a[2] * 4 + a[3]; if (power >= 3 * (4 * 4 * 4) && power<3 * (4 * 4 * 4) + 2 * (4 * 4))power = power - 4 * 4 * 4; } return power; } //划定搜索范围 Limit getLimit(int x[][15], int width) { Limit limit; //计算limit.up for (int i = 0; i<15; i++) for (int j = 0; j<15; j++) if (x[i][j] != 0) { limit.up = i; limit.left = j; limit.right = j; limit.down = i; goto out; } out: //计算limit.left\down for (int i = limit.up; i<15; i++) for (int j = 0; j<15; j++) if (x[i][j] != 0) { limit.down = i; if (j<limit.left)limit.left = j; break; } //计算limit.right for (int i = limit.up; i<15; i++) for (int j = 14; j >= 0; j--) if (x[i][j] != 0 && j>limit.right) { limit.right = j; break; } limit.up = max(limit.up - width, 0); limit.left = max(limit.left - width, 0); limit.right = min(limit.right + width, 14); limit.down = min(limit.down + width, 14); return limit; } //本质上是关于势的后序遍历 //分析棋盘,返回最优势的值 //使用到alpha-bet剪枝 int start(int x[][15], int player, int depth/*搜索深度*/, int width/*搜索宽度*/, int maxUnclePower) { int maxPower = -1000; int n = 0;//记录空棋子的个数 for (int i = 0; i<15; i++) for (int j = 0; j<15; j++) if (x[i][j] == 0)n++; //棋盘已满 if (n == 0)return 0; //划定搜索范围 Limit limit = getLimit(x, width); //判断己方是否活四 for (int i = limit.up; i <= limit.down; i++) for (int j = limit.left; j <= limit.right; j++) { if (x[i][j] != 0)continue;//检查落点是否为空 x[i][j] = player;//落点假设 if (win(x, i, j)) { x[i][j] = 0;//落点还原 return 4 * 4 * 4 * 4;//返回绝对优势 } x[i][j] = 0;//落点还原 } //判断敌方是否活四 for (int i = limit.up; i <= limit.down; i++) for (int j = limit.left; j <= limit.right; j++) { if (x[i][j] != 0)continue;//检查落点是否为空 x[i][j] = player*-1;//敌方落点假设 if (win(x, i, j)) { x[i][j] = player;//己方落点假设 maxPower = start(x, player*-1, depth - 1, width, maxPower*-1)*-1;//根据计算情况赋值 x[i][j] = 0;//落点还原 return maxPower; } x[i][j] = 0;//落点还原 } //分支节点 if (depth>0) { for (int i = limit.up; i <= limit.down; i++) for (int j = limit.left; j <= limit.right; j++) { if (x[i][j] != 0)continue;//检查落点是否为空 x[i][j] = player;//落点假设 int temPower = start(x, player*-1, depth - 1, width, maxPower*-1)*-1; if (temPower >= maxUnclePower)//alpha-bet剪枝 { x[i][j] = 0;//落点还原 return temPower; } if (temPower == 4 * 4 * 4 * 4)//判断是否形成绝对优势:如果是,剪除所有兄弟节点 { x[i][j] = 0;//落点还原 return 4 * 4 * 4 * 4; } if (maxPower<temPower) maxPower = temPower; x[i][j] = 0;//落点还原 } return maxPower; } //最后一层分支节点 for (int i = limit.up; i <= limit.down; i++) for (int j = limit.left; j <= limit.right; j++) { if (x[i][j] != 0)continue;//检查落点是否为空 x[i][j] = player;//落点假设 int temPower = getLeafPower(x, i, j); if (temPower >= maxUnclePower)//alpha-bet剪枝 { x[i][j] = 0;//落点还原 return temPower; } if (maxPower<temPower) maxPower = temPower; x[i][j] = 0;//落点还原 } return maxPower; } //算法入口 Space ai(int x[][15], int player, int depth/*搜索深度*/, int width/*搜索宽度*/) { Space arrSpace[8];//记录相同势的棋子的队列 int arrEnd=-1;//队列队尾指针 int maxPower=-1000; int n = 0;//记录空棋子的个数 for (int i = 0; i<15; i++) for (int j = 0; j<15; j++) if (x[i][j] == 0)n++; //棋盘为空 if (n == 15 * 15) { arrSpace[0].row = 8; arrSpace[0].list = 8; return arrSpace[0]; } //棋盘已满 if (n == 0) { arrSpace[0].row = -1; arrSpace[0].list = -1; return arrSpace[0]; } //划定搜索范围 Limit limit = getLimit(x, width); //判断己方是否活四 for (int i = limit.up; i <= limit.down; i++) for (int j = limit.left; j <= limit.right; j++) { if (x[i][j] != 0)continue;//检查落点是否为空 x[i][j] = player;//落点假设 if (win(x, i, j)) { arrEnd++; arrSpace[0].row = i; arrSpace[0].list = j; x[i][j] = 0;//落点还原 return arrSpace[0]; } x[i][j] = 0;//落点还原 } //判断敌方 a5fc 是否活四 for (int i = limit.up; i <= limit.down; i++) for (int j = limit.left; j <= limit.right; j++) { if (x[i][j] != 0)continue;//检查落点是否为空 x[i][j] = player*-1;//敌方落点假设 if (win(x, i, j)) { x[i][j] = player;//己方落点假设 arrSpace[0].row = i; arrSpace[0].list = j; x[i][j] = 0;//落点还原 return arrSpace[0]; } x[i][j] = 0;//落点还原 } for (int i = limit.up; i <= limit.down; i++) for (int j = limit.left; j <= limit.right; j++) { if (x[i][j] != 0)continue;//检查落点是否为空 x[i][j] = player;//落点假设 int temPower = start(x, player*-1, max(1,depth)/*搜索深度不能小于1*/ - 1, max(1,width)/*搜索宽度不能小于1*/,maxPower*-1)*-1; if (temPower == 4 * 4 * 4 * 4)//判断是否形成绝对优势:如果是,剪除所有兄弟节点 { arrSpace[0].row = i; arrSpace[0].list = j; x[i][j] = 0;//落点还原 return arrSpace[0]; } if(arrEnd<8 && maxPower==temPower) { arrEnd++; arrSpace[arrEnd].row=i; arrSpace[arrEnd].list=j; }else if (maxPower<temPower) { arrSpace[0].row = i; arrSpace[0].list = j; arrEnd=0; maxPower = temPower; } x[i][j] = 0;//落点还原 } srand((unsigned)time(NULL)); return arrSpace[rand()%(arrEnd+1)]; } //测试 int main() { int a[15][15] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; Space b = ai(a, black); printf("(%d,%d)\n", b.row, b.list); return 0; }
相关文章推荐
- 【alpha-bet剪枝】五子棋AI
- 五子棋AI博弈树之带Alpha-Beta剪枝的极大极小过程函数
- 五子棋AI算法第三篇-Alpha Beta剪枝
- 五子棋AI循序渐进【3】基石——超出边界的alpha-beta剪裁
- 基于Alphabet剪枝的五子棋AI
- 【机器学习实战】制作五子棋AI之一:图片预处理(尺寸变换和增加alpha通道)
- QT五子棋项目详解之五:AI人机对战Alpha-Beta剪枝算法
- alpha-beta剪枝搜索
- 五子棋Java实现,能正常运行,对AI有兴趣的可以一起探讨
- 为何谷歌围棋AI AlphaGo可能会把李世石击溃
- 博弈(alpha-beta 剪枝)—— POJ 1568 Find the Winning Move
- 五子棋AI设计
- AI----------五子棋
- 【五子棋AI】启发算法——迭代加深与内部迭代加深
- 练练手,近期实现的一个基于alpha-beta剪枝技术的机器人“追逐”程序
- 并行实现有自学习能力的五子棋AI
- 五子棋AI算法第五篇-算杀
- 五子棋AI算法简易实现(二)
- 博弈(alpha-beta 剪枝)POJ —— 1085 Triangle War
- 五子棋AI算法第七篇-Zobrist