一字棋指的是:在一个九宫格内率先连成三个字的取胜

 首先,基于前面决策树的讲解 博弈的棋类游戏等等 只要找到合适的估值函数都可以使用博弈树来实现 下面我们来使用博弈树完成一字棋的算法。

根据前面的算法思想我们算法大致分为几步:

1.对棋局落子有正确的估值

2.通过遍历建立博弈树

3.对博弈树进行α-β剪枝增快查找速度(这里由于数据量较小 放在最后一起讲解)

4.根据极大值 极小值搜索获取博弈树产生的结果

 

简单的一字棋游戏 python 一字棋博弈_sed

首先在我们假设电脑先走 这时通过

在博弈树中通过这段代码在open表中将所有棋局储存 并且打分

for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(closed[closedtop]->all[i][j]==N) //在firstox中已经将棋局传入
{
    opentop++;//新节点于open表入队
    open[opentop]=new OXchess;//开辟空间
    open[opentop]->should(closed[closedtop],closedtop,i,j,step);//对新节点进行合适的操作
}

 

简单的一字棋游戏 python 一字棋博弈_i++_02

在经历完一轮循环后 将所有的open的元素 入closed表 (此时closed中的所有元素 flag = 1)

也就是记录了计算机所有的落子位置 下面进行第二次循环 同理完成人类落子 。。。(可以一直模拟下去 但是随着模拟的回合增加 由于估值函数不可能完全拟合 会产生过拟合的现象)

并且如果完全模拟完所有的情况有9! = 362 880种情况 

简单的一字棋游戏 python 一字棋博弈_sed_03

此时编译器也会报错 这里我们将博弈树深度设为5 

会发现 AI出现了问题 由于拟合层数过多 但是估值函数较为粗糙 导致随着拟合次数的增加 会使估值的误差也快速上涨 导致出现过拟合的问题 

简单的一字棋游戏 python 一字棋博弈_i++_04

我们可以通过调整估值代码的参数来完成更多层数的拟合 通过观察可以发现这个问题是 对于我马上要形成三个棋子并且下一步是我下棋的估值过低 导致在不断的迭代过程中 自己形成二子的得分甚至高于防止对手形成三子 对于这种情况就要提高估值 在这个游戏中往后看两步足以取胜 我们选择迭代次数为2 也就是flagmax = 2

void toscore()//得到当前局面score值的函数
	{
		score=0;//score复位为0
		int i,j,o,x,n;//i.j循环用,o,x,n分别代表某一路(连续三子为一路)上oxn棋子的数目
		for(i=0;i<3;i++)//横向
		{
			o=0;x=0;n=0;//每一路之前要复位置零
			for(j=0;j<3;j++)
			{
				if(all[i][j]==O)//o计数
					o++;
				if(all[i][j]==X)//x计数
					x++;
				if(all[i][j]==N)//n计数
					n++;
 
				if(o+n==3)//当这一路上只有O棋子与空棋子时
				{
					if(o==3)//O有3子
						score=score+999999;//这种棋面已赢,评估值无限大
					if(o==2)//O有2子
					{
						if(flag%2==1)//如果这种棋面的层数是奇数,说明下一步是计算机下O棋,当某一路上已有2个O子时,已经必胜
									//评估值很大,但要小于对方已赢棋面评估值的绝对值,否则会产生不去围堵对方的胜招,而自顾自做2连子的棋面
							score=score+20000;
						else//如果下一步是人类下棋,这种局面的评估值不是很大
							score=score+1000;
					}
					if(o==1)//O有1子
						score=score+100;//加一点评估值
				}
 
				if(x+n==3)//当这一路上只有X棋子与空棋子时
				{
					if(x==3)//X有3子
						score=score-99999;//人类已经赢的棋面,评估值无限小
									//但绝对值要小于计算机已赢棋面的绝对值,否则会产生明明自己走在某处就可以直接胜利还偏偏去围堵对方的胜招的情况
					if(x==2)//X有2子
					{
						if(flag%2==0)//如果下一步是人类下棋,评估值很小
							score=score-10000;
						else//如果下一步是计算机下棋,评估值不是很小
							score=score-1000;
					}
					if(x==1)//X有1子
						score=score-100;//减一点评估值
				}
 
				//此处没有写oxn都有的情况 因为这种情况 这一条路 谁也赢不了
			}
		}
		for(i=0;i<3;i++)//竖向,下面同上
		{
			o=0;x=0;n=0;
			for(j=0;j<3;j++)
			{
				if(all[j][i]==O)
					o++;
				if(all[j][i]==X)
					x++;
				if(all[j][i]==N)
					n++;
				if(o+n==3)
				{
					if(o==3)
						score=score+999999;
					if(o==2)
					{
						if(flag%2==1)
							score=score+20000;
						else
							score=score+1000;
					}
					if(o==1)
						score=score+100;
				}
				if(x+n==3)
				{
					if(x==3)
						score=score-99999;
					if(x==2)
					{
						if(flag%2==0)
							score=score-10000;
						else
							score=score-1000;
					}
					if(x==1)
						score=score-100;
				}
			}
		}
		o=0;x=0;n=0;
		for(i=0;i<3;i++)//左上——右下,下面同上
		{
			if(all[i][i]==O)
				o++;
			if(all[i][i]==X)
				x++;
			if(all[i][i]==N)
				n++;
			if(o+n==3)
			{
				if(o==3)
					score=score+999999;
				if(o==2)
				{
					if(flag%2==1)
						score=score+20000;
					else
						score=score+1000;
				}
				if(o==1)
					score=score+100;
			}
			if(x+n==3)
			{
				if(x==3)
					score=score-99999;
				if(x==2)
				{
					if(flag%2==0)
						score=score-10000;
					else
						score=score-1000;
				}
				if(x==1)
					score=score-100;
			}
		}
		o=0;x=0;n=0;
		for(i=0;i<3;i++)//右上——左下,下面同上
		{
			if(all[i][2-i]==O)
				o++;
			if(all[i][2-i]==X)
				x++;
			if(all[i][2-i]==N)
				n++;
			if(o+n==3)
			{
				if(o==3)
					score=score+999999;
				if(o==2)
				{
					if(flag%2==1)
						score=score+20000;
					else
						score=score+1000;
				}
				if(o==1)
					score=score+100;
			}
			if(x+n==3)
			{
				if(x==3)
					score=score-99999;
				if(x==2)
				{
					if(flag%2==0)
						score=score-10000;
					else
						score=score-1000;
				}
				if(x==1)
					score=score-100;
			}
		}
	}
	
};

 下面完成极大值极小值搜索 但是这里的树由于使用线性表实现 因此是一棵线性的树 

简单的一字棋游戏 python 一字棋博弈_i++_05

上图是对比图注意 不同的颜色代表不同的深度 于是下面使用极大极小值搜索 一个回合(人一次 计算机一次)完成一次替换

for(int lag=closedtop;lag>0;lag--)//从closed表栈顶处向下扫描,通过叶子节点的score值给整个博弈树各节点的score赋值
		{
			if(closed[lag]->flag%2==0)//或节点 人类下棋时
			{//或节点的父节点是与节点,与节点的score值取其子节点中最小的score值
				if(closed[lag]->score<=closed[closed[lag]->parent]->score)//如果该节点的score值比其父节点的score值低
				{
					closed[closed[lag]->parent]->score=closed[lag]->score;//就把该节点的score值赋给父节点
				}
			}
			else//与节点 电脑下棋时
			{//与节点的父节点是或节点,或节点的score值取其子节点中最大的score值
				if(closed[lag]->score>=closed[closed[lag]->parent]->score)//如果该节点的score值比其父节点的score值高
				{
					closed[closed[lag]->parent]->score=closed[lag]->score;//就把该节点的score值赋给父节点
					if(closed[lag]->flag==1)//如果该节点是第一层节点,说明此时博弈树选择的第一层节点就是该节点
						tag=lag;//记录下该节点在closed表中的数组下标
				}
			}
		}

最后附上所有代码以供参考: 

#include<iostream>
using namespace std;
 
enum Chess{O,X,N};//棋子的类型:O代表计算机,X代表人类,N代表空
enum Node{and,or};//棋盘状态节点的类型:and代表与节点 我方下棋,or代表或节点 对手下棋
#define maxsize 9999//open和closed表的最大容量
#define flagmax 2//分析最高层数(也就是最多分析的回合),设为2时AI出错率为0,设为更高值时,会出现过拟合现象,导致拟合效果下降
 
class OXchess//棋盘状态节点类,用于博弈树搜索
{
 
public:
 
	Chess all[3][3];//当前棋盘状态
	int play[2];//当前落子位置 play[0]为x play[1]为y
	int parent;//当前父节点指针
	int score;//当前棋盘状态分数
	int flag;//当前扩展层数
	Node node;//当前节点类型
 
	OXchess()//构造函数,用来初始化棋盘状态节点
	{
		for(int i=0;i<3;i++)
			for(int j=0;j<3;j++)
				all[i][j]=N;//棋盘置空
		play[0]=-1;play[1]=-1;//落子为空
		parent=-2;//父节点指针为空
		score=9999;//score初始值
		flag=0;//flag初始值
		node=or;//节点类型初始值
	}
 
	~OXchess(){}//析构函数
 
	void should(OXchess *a,int closedtop,int x,int y,int step)//对每个新产生的节点应该做的操作的函数(落子、棋局打分)
	{
		for(int i=0;i<3;i++)//首先复制父节点的棋盘
			for(int j=0;j<3;j++)
			{
				all[i][j]=a->all[i][j]; 
			}
		play[0]=x;play[1]=y;//然后根据参数存储即将的落子
		parent=closedtop;//确定父节点指针	
		flag=a->flag+1;//当前层数为父节点+1
 
		if(flag%2==0)//当该节点层数为偶数时
		{
			score=-999999999;//score设为无限小,方便与其子节点中score较大的值比较,并获取其值
			node=or;//节点类型为或节点
			playchess(x,y,X);//在落子坐标放上人类的棋子
		}
 
		else//当该节点层数为奇数时
		{
			score=999999999;//score设为无限大,方便与其子节点中score较小的值比较,并获取其值
			node=and;//节点类型为与节点
			playchess(x,y,O);//在落子处坐标放上计算机的棋子
		}
 
		if(flag==flagmax || step==8)//当该节点为无法再扩展的节点时,扫描棋盘通过局面得到score值
		{
			toscore();//得到当前局面score值
		}
	}
	
	void playchess(int x,int y,Chess z)//落子的函数
	{
		all[x][y]=z;//在坐标x,y处落子z
	}
 
	void copyOX(OXchess *a)//复制同类对象的函数
	{
		for(int i=0;i<3;i++)
			for(int j=0;j<3;j++)
				all[i][j]=a->all[i][j];//复制棋盘
		play[0]=a->play[0];play[1]=a->play[1];//复制落子位置
		parent=a->parent;//复制父节点
		score=a->score;//复制score
		flag=a->flag;//复制层数
		node=a->node;//复制节点类型
	}
 
	void toscore()//得到当前局面score值的函数
	{
		score=0;//score复位为0
		int i,j,o,x,n;//i.j循环用,o,x,n分别代表某一路(连续三子为一路)上oxn棋子的数目
		for(i=0;i<3;i++)//横向
		{
			o=0;x=0;n=0;//每一路之前要复位置零
			for(j=0;j<3;j++)
			{
				if(all[i][j]==O)//o计数
					o++;
				if(all[i][j]==X)//x计数
					x++;
				if(all[i][j]==N)//n计数
					n++;
 
				if(o+n==3)//当这一路上只有O棋子与空棋子时
				{
					if(o==3)//O有3子
						score=score+999999;//这种棋面已赢,评估值无限大
					if(o==2)//O有2子
					{
						if(flag%2==1)//如果这种棋面的层数是奇数,说明下一步是计算机下O棋,当某一路上已有2个O子时,已经必胜
									//评估值很大,但要小于对方已赢棋面评估值的绝对值,否则会产生不去围堵对方的胜招,而自顾自做2连子的棋面
							score=score+20000;
						else//如果下一步是人类下棋,这种局面的评估值不是很大
							score=score+1000;
					}
					if(o==1)//O有1子
						score=score+100;//加一点评估值
				}
 
				if(x+n==3)//当这一路上只有X棋子与空棋子时
				{
					if(x==3)//X有3子
						score=score-99999;//人类已经赢的棋面,评估值无限小
									//但绝对值要小于计算机已赢棋面的绝对值,否则会产生明明自己走在某处就可以直接胜利还偏偏去围堵对方的胜招的情况
					if(x==2)//X有2子
					{
						if(flag%2==0)//如果下一步是人类下棋,评估值很小
							score=score-10000;
						else//如果下一步是计算机下棋,评估值不是很小
							score=score-1000;
					}
					if(x==1)//X有1子
						score=score-100;//减一点评估值
				}
 
				//此处没有写oxn都有的情况 因为这种情况 这一条路 谁也赢不了
			}
		}
		for(i=0;i<3;i++)//竖向,下面同上
		{
			o=0;x=0;n=0;
			for(j=0;j<3;j++)
			{
				if(all[j][i]==O)
					o++;
				if(all[j][i]==X)
					x++;
				if(all[j][i]==N)
					n++;
				if(o+n==3)
				{
					if(o==3)
						score=score+999999;
					if(o==2)
					{
						if(flag%2==1)
							score=score+20000;
						else
							score=score+1000;
					}
					if(o==1)
						score=score+100;
				}
				if(x+n==3)
				{
					if(x==3)
						score=score-99999;
					if(x==2)
					{
						if(flag%2==0)
							score=score-10000;
						else
							score=score-1000;
					}
					if(x==1)
						score=score-100;
				}
			}
		}
		o=0;x=0;n=0;
		for(i=0;i<3;i++)//左上——右下,下面同上
		{
			if(all[i][i]==O)
				o++;
			if(all[i][i]==X)
				x++;
			if(all[i][i]==N)
				n++;
			if(o+n==3)
			{
				if(o==3)
					score=score+999999;
				if(o==2)
				{
					if(flag%2==1)
						score=score+20000;
					else
						score=score+1000;
				}
				if(o==1)
					score=score+100;
			}
			if(x+n==3)
			{
				if(x==3)
					score=score-99999;
				if(x==2)
				{
					if(flag%2==0)
						score=score-10000;
					else
						score=score-1000;
				}
				if(x==1)
					score=score-100;
			}
		}
		o=0;x=0;n=0;
		for(i=0;i<3;i++)//右上——左下,下面同上
		{
			if(all[i][2-i]==O)
				o++;
			if(all[i][2-i]==X)
				x++;
			if(all[i][2-i]==N)
				n++;
			if(o+n==3)
			{
				if(o==3)
					score=score+999999;
				if(o==2)
				{
					if(flag%2==1)
						score=score+20000;
					else
						score=score+1000;
				}
				if(o==1)
					score=score+100;
			}
			if(x+n==3)
			{
				if(x==3)
					score=score-99999;
				if(x==2)
				{
					if(flag%2==0)
						score=score-10000;
					else
						score=score-1000;
				}
				if(x==1)
					score=score-100;
			}
		}
	}
	
};
 
class OX//游戏类实体
{
 
public:
 
	Chess chess[3][3];//记录当前棋盘状态
	int step;//记录当前已走步数
	Chess wholast;//记录上一步走棋的是谁
	bool ifend;//记录游戏是否结束
	bool winner;//记录游戏是否有胜者产生
	bool humanfirst;//记录是人先走还是计算机先走
 
	OX()//构造函数,初始化游戏
	{
		char command,del;//command用来接收指令 a.人先走棋 b.电脑先走 。 del用来吸收换行符。
		for(int i=0;i<3;i++)
			for(int j=0;j<3;j++)
				chess[i][j]=N;//初始化棋盘
		wholast=N;//初始化wholast
		step=0;//初始化step
		ifend=false;//初始化ifend
		winner=false;//初始化winner
		cout<<"一字棋启动ing......"<<endl<<"请选择(a.人先走棋 b.电脑先走):"<<endl<<"->";
		command=getchar();//获取指令
		del=getchar();//吸收换行符
		while(command!='a' && command!='b')//指令输错的时候要求玩家重新输
		{
			cout<<"小老弟走点心,按着我说的来,请重新选择(a.人先走棋 b.电脑先走):"<<endl<<"->";
			command=getchar();
			del=getchar();
		}
		if(command=='a')//初始化humanfirst
			humanfirst=true;
		else
			humanfirst=false;
	};
 
	~OX()//析构函数,在游戏结束时 输出棋盘 然后 输出游戏结果
	{
		display();//输出棋盘
		if(winner)//存在胜者时输出谁赢了
		{
			if(wholast==O)
				cout<<"我赢了!人类被我打败了 啊哈哈哈"<<endl;
			if(wholast==X)
				cout<<"人类竟然能打败我 这这这不可能..."<<endl;
		}
		else//不存在胜者时输出平局
			cout<<"咋们水平差不多,人类也就如此"<<endl;
	}
 
	void dis(Chess x)//输出棋子的函数
	{//保证棋盘中先走的人的棋子显示的是O,后走的人的棋子显示的是X(当计算机先走时,输出结果与棋盘存储结果相同,人先走时则相反)
 
		if(x==O)
		{
			if(humanfirst==true)
				cout<<"X";
			else
				cout<<"O";
		}
 
		if(x==X)
		{
			if(humanfirst==true)
				cout<<"O";
			else
				cout<<"X";
		}
 
		if(x==N)//棋子为空时输出空格
			cout<<" ";
		cout<<" ";
	}
 
	void display()//输出棋盘的函数
	{
		cout<<endl
		<<"   1   2   3"<<endl
		<<" ┏━┳━┳━┓"<<endl
		<<"1┃";dis(chess[0][0]);cout<<"┃";dis(chess[0][1]);cout<<"┃";dis(chess[0][2]);cout<<"┃"<<endl
		<<" ┣━╋━╋━┫"<<endl
		<<"2┃";dis(chess[1][0]);cout<<"┃";dis(chess[1][1]);cout<<"┃";dis(chess[1][2]);cout<<"┃"<<endl
		<<" ┣━╋━╋━┫"<<endl
		<<"3┃";dis(chess[2][0]);cout<<"┃";dis(chess[2][1]);cout<<"┃";dis(chess[2][2]);cout<<"┃"<<endl
		<<" ┗━┻━┻━┛"<<endl<<endl;
	}
 
	void play(int x,int y,Chess z)//下棋的函数,于棋盘chess[x][y]处下子z
	{
		chess[x][y]=z;
	}
 
	bool havewinner()//判断wholast是否获得了胜利
	{
		int i,j,z;//i,j用来循环,z用来记录一条线上wholast棋子的数目
		for(i=0;i<3;i++)//横向
		{
			z=0;
			for(j=0;j<3;j++)
			{
				if(chess[i][j]==wholast)
					z++;
				if(z==3)
					return true;
			}
		}
		for(i=0;i<3;i++)//竖向
		{
			z=0;
			for(j=0;j<3;j++)
			{
				if(chess[j][i]==wholast)
					z++;
				if(z==3)
					return true;
			}
		}
		z=0;
		for(i=0;i<3;i++)//左上——右下
		{
			if(chess[i][i]==wholast)
				z++;
			if(z==3)
				return true;
		}
		z=0;
		for(i=0;i<3;i++)//右上——左下
		{
			if(chess[i][2-i]==wholast)
				z++;
			if(z==3)
				return true;
		}
		return false;
	}
 
	void getend()//每次有计算机或玩家下子后即判断当前局势
	{
		bool x=havewinner();//判断下子的人是否赢了
		if(x)//如果赢了
		{
			ifend=true;//ifend置true,代表游戏已结束
			winner=true;//winner置true,代表产生胜者
		}
		if(step==9)//棋盘已满
			ifend=true;//ifend置true,代表游戏已结束,此时若winner保持初始值false,则代表游戏是平局
	}
 
	void humanplay()//人类下棋的函数
	{	
		int x,y;//用来记录步子,x为纵坐标,y为横坐标
		display();//输出当前棋盘状态给玩家看
		cout<<"小老弟让我看看你的高招:(例:若要下在棋盘中横坐标为1,纵坐标为3的位置,请输入:1+空格+3即:1 3)"<<endl<<"->";
		cin>>y>>x;//输入指令
		while(!(y>0 && x>0 && y<4 && x<4 && chess[x-1][y-1]==N) )//指令输错的时候要求玩家重新输
		{
			cout<<"你怎么不按规则和我下棋啊!:(例:若要下在棋盘中横坐标为1,纵坐标为3的位置,请输入:1+空格+3即:1 3)"<<endl<<"->";
			cin>>y>>x;
		}
		x--;y--;//棋盘坐标与数组坐标的变换
		play(x,y,X);//人类下棋
		wholast=X;//wholast置X,代表最近一步棋是人类下的
		step++;//已走步数+1
	}
 
