运用博弈树实现五子棋人机对战

一、构建博弈树


       当轮到机器落子时,程序调用主函数,树的深度n=0,代表以当前局势为整个博弈树根结点,深度为0;同时在棋盘上没有落子的地方依次调用creatNode函数,在creatNode函数内部,n首先加一,n代表当前节点的深度,创建孩子结点(即当n为奇数时在棋盘上没有棋子的所有地方分别假设机器下一个子,当n为偶数时在棋盘上没有棋子的所有地方分别假设玩家下一个子,每个地方假设下子后的局势代表一个孩子节点)。对所有孩子结点依次进行判断,若孩子节点代表的棋局没分出胜负且深度没有达到指定深度,则以该孩子节点为根节点在棋盘上未落子的地方调用creatNode函数创建孩子结点,否则该结点为叶子结点,应该对叶子结点进行估值。然后对孩子结点重复上述过程直到树建立完毕。


二、对叶子结点进行估值


       对于所代表棋局AI赢的叶子结点,应该对其估一个足够大的值,对于所代表棋局玩家赢的叶子结点,应该对其估一个足够小的值,对于其它叶子结点,应对整个棋局遍历,判断整个棋盘上有多少个活一、死一,活二、死二、或三、死三、活四和死四。并将每种情况的数量乘上其预先设定好的权值并将所乘结果累加,得到的最终结果就是该叶子结点的权值。


三、回溯


         由上分析可知,博弈树的建树和对所有结点估值的过程均按深度优先的原则进行的,当n为奇数时,该层的所有结点均为min局面,当n为偶数时,该层的所有结点均为max局面,将同一个根结点所有孩子结点对应的最值返回给根结点,直到求出深度为一的所有结点的权值,然后在主函数中求出整个博弈树根结点的所有孩子结点中权值最大的结点,同时获得根节点到该孩子结点的假设下棋位置,这个位置就是机器这一步要下的位置。

四、代码实现(本程序博弈树的最大深度只能为奇数)

4.1主函数

	public void infernal()
	{
		HashMap hm1=new HashMap();
		HashMap hm2=new HashMap();
		int t;
		int max=-1000000,n=0;
		//玩家先手时各种棋局的权值
		hm1.put("1",-4);
		hm1.put("21",5);
		hm1.put("12",5);
		hm1.put("212",0);
		hm1.put("11",-100);
		hm1.put("112",-50);
		hm1.put("211",-50);
		hm1.put("2112",0);
		hm1.put("111",-8000);
		hm1.put("1112",-1500);
		hm1.put("2111",-1500);
		hm1.put("21112",0);
		hm1.put("1111",-30000);
		hm1.put("21111",-30000);
		hm1.put("11112",-30000);
		hm1.put("211112",0);		
		hm1.put("2",4);
		hm1.put("121",100);
		hm1.put("22",100);
		hm1.put("122",50);
		hm1.put("221",50);
		hm1.put("1221",0);
		hm1.put("222",1000);
		hm1.put("1222",200);
		hm1.put("2221",200);
		hm1.put("12221",0);
		hm1.put("2222",20000);
		hm1.put("22221",8000);
		hm1.put("12222",8000);
		hm1.put("122221",0);
		//AI后手时各种棋局的权值
		hm2.put("1",4);
		hm2.put("21",5);
		hm2.put("12",5);
		hm2.put("212",100);
		hm2.put("11",100);
		hm2.put("112",50);
		hm2.put("211",50);
		hm2.put("2112",0);
		hm2.put("111",1000);
		hm2.put("1112",200);
		hm2.put("2111",200);
		hm2.put("21112",0);
		hm2.put("1111",20000);
		hm2.put("21111",8000);
		hm2.put("11112",8000);
		hm2.put("211112",0);
		hm2.put("2",-4);
		hm2.put("121",0);
		hm2.put("22",-100);
		hm2.put("122",-50);
		hm2.put("221",-50);
		hm2.put("1221",0);
		hm2.put("222",-8000);
		hm2.put("1222",-1500);
		hm2.put("2221",-1500);
		hm2.put("12221",0);
		hm2.put("2222",-30000);
		hm2.put("22221",-30000);
		hm2.put("12222",-30000);
		hm2.put("122221",0);		
		//利用博弈树求得最佳落子坐标
		for(int i=0;imax)
				    {
					    max=t;
					    xx=i;
					    yy=j;
			 	    }
				}
			}
		}
    }
	

 4.2建树、估值和回溯

        int max=-1000000;
		int min=1000000;
		int maxmin=0;
		int t;
		n++;
		//AI后手执白子,棋子值为2
		if(wanJia==0)
		{
			//假设AI下白子,min局面
			if(n%2==1)
			{
			    chessArr[x][y]=2;
			    //棋局结束AI胜,叶子结点,直接评估
			    if(treeJudge(2,x,y))
			    {
				    maxmin=min;
			    }	
			    //棋局未结束
			    else
			    {
			    	//树的深度小于某设定层数,继续建树
				    if(n<1)
				    {
				    	for(int i=0;i=0;p++,q--)
										{
											if(chessArr[p][q]==0)break;
											else 
											{
												if(chessArr[i][j]==chessArr[p][q])
                                                    code=chessArr[p][q]+code;
												else
												{								
													code=chessArr[p][q]+code;
													break;	
												}	
											}
										}
										if(i!=0&&j!=LINES-1&&chessArr[i-1][j+1]!=0)
										{
											code+=chessArr[i-1][j+1];
										}
								        maxmin+=hm1.get(code);
										code="";										
								    }
					            }
					        }
					    }
				    }
				}
			}
			//假设玩家下黑子,max局面
			else
			{
				chessArr[x][y]=1;
				//棋局结束玩家胜,叶子结点,直接评估
				if(treeJudge(1,x,y))
				{
				    maxmin=max;
				}
				//棋局未结束
				else
				{
					//树的深度必小于该指定层数,继续建树
					for(int i=0;imax)
								{
									max=t;
								}
						    }
				        }
					}
					maxmin=max;
				 }
			}			
		}
        chessArr[x][y]=0; //将该棋盘位置还原
		return maxmin;

     当然,对于五子棋来说,其博弈树是十分庞大的,尤其是时间复杂度非常高,且随最大深度的增加呈指数增长。这时就可以使用alpha-beta剪枝等技术来优化博弈树。

你可能感兴趣的:(eclipse,java,算法)