您的位置:首页 > 大数据 > 人工智能

【随机】【加壳】【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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: