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

📄 gobangai.java

📁 java写的五子棋
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
	 *@return 返回新的列数
	 */
	protected int jMove(int j,int dir)
	{
		switch (dir)
		{
		case LEFT:
		case UP_LEFT:
		case DOWN_LEFT:
			j--;                          //向左
		break;
		case RIGHT:
		case UP_RIGHT:
		case DOWN_RIGHT:
			j++;                          //向右
		break;
		default:
		}
		return j;
	}

/*======================2.对棋局评分的各个方法=======================*/
	/**
	 *对于当前角色记录棋局中每一个空格的每个方向情况。即是求situation
	 *@param player 当前角色
	 *@param i 棋的行数
	 *@param j 棋的列数
	 *return situation 每一个空格的情况。
	 *其中,前两个索引是横纵坐标,第三个索引是方向
	 */
	public void getSituation(int player, int i, int j)
	{
		int step;                                          //距离空格的步数
		int iTest, jTest;                                  //要检验的格
		dirContinue:
		for (int dir = 0; dir < DIR; dir++)     //对于每一个方向
		{
			//iTest,jTest分别指向i,j
			iTest = i;
			jTest = j;
			for (step = 1; true; step ++)
			{
				if (step == 5)
				//找到连株
				{
					situation[player][i][j][dir] = FINISH_BANG;
					continue dirContinue;      //下一个方向
				}
				//向指定的方向移动一格
				iTest = iMove(iTest, dir);
				jTest = jMove(jTest, dir);
				if (! ifIn(iTest,jTest) || 
					tableAI[iTest][jTest] == antiPlayer(player))
				//若iTest,jTest越界,或是对手的棋
				{
					situation[player][i][j][dir] = DEAD_ZERO + step - 1;
					continue dirContinue;      //下一个方向
				}
				else if (tableAI[iTest][jTest] == NOTHING)
				//若iTest,jTest是空位
				{
					situation[player][i][j][dir] = LIFE_ZERO + step - 1;
					continue dirContinue;      //下一个方向
				}
				else if (tableAI[iTest][jTest] == player);
				//若iTest,jTest是自己的棋,不需操作
			}
		}
	}

	/**
	 *获得棋局中每一个空格的四个方向(即在一条直线上)的评分。即是求MarkLink
	 *@param player 当前角色
	 *@param i 棋的行数
	 *@param j 棋的列数
	 */
	public void getLinkMark(int player, int i, int j)
	{
		int tempMark;                                          //临时记分
		int tempMarkLink = 0;
		dirContinue:
		for (int dir = 0; dir < DIR/2; dir ++) //对于四条直线的方向
		{
			//把(一条直线上)两个方向的情况记到临时记分
			tempMark = situation[player][i][j][dir] + 
					   situation[player][i][j][dir + 4];
			if (tempMark >= 100)                  //找到连珠
			{
				tempMarkLink = LINK_BANG;  //连珠
				markLink[player][i][j][dir] = tempMarkLink;
				continue dirContinue;      //下一个方向
			}
			switch (tempMark)
			{
			case 0:   //活一
				tempMarkLink = LINK_LIFE_ONE;   
				break;
			case 1:   //活二
				tempMarkLink = LINK_LIFE_TWO;
				break;
			case 2:   //活三
				tempMarkLink = LINK_LIFE_THREE;
				break;
			case 3:   //活四
				tempMarkLink = LINK_LIFE_FOUR;
				break;
			case 4:
			case 5:
			case 6:   //连珠
				tempMarkLink = LINK_BANG;
				break;
			case 10:  //死一
				tempMarkLink = LINK_DEAD_ONE;
				break;
			case 11:  //死二
				tempMarkLink = LINK_DEAD_TWO;
				break;
			case 12:  //死三
				tempMarkLink = LINK_DEAD_THREE;
				break;
			case 13:  //死四
				tempMarkLink = LINK_DEAD_FOUR;
				break;
			case 14:
			case 15:
			case 16:   //连珠
				tempMarkLink = LINK_BANG;
				break;
			case 20:
			case 21:
			case 22:
			case 23:
				tempMarkLink = LINK_DEAD;
				break;
			case 24:
			case 25:
			case 26:   //连珠
				tempMarkLink = LINK_BANG;
				break;
			default:
				tempMarkLink = LINK_DEAD;
			}
			markLink[player][i][j][dir] = tempMarkLink;
		}
	}

	/**
	 *获得棋局中每一个空格的总评分。即是求mark
	 *@param player 当前角色
	 *@param i 棋的行数
	 *@param j 棋的列数
	 */
	public void getTotleMark(int player, int i, int j)
	{
		int tempMark = 0;
		int lastLinkMark = 0;            //记录上一次的评分,采用markLink的值
		for (int dir = 0; dir < DIR/2; dir ++)
		{
			switch (markLink[player][i][j][dir])
			{
			case LINK_DEAD_TWO:                              //死2
				if (lastLinkMark < markLink[player][i][j][dir])
				//前面的评分少于当前的markLink[player][i][j][dir]
				{
					lastLinkMark = markLink[player][i][j][dir];
					tempMark = MARK_D2;
				}
				break;
			case LINK_LIFE_TWO:                              //活2
				if (lastLinkMark < markLink[player][i][j][dir])
				{
					lastLinkMark = markLink[player][i][j][dir];
					tempMark = MARK_L2;
				}
				else if (lastLinkMark == LINK_LIFE_TWO)
				//若前面的评分与当前markLink[player][i][j][dir]都是活2
				{
					tempMark = MARK_L2L2;             //双活2
				}
				break;
			case LINK_DEAD_THREE:                            //死3
				if (lastLinkMark < markLink[player][i][j][dir] &&
					tempMark != MARK_L2L2)
				//前面的评分不是双活2
				{
					lastLinkMark = markLink[player][i][j][dir];
					tempMark = MARK_D3;               //单死3
				}
				if (lastLinkMark == LINK_LIFE_THREE)
				//前面已经有一个活3
				{
					tempMark = MARK_D3L3;             //死3活3
				}
				break;
			case LINK_LIFE_THREE:                            //活3
				if (lastLinkMark == LINK_DEAD_THREE)
				//前面已经有一个死3
				{
					lastLinkMark = markLink[player][i][j][dir];
					tempMark = MARK_D3L3;             //死3活3
				}
				else if (lastLinkMark < markLink[player][i][j][dir])
				{
					lastLinkMark = markLink[player][i][j][dir];
					tempMark = MARK_L3;               //单活3
				}
				else if (lastLinkMark == LINK_LIFE_THREE)
				//若前面的评分与当前markLink[player][i][j][dir]都是活3
				{
					tempMark = MARK_L3L3;             //双活3
				}
				break;
			case LINK_DEAD_FOUR:
				if (lastLinkMark == LINK_DEAD_FOUR ||
					lastLinkMark == LINK_LIFE_THREE)
				//前面已经有一个死4 或 活3
				{
					tempMark = MARK_L4_D4D4_D4L3;     //双死4 或 死4活3
				}
				else if (lastLinkMark < markLink[player][i][j][dir])
				{
					lastLinkMark = markLink[player][i][j][dir];
					tempMark = MARK_D4;               //单死4
				}
				break;
			case LINK_LIFE_FOUR:                             //活4
				if (lastLinkMark < markLink[player][i][j][dir])
				{
					lastLinkMark = markLink[player][i][j][dir];
					tempMark = MARK_L4_D4D4_D4L3;
				}
				break;
			case LINK_BANG:                                  //连珠
				tempMark = MARK_BANG;
				lastLinkMark = markLink[player][i][j][dir];
				//System.out.println(i + "  " + j + "find bang   " + tempMark);    //测试
				break;
			default:
			}
		}
		mark[player][i][j] = tempMark;                       //作记录
		//System.out.println(i +"," + j +"tempMark   " + mark[player][i][j]);
	}

	/**
	 *搜索出最高利益的COUNT个空格的坐标(连接所有关于评分的方法)
	 *@param player 当前角色
	 *@return 返回最高分的COUNT个空格的坐标,用二维数组表示
	 */
	public int[][] getMaxProfitPoint(int player)
	{
		//遍历每一个坐标,分三步进行评分
		for (int i = 0 ; i < BOARD_SIZE ; i++)
		{
			for (int j = 0 ; j < BOARD_SIZE ; j++)
			{
				if (tableAI[i][j] == NOTHING)                    //若是空格
				{
					getSituation(COMPUTER, i, j);
					getLinkMark(COMPUTER, i, j);
					getTotleMark(COMPUTER, i, j);
					getSituation(PLAYER, i, j);
					getLinkMark(PLAYER, i, j);
					getTotleMark(PLAYER, i, j);
				}
			}
		}
		//maxPoints记录最高利益的COUNT个空格的坐标:
		//第一个索引是表示第几个最高分点;
		//第二个索引有三个元素,分别是iMax,jMax,profitMax。
		int[][] maxPoints = new int[COUNT][3];
		int iMax, jMax, profitMax, profitTemp;
		for (int count = 0; count < COUNT; count ++)  //第count个最高利益点
		{
			iMax = 0;
			jMax = 0;
			profitMax = 0;       //最大利益为两个角色在空格得分的加权平均值
			//遍历每一个坐标
			for (int i = 0 ; i < BOARD_SIZE ; i++)
			{
				for (int j = 0 ; j < BOARD_SIZE ; j++)
				{
					//利益=权*本角色的分数 + (10-权)*对手的分数
					profitTemp = WEIGHT * mark[player][i][j] +
							(10 - WEIGHT) * mark[antiPlayer(player)][i][j];
					if (profitMax < profitTemp)
					{
						iMax = i;
						jMax = j;
						profitMax = profitTemp;
					}
				}
			}
			//把第count个最高利益点记录在maxPoints
			maxPoints[count][I_MAX] = iMax;
			maxPoints[count][J_MAX] = jMax;
			maxPoints[count][PROFIT_MAX] = profitMax;
			//把这个最高利益点的分数改为零,以便找出下一个最高分点
			mark[COMPUTER][iMax][jMax] = 0;
			mark[PLAYER][iMax][jMax] = 0;
		}
		return maxPoints;
	}
	
