⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 _gamer.cpp

📁 利用c++编写的带人工智能的跳棋程序。屏幕的中央是棋盘
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//此文件定义了游戏者类以及由它派生出的human和robot类
#include "_Gamer.h"
#include <iostream.h>
#include<conio.h>
#include<math.h>
#define ReachAim 		10*27			// 棋子达到目的地的加分
#define DisStep 		1
#define EnemyScoreQuotiety 	0.95
#define ConverDepth 		550
#define DisjointVal 		1


void _Gamer::Init(int *IDandChess,_Nodes* p)		// 游戏者的初始化,参数分别为指向含有游戏者序号和
							// 该游戏者所拥有的棋子的数组指针,指向棋盘的指针
{
	Nodes=p;  					// 获得棋盘上的所有棋子
	Sellect=-1;					// 没有选定棋子
	Aimplace=-1;					// 没有目的节点
	gamerID=IDandChess[0];				// IDandChess[]数组的第一个元素即为gamerID
	for(int i=0;i<10;i++)				// 给游戏者的棋子赋值,从棋盘reset函数中获得初始的
							// 棋子的节点序号
	{
		MyChessman[i]=IDandChess[i+1];
	}
}

//---------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------
_Message _Human::Input()				// 消息控制
{
	static int position=60;				// position置为60即在棋盘的最中央
	_Message msg;					// 用于返回的消息
	char ch;
	int p,q,i,j,k;
	ch=getch();  					// 获得棋盘输入的信息
	i=position;					// i是当前的选择框位置
	switch(ch)
	{
		 case 'Q':				// Q则退出
				msg.wparam=EXIT;
				return  msg;
		 break;
		 case 'q':				// Q则退出
				msg.wparam=EXIT;
				return  msg;
		 break;
		 case 'R':				// R则重新开局
				msg.wparam=RESTART;
				return msg;
		 break;
		 case 'r':				// R则重新开局
				msg.wparam=RESTART;
				return msg;
		 break;
		 case 'H':				// H则为悔棋
				msg.wparam=REGRET;
				return msg;
		 break;
		 case 'h':				// H则为悔棋
				msg.wparam=REGRET;
				return msg;
		 break;
		 case 'a':				// a为向左移动
			 if(i!=0) 			// 如果不是最顶端的棋子,则向左移动一格,
				position=i-1;		// 若已是最左端,则移动到上一行最右边的节点
							// 若是最顶端则不作移动
			 msg.wparam=MOVESELLECT;	// 消息类型为移动选择框
			 msg.lparam=position;		// msg.lparam为当前选择框所在的节点序号
			 return  msg;
		  break;
		  case 'A':				// a为向左移动
			 if(i!=0) 			// 如果不是最顶端的棋子,则向左移动一格,
				position=i-1;		// 若已是最左端,则移动到上一行最右边的节点
							// 若是最顶端则不作移动
			 msg.wparam=MOVESELLECT;	// 消息类型为移动选择框
			 msg.lparam=position;		// msg.lparam为当前选择框所在的节点序号
			 return  msg;
		  break;
		  case 'd':				// d为向右走棋
			  if(i!=120)			// 如果不是最底端棋子的话,右移一格,若已是最
				position=i+1;		// 右端,则移动到下一行的最左边的节点
							// 若是最底端则不作移动
			  msg.lparam=position;		// msg.lparam为当前选择框所在的节点序号
			  msg.wparam=MOVESELLECT;	// 消息类型为移动选择框
			  return  msg;
		  break;
		  case 'D':				// d为向右走棋
			  if(i!=120)			// 如果不是最底端棋子的话,右移一格,若已是最
				position=i+1;		// 右端,则移动到下一行的最左边的节点
							// 若是最底端则不作移动
			  msg.lparam=position;		// msg.lparam为当前选择框所在的节点序号
			  msg.wparam=MOVESELLECT;	// 消息类型为移动选择框
			  return  msg;
		  break;
		  case 'w':				// w为向上走棋
			  k=i;
			  if(i!=0)
			  {
				  p=Nodes[i].cood.x;	// 取得当前节点的位置坐标
				  q=Nodes[i].cood.y;
				  for(;abs(Nodes[i-1].cood.y-Nodes[i].cood.y)<3;i--);
							// 若序号为i-1的节点和序号为i的节点在同一行则循环(没有循环体)
				  i--;			// 找到上一行最右端的棋子
				  for(j=i-1;abs(Nodes[i].cood.y-Nodes[j].cood.y)<3;j--)
							// 序号为i的节点和序号为j的节点在同一行,循环
					  if(abs(Nodes[j].cood.x-p)<abs(Nodes[i].cood.x-p))
							// 寻找横坐标与开始时的横坐标最相近的点
						i=j;	// 找到这个点
			  }
			  position=i;
			  msg.wparam=MOVESELLECT;	// 消息类型为移动选择框
			  msg.lparam=position;		// msg.lparam为当前选择框所在的节点序号
			  return msg;
			 break;
		 case 'W':				// w为向上走棋
			  k=i;
			  if(i!=0)
			  {
				  p=Nodes[i].cood.x;	// 取得当前节点的位置坐标
				  q=Nodes[i].cood.y;
				  for(;abs(Nodes[i-1].cood.y-Nodes[i].cood.y)<3;i--);
							// 若序号为i-1的节点和序号为i的节点在同一行则循环(没有循环体)
				  i--;			// 找到上一行最右端的棋子
				  for(j=i-1;abs(Nodes[i].cood.y-Nodes[j].cood.y)<3;j--)
							// 序号为i的节点和序号为j的节点在同一行,循环
					  if(abs(Nodes[j].cood.x-p)<abs(Nodes[i].cood.x-p))
							// 寻找横坐标与开始时的横坐标最相近的点
						i=j;	// 找到这个点
			  }
			  position=i;
			  msg.wparam=MOVESELLECT;	// 消息类型为移动选择框
			  msg.lparam=position;		// msg.lparam为当前选择框所在的节点序号
			  return msg;
			 break;

		 case 's':				// s为向下走棋
			  k=i;
			  if(i!=120)
			  {
				  p=Nodes[i].cood.x;	// 取得当前节点的位置坐标
				  q=Nodes[i].cood.y;
				  for(;abs(Nodes[i+1].cood.y-Nodes[i].cood.y)<3;i++);
							// 序号为i+1的节点和序号为i的节点在同一行
				  i++;			// 找到下一行最左端的棋子
				  for(j=i+1;abs(Nodes[j].cood.y-Nodes[i].cood.y)<3;j++)
							// 序号为i的节点和序号为j的节点在同一行
					if(abs(Nodes[j].cood.x-p)<abs(Nodes[i].cood.x-p))
							// 寻找横坐标与开始时的横坐标最相近的点
						i=j;	// 找到这个点
			  }
			  position=i;
			  msg.wparam=MOVESELLECT; 	// 消息类型为移动选择框
			  msg.lparam=position;		// msg.lparam为当前选择框所在的节点序号
			  return msg;
			  break;
		 case 'S':				// s为向下走棋
			  k=i;
			  if(i!=120)
			  {
				  p=Nodes[i].cood.x;	// 取得当前节点的位置坐标
				  q=Nodes[i].cood.y;
				  for(;abs(Nodes[i+1].cood.y-Nodes[i].cood.y)<3;i++);
							// 序号为i+1的节点和序号为i的节点在同一行
				  i++;			// 找到下一行最左端的棋子
				  for(j=i+1;abs(Nodes[j].cood.y-Nodes[i].cood.y)<3;j++)
							// 序号为i的节点和序号为j的节点在同一行
					if(abs(Nodes[j].cood.x-p)<abs(Nodes[i].cood.x-p))
							// 寻找横坐标与开始时的横坐标最相近的点
						i=j;	// 找到这个点
			  }
			  position=i;
			  msg.wparam=MOVESELLECT; 	// 消息类型为移动选择框
			  msg.lparam=position;		// msg.lparam为当前选择框所在的节点序号
			  return msg;
			  break;
		 case ' ':				// 空格为选定棋子
			 if(this ->Sellect==-1)		// 如果游戏者还未选定棋子
				msg.wparam=SELLECT;	// 则消息类型为选子
			 else
				msg.wparam=MOVECHESSMAN;// 否则为走棋
			 msg.lparam=position;		// msg.lparam为当前选择框所在的节点序号
			 return msg;
			 break;
		 default:break;
	 }
	 msg.wparam=REINPUT;				// 什么都没有则重新输入
	 return msg;
}