	void computerplay()//计算机下棋的函数
	{
		int x[2]={-1,-1};//保存棋招
		minmax(x);//极大极小法获取棋招 传入的是数组首地址
		play(x[0],x[1],O);//按棋招下棋
		cout<<endl<<"计算机下在了位置:"<<x[1]+1<<" "<<x[0]+1<<endl;//输出下子的位置
		wholast=O;//wholast置O,代表最近一步棋是计算机下的
		step++;//已走步数+1
	}
 
	void minmax(int a[])//构建博弈树 并利用极大极小值算法获取结果
	{
		OXchess *open[maxsize],*closed[maxsize];//建立open表closed表
		int opentop=-1,openrear=-1,closedtop=-1,tag=-1;//open表为队列,closed表为栈,tag用于记录博弈树最终所选择的第一层节点
		opentop++;//当前局面入队,作为open表的第一个节点
		open[opentop]=new OXchess;//开辟空间
		firstOX(open[opentop]);//open表生成这次博弈树的第一个节点
		while(opentop!=openrear)//当open表不为空的时候
		{
			closedtop++;openrear++;//open表队尾元素出队,然后进入closed表入栈
			closed[closedtop]=new OXchess;//新节点开辟空间
			closed[closedtop]->copyOX(open[openrear]);//把元素复制过去
			free(open[openrear]);//再回收旧空间

			//只执行一次将所有可以落子的位置 遍历打分
			if(closed[closedtop]->flag<flagmax && step<9)//当棋盘未满,并且被考察节点的层数未到达最大层数限制时,生成新节点
			{
				for(int i=0;i<3;i++)
					for(int j=0;j<3;j++)
						if(closed[closedtop]->all[i][j]==N) //在firstox中已经将棋局传入
						{
							opentop++;//新节点于open表入队
							open[opentop]=new OXchess;//开辟空间
							open[opentop]->should(closed[closedtop],closedtop,i,j,step);//对新节点进行合适的操作
						}
			}
		}

		//极大极小搜索
		for(int lag=closedtop;lag>0;lag--)//从closed表栈顶处向下扫描,通过叶子节点的score值给整个博弈树各节点的score赋值
		{
			if(closed[lag]->flag%2==0)//或节点 人类下棋时
			{//或节点的父节点是与节点,与节点的score值取其子节点中最小的score值
				if(closed[lag]->score<=closed[closed[lag]->parent]->score)//如果该节点的score值比其父节点的score值低
				{
					closed[closed[lag]->parent]->score=closed[lag]->score;//就把该节点的score值赋给父节点
				}
			}
			else//与节点 电脑下棋时
			{//与节点的父节点是或节点,或节点的score值取其子节点中最大的score值
				if(closed[lag]->score>=closed[closed[lag]->parent]->score)//如果该节点的score值比其父节点的score值高
				{
					closed[closed[lag]->parent]->score=closed[lag]->score;//就把该节点的score值赋给父节点
					if(closed[lag]->flag==1)//如果该节点是第一层节点,说明此时博弈树选择的第一层节点就是该节点
						tag=lag;//记录下该节点在closed表中的数组下标
				}
			}
		}
		a[0]=closed[tag]->play[0];//获取最终博弈树选择的那个节点所存储的棋招纵坐标
		a[1]=closed[tag]->play[1];//获取最终博弈树选择的那个节点所存储的棋招横坐标
	}
 
	void firstOX(OXchess *a)//open表生成第一个节点的函数
	{
		a->parent=-1;//parent置-1
		for(int i=0;i<3;i++)
			for(int j=0;j<3;j++)
				a->all[i][j]=chess[i][j];// 从游戏实例获取棋盘局面
		a->score=-999999999;//score置无限小
	}
 
};
 
int main()//主函数
{
	OX OX1;//定义游戏实体,自动调用构造函数OX();初始化游戏
	if(OX1.humanfirst)//如果人先走
		OX1.humanplay();//那就人走
	else//如果计算机先走
		OX1.computerplay();//那就计算机走
	OX1.getend();//判断局势,判断ifend和winner的值是否需要改变
	while(!OX1.ifend)//如果游戏没结束
	{
		if(OX1.wholast==O)//如果上一步走棋的是计算机
			OX1.humanplay();//那么轮到人类走棋
		else//如果上一步走棋的是人类
			OX1.computerplay();//那么轮到计算机走棋
		OX1.getend();//判断局势,判断ifend和winner的值是否需要改变
	}//如果游戏结束,退出程序时自动调用OX类的析构函数~OX();,输出游戏结果。
	return 0;
}