/*========================3.求AI的主要的方法=======================*/
	/**
	 *用AI求出电脑下一步的坐标
	 *@return 返回电脑下一步的坐标的数组
	 */
	public int[] GobangAImain()
	{
		//先把table的内容记录到tableAI
		for (int i = 0 ; i < BOARD_SIZE ; i++)
		{
			for (int j = 0 ; j < BOARD_SIZE ; j++)
			{
				tableAI[i][j] = table[i][j];
			}
		}
		//定义各个临时变量
		int iBest = 0;                                 //最好的空格的行数
		int jBest = 0;                                 //最好的空格的列数
		int profit = 0;                                //当前考虑的棋的利益
		int profitMax = -9999;                         //最大的利益初始化
		//找出最高利益的COUNT个空格的坐标
		int[][] bestProfit = getMaxProfitPoint(COMPUTER);
		int i;                                     //当前考虑的行数
		int j;                                     //当前考虑的列数
		for (int count = 0; count < COUNT; count ++)   //第count个最高分点
		{
			i = bestProfit[count][I_MAX];            //这个最高分点的行数
			j = bestProfit[count][J_MAX];            //这个最高分点的列数
			//利益 = 获取本角色利益值() - GobangAImain(角色=相反角色,层次-1); 
			profit = bestProfit[count][PROFIT_MAX] - 
					 GobangAI(PLAYER, DEPTH-1, i, j);
			//System.out.println("i = " + i + "; j = " + j +"; profit = " + profit);         //测试用
			if (profitMax < profit)
			{
				profitMax = profit;
				iBest = i;                          //纪录这个空格的行数
				jBest = j;                          //纪录这个空格的列数
				//System.out.println("profit = " + profit);         //测试用
			}
		}
		//遍历每一个坐标
		if (tableAI[iBest][jBest] != NOTHING)         //若所求的位置有棋
		{
			System.out.println("Error!");
			System.exit(0);
		}
		return new int[]{iBest, jBest};
	}

	/**
	 *考虑往后几步的递归方法
	 *@param player 当前角色
	 *@param depth 考虑剩下depth步
	 *@param iLast 上一步的棋的行数
	 *@param jLast 上一步的棋的列数
	 *@return 返回这个空格的利益(来源于mark的值)
	 */
	public int GobangAI(int player, int depth, int iLast, int jLast)
	{
		
		int profit = 0;                            //当前考虑的棋的利益
		int profitMax = -9999;                     //最大的利益初始化
		if (depth == 0)
		//考虑到最低层了,返回0利益
		{
			return 0;
		}
		//先记录上一步,对手下的棋
		tableAI[iLast][jLast] = antiPlayer(player);
		//找出最高分的COUNT个空格的坐标
		int[][] bestProfit = getMaxProfitPoint(player);

		//System.out.println("mark = " + bestProfit[0][PROFIT_MAX]);         //测试用
		int i;                                     //当前考虑的行数
		int j;                                     //当前考虑的列数
		for (int count = 0; count < COUNT; count ++)   //第count个最高分点
		{
			i = bestProfit[count][I_MAX];            //这个最高分点的行数
			j = bestProfit[count][J_MAX];            //这个最高分点的列数
			//利益 = 获取本角色利益值() - GobangAImain(角色=相反角色,层次-1); 
			profit = bestProfit[count][PROFIT_MAX] - 
					 GobangAI(antiPlayer(player), depth-1, i, j);
			if (profitMax < profit)
			{
				profitMax = profit;

				//System.out.println("profit = " + profit);         //测试用
			}
		}
		tableAI[iLast][jLast] = NOTHING;              //退回这步棋
		return profitMax;
	}
}

⌨️ 快捷键说明

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