//-------------------------------------------------------------------------------------
_Message _Robot::Input()
{
	_Message msg;					// 传递消息的变量
	SearchDepth=4;					// 搜索深度为4
	Think();					// 思考的过程
	msg.wparam=MOVECHESSMAN;			// 只有移动棋子这一种消息
    	msg.lparam=Aimplace;				// 目的节点
    	return msg;
}

_Robot::_Robot()                   			// Robot的构造函数
{
    GamerType = 0;					// 游戏者类型为0,即电脑控制
    GamerQuen = 0;					// 游戏者顺序列表为空
    SearchDepth = 4;       				// 搜索深度是4
    NearWin = 0;					// 表示还没有进入中盘
    FarWin = 1;					 	// 表示是刚开局
}

_Robot::~_Robot()                          		// Robot的析构函数
{
    _GamerQuen *t;					// 用于释放游戏者顺序链表指针的中间变量
    while( GamerQuen != 0 )                		// 从前往后析构(删除指针)
    {
        t = GamerQuen;
        if(t->Next != 0)
        {
            GamerQuen = t->Next ;
            delete t;
            t = 0;
        }
        else break;
    }
    delete t;
    t = 0;
}

//-------------------------------------------------------------------------------------
void _Robot::Think()					// 电脑玩家的思考
{
	_Nodes tNodes[121];				// 存放棋盘的临时数组
	_EvalueData data,data2,data3;			// 存放全搜索的不同深度的评估值
	for(int i=0;i<121;i++)  			// 将棋盘拷贝到tNodes中
	{
    	tNodes[i] = Nodes[i];
        for(int j=0;j<6;j++)  				// 指针的拷贝需要特别注意
            if( Nodes[i].pointers[j] != NULL )
                tNodes[i].pointers[j] = &tNodes[ Nodes[i].pointers[j]->index ];
    	}
    	/* 通常情况下,电脑玩家只需一步或者两步就可以胜利,但是由于搜索的深度大于这个步数,电脑会
    	继续搜索,以至于左右走动而不能够赢棋,下面的代码就是为了解决这个问题是写的 */
    if( NearWin ==1 || FarWin ==1)         		// 如果将要胜利,则搜索一步或者两步,
    {                            			// 取得其中的较大的评估值
        data = Search(tNodes,1); 			// 存放较大的评估值
        data2 = Search(tNodes,2);			// 搜索一步
        data3 = Search(tNodes,3);			// 搜索两步
        if( data.Score < data2.Score )
            data = data2;
        if( data.Score < data3.Score )
            data = data3;
    }
    else						//若离胜利还远,则采用剪枝法搜索
        data = aBSearch(tNodes,SearchDepth,20000);
    Sellect = data.start ;				// 确定要选择的棋子
    Aimplace = data.end ;				// 确定目的节点
}

//-------------------------------------------------------------------------------------
_EvalueData _Robot::Evalue(_Nodes *tNodes,int type,int enemy)	
{							// 评估函数,参数分别为指向棋盘的指针,
							// 游戏者序号,对手的序号

    _EvalueData data[2] = { {0,0,0},{0,0,0} };		//存放本方和对手的评估函数值
    int Pos[2][10],k=0,h=0,i,j,walkway,Sumdis;
    _Nodes *tnode;
    for(i=0;i<121;i++)					// 找到棋盘上属于本方和对手的棋子
    {
        if( tNodes[i].Chessman == type )		// pos[0]中存放本方的十个棋子
            Pos[0][k++] = i ;
        else if( tNodes[i].Chessman == enemy )		// pos[1]中存放对手的十个棋子
                 Pos[1][h++] = i ;
    }

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -