五子棋(人机)-极大极小值搜索算法

从人落子开始到出现胜负或者和局,之间所落的子,构成了一个解。而解空间就是一个树,解就是这解空间中的一条路径。只不过这个解空间是电脑的选择和人的选择共同构成的(奇数层是电脑(因为轮到电脑落子么),偶数层是人)。

极大极小值搜索算法,来搜索回溯这个解空间:它假设人和电脑都是极其聪明的,他们都会选择出最优的一步。

但是搜索整棵树是不现实的,16*16!指数级,所以只回溯n步,即这个AI考虑的是N步之内的最优解,它是考虑了n步之后的情况的

-------------------------------------------------------------------我是分割线-------------------------------------------------------------------------

假设玩家执黑子,电脑执白子

评估函数:评估函数将对棋盘上的所有黑子做出评分(连成线的等级越高,数量越多,估分就越高)作scorehumber;也将对棋盘上的所有白子做出评分(连成线的等级越高,数量越多,估分就越高)作scorecomputer。然后评估值为【scorecoputer-scorehumber】。它将认为,这个评估值越高,整个局面对电脑越有利;这个评估值越低,整个局面对玩家越有利。

max-min搜索最优解,即向后回溯depth步,轮到电脑时,电脑做出最有利于自己的选择(选择最高的评估值),轮到玩家时,玩家做出最有利于自己的选择(选择最低的评估值)。(他们的选择将被推迟,叶子节点先做出选择,然后层层往上推出那一层的最优解)。伪码:

int MinMax(int depth) { // 函数的评估都是以白方的角度来评估的
 if (SideToMove() == WHITE) { // 白方是“最大”者 
  return Max(depth); 
 } else {           // 黑方是“最小”者 
  return Min(depth); 
 } 
}   
int Max(int depth) { 
 int best = -INFINITY; 
 if (depth <= 0) { 
  return Evaluate(); 
 } 
 GenerateLegalMoves(); 
 while (MovesLeft()) { 
  MakeNextMove(); 
  val = Min(depth - 1); 
  UnmakeMove(); 
  if (val > best) { 
   best = val; 
  } 
 } 
 return best; 
}   
int Min(int depth) { 
 int best = INFINITY; // 注意这里不同于“最大”算法 
 if (depth <= 0) { 
  return Evaluate(); 
 } 
 GenerateLegalMoves(); 
 while (MovesLeft()) { 
  MakeNextMove(); 
  val = Max(depth - 1); 
  UnmakeMove(); 
  if (val < best) {  // 注意这里不同于“最大”算法 
   best = val; 
  } 
 } 
 return best; 
} 
-------------------------------------------------------------------我是分割线-------------------------------------------------------------------------

计分板

成五+100000
活四+10000
死四+1000
活三+1000
死三+100
活二+100
死二+10
活一+10
-------------------------------------------------------------------我是分割线-------------------------------------------------------------------------

int max_noalphabeta(int depth,int i1,int i2);//轮到电脑走步时,电脑作的选择 
int min_noalphabeta(int depth,int i1,int i2);//轮到人走步时,人作的选择 
void generatepoint(vector< pair<int,int> > &v);//产生空子序列 
int scoretable(int number,int empty1);//积分表 
int countscore(vector<int> n,int turn);	//算单个数组分 
bool hasne(int x,int y);//周围是否有子存在,无子的就加考虑 

bool hasne(int x,int y)//空子只算旁边有子的
{
	int i,j;
	for(i=(x-3>0?x-3:0);i<=x+3&&i<16;++i)
		for(j=(y-3>0?y-3:0);j<=y+3&&j<16;++j)
			if(i!=0||j!=0)
				if(pos[i][j]!=0)
					return true;
	return false;
} 
void generatepoint(vector< pair<int,int> > &v)//产生空子序列		
{
	for(int i=0;i<16;++i)
		for(int j=0;j<16;++j)
			if(pos[i][j]==0&&hasne(i,j))
			{
				pair<int,int> p;
				p.first=i;
				p.second=j;
				v.push_back(p);
			} 
}
//按照成五100000、活四10000、活三1000、活二100、活一10、死四1000、死三100、死二10的规则 
//给棋盘上的所有棋子打分,之和为电脑的单方面得分scorecomputer,然后对玩家的棋子同样打分,之和为scorehumber
//scoreComputer-scorehumber即为当前局势的总分数 
int scoretable(int number,int empty1)//计分板
{
	if(number>=5)	return 100000;
	else if(number==4)
	{
		if(empty1==2)	return 10000;
		else if(empty1==1)	return 1000;
	}
	else if(number==3)	
	{
		if(empty1==2)	return 1000;
		else if(empty1==1)	return 100;
	}
	else if(number==2)
	{
		if(empty1==2)	return 100;
		else if(empty1==1)	return 10;
	}
	else if(number==1&&empty1==2)	return 10;
	return 0;
}
int countscore(vector<int> n,int turn)//正斜线、反斜线、横、竖,均转成一维数组来计算 
{
	int scoretmp=0;
	int len=n.size();
	int empty1=0;
	int number=0;
	if(n[0]==0)	++empty1;
	else if(n[0]==turn)	++number;
	int i=1;
	while(i<len)
	{
		if(n[i]==turn)	++number;
		else if(n[i]==0)
		{
			if(number==0)	empty1=1;
			else
			{
				scoretmp+=scoretable(number,empty1+1);
				empty1=1;
				number=0;
			}
		}
		else
		{
			scoretmp+=scoretable(number,empty1);
			empty1=0;
			number=0;
		}
		++i;
	}
	scoretmp+=scoretable(number,empty1);
	return scoretmp;
}
int evaluate_minmax_noalphabeta()//评估函数,评估局势
{
	int scorecomputer=0;
	int scorehumber=0;
	//横排们 
	for(int i=0;i<16;++i)
	{
		vector<int> n;
		for(int j=0;j<16;++j)
			n.push_back(pos[i][j]);
		scorecomputer+=countscore(n,2);
		scorehumber+=countscore(n,1);
		n.clear();
	}
	//竖排们
	for(int j=0;j<16;++j)
	{
		vector<int> n;
		for(int i=0;i<16;++i)
			n.push_back(pos[i][j]);
		scorecomputer+=countscore(n,2);
		scorehumber+=countscore(n,1);
		n.clear();
	} 
	//上半正斜线们 
	for(int i=0;i<16;++i)
	{
		int x,y;
		vector<int> n;
		for(x=i,y=0;x<16&&y<16;++x,++y)
			n.push_back(pos[y][x]);
		scorecomputer+=countscore(n,2);
		scorehumber+=countscore(n,1);
		n.clear();
	} 
	//下半正斜线们
	for(int j=1;j<16;++j)
	{
		int x,y;
		vector<int> n;
		for(x=0,y=j;y<16&&x<16;++x,++y)
			n.push_back(pos[y][x]);
	 	scorecomputer+=countscore(n,2);
		scorehumber+=countscore(n,1);
		n.clear();
	} 
	//上半反斜线们
	for(int i=0;i<16;++i)
	{
		vector<int> n;
		int x,y;
		for(y=i,x=0;y>=0&&x<16;--y,++x)
			n.push_back(pos[y][x]);
		scorecomputer+=countscore(n,2);
		scorehumber+=countscore(n,1);
		n.clear();
	} 
	//下半反斜线们
	for(int j=1;j<16;++j)
	{
		vector<int> n;
		int x,y;
		for(y=j,x=15;y<16&&x>=0;++y,--x)
			n.push_back(pos[x][y]);
		scorecomputer+=countscore(n,2);
		scorehumber+=countscore(n,1);
		n.clear();
	} 
	return scorecomputer-scorehumber;
} 
int min_noalphabeta(int depth,int i1,int i2)//玩家落子时													//当min(人)走步时,人的最好情况 
{
	int res=evaluate_minmax_noalphabeta();
	Chess cc;
	cc.chess_isover(i1,i2,2);
	if(isover!=0||depth<=0)
	{
		isover=0;
		return res;
	}
	vector< pair<int,int> > v;
	generatepoint(v);
	int len=v.size();
	int best=INT_MAX;
	for(int i=0;i<len;++i)
	{
		pos[v[i].first][v[i].second]=1;
		int tmp=max_noalphabeta(depth-1,v[i].first,v[i].second);
		if(tmp<best)	best=tmp;//玩家落子时选择最有利自己的局面,将推迟,叶子节点做出选择后,层层往上推	
		pos[v[i].first][v[i].second]=0;
	} 
	return best;
}
int max_noalphabeta(int depth,int i1,int i2)													//当max(电脑)走步时,max(电脑)应该考虑最好的情况 
{
	int res=evaluate_minmax_noalphabeta();
	Chess cc;
	cc.chess_isover(i1,i2,1);
	if(isover!=0||depth<=0)
	{
		isover=0;
		return res;
	}
	vector< pair<int,int> > v;
	generatepoint(v);
	int len=v.size();
	int best=INT_MIN;
	for(int i=0;i<len;++i)
	{
		pos[v[i].first][v[i].second]=2;
		int tmp=min_noalphabeta(depth-1,v[i].first,v[i].second);
		if(tmp>best)	best=tmp;//电脑落子时,选择最有利于自己的局面,将推迟	
		pos[v[i].first][v[i].second]=0;
	} 
	return best;
}
void Chess::chess_ai_minmax_noalphabeta(int &x,int &y,int depth)//极大极小值算法搜索n步后的最优解 
{
	vector< pair<int,int> > v;
	generatepoint(v);
	int best=INT_MIN;
	int len=v.size();
	vector< pair<int,int> > v2; 
	for(int i=0;i<len;++i)
	{
		pos[v[i].first][v[i].second]=2;	//选该子,将该子置白,防止后面递归时,再递归到 
		int tmp=min_noalphabeta(depth-1,v[i].first,v[i].second);
		if(tmp==best)
			v2.push_back(v[i]);
		if(tmp>best)
		{
			best=tmp;
			v2.clear();
			v2.push_back(v[i]);
		}
		pos[v[i].first][v[i].second]=0;	//假设完之后,该子需要重新置空,恢复原来的样子 
	}
	len=v2.size();
	int i=(int)(rand()%len);
	x=v2[i].first;
	y=v2[i].second; 
}

参考:

http://blog.csdn.net/lihongxun945/article/details/50625267

http://blog.csdn.net/kingkong1024/article/details/7639401

  • 4
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
1.项目代码均经过功能验证ok,确保稳定可靠运行。欢迎下载体验!下载完使用问题请私信沟通。 2.主要针对各个计算机相关专业,包括计算机科学、信息安全、数据科学与大数据技术、人工智能、通信、物联网等领域的在校学生、专业教师、企业员工。 3.项目具有丰富的拓展空间,不仅可作为入门进阶,也可直接作为毕设、课程设计、大作业、初期项目立项演示等用途。 4.当然也鼓励大家基于此进行二次开发。在使用过程中,如有问题或建议,请及时沟通。 5.期待你能在项目中找到乐趣和灵感,也欢迎你的分享和反馈! 【资源说明】 Java开发基于Alpha-Beta剪枝极大极小博弈算法的五子棋AI游戏源码+项目说明.zip 程序设计分析 在开始编写程序之前,我们应该先要对五子棋游戏要做的事进行剖析,明确设计任务,功能要求等等。通过程序要实现的功能,设定具体的每个模块所完成的每一个功能,然后连接每一个模块来实现所需要的功能设计。 画棋盘 绘制左侧的棋盘:绘制棋盘线、绘制天元和星、绘制预选框、绘制左侧数字和底下字母、绘制棋子、给棋子添加序号。 绘制右侧的功能区:添加显示棋子估值信息区域、模式:人人对战、人机对战、智能:估值函数、估值函数+搜索树、搜索树:搜索深度、每层节点、其他:显示落子顺序、悔棋、新游戏、人类先手、机器先手 添加事件 在下棋区域鼠标移动,预选框跟随鼠标移动、在交叉点点击落子、悔棋、新游戏、显示落子顺序、人类先手、机器先手、在棋子上点击鼠标右键显示估值 人人下棋 在交叉点落子的时候,判断输赢 人机下棋 估值函数、极大极小值搜索、Α-β剪枝优化。 估值函数:计算某个点的多价值有高。 首先设计好可能出现的棋型,并且根据会赢的概率给出相应的分值。 攻:对我自己有利的棋型的价值 守:对对方有利的棋型的价值 某个点的价值 = 攻价值+守价值 估值函数有个弊端,只考虑眼前的事 极大极小值搜索: 思想:轮到自己下棋的时候,找最大值。轮到对方下的时候,寻找最小值。但是这样计算量是比较大的。开始游戏有,棋盘上是有一个黑子。人工下一个白子。搜索5层。第一个层可选择位置:223。第二层:223x222。第三层 223 x 222 x 221 …… Α-β剪枝优化:Α-β是两个值,Α自己,β对手。初始值Α是无穷小的数。β是无穷大的数。A<β这个场景才会合法。否则这枝(树枝)将被剪掉(不进行遍历) 如果是自己的棋,得到得分以后,和Α进行比较,如果得分大于Α。Α=score 如果是对手的棋,得到得分以后,和β进行比较,如果得分小于β。Β=score
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值