“Network”游戏棋人机对弈的设计与实现(三)—让计算机走出合法步骤

本文介绍如何让计算机在'Network'游戏中走出合法的黑棋步骤,涉及'add'和'step'两种方式。通过判断棋子数量决定行动,使用NodeRecord数组记录坐标。在'add'阶段,检查老家区域并放置棋子;在'step'阶段,随机选择棋子移动并更新状态。未来计划采用MiniMax算法优化决策过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计算机能够判断步骤是否合法后,就可以让计算机走出合法的步骤了。通常使用MiniMax算法来使计算机做出最优选择,在运用该算法之前,首先让计算机能走出合法的步骤,这也是下一步运用MiniMax算法的基础。这里以电脑走黑棋为例进行说明。

“Network”总共有两种走棋方式,一种是“add"一种是”step",这在(一)中已经说明过,当棋盘上一方的棋子没有达到10个时走的是"add",达到10个后走的是"step"。因此这就需要棋盘对象要记录棋盘上双方棋子数,具体代码见(二)中的代码。本实现采用MachinePlayer类来代表计算机。

(1)首先计算机判断棋盘上的黑棋棋子数,如果小于10,则走"add",如果大于10,则走"step"。

(2)由于在"step"的时候需要选择一个棋子来进行移动,因此需要知道棋盘上双方棋子的坐标,这里采用NodeRecord[]数组来存储双方各自的棋子坐标。

(3)首先判断是”add"还是“step”,如果是“add",则首先判断黑方的老家区域是否有棋子,因为老家区域的棋子是构成"Network"的必要条件。如果没有则在老家区域放上棋子,在放之前要首先检查是否是合法步骤。

(4)在"add"情况下,若上下两个老家区域都有棋子,则选择棋盘内部的节点放置棋子,这里先暂时采用扫描坐标的方式进行放置(刚开始采用的是取随机数的方式,但是测试时发现这样效率很低,因为棋盘上坐标比较多,并且每次还需要判断是否合法,因此经常陷入无限循环的状态),后面会采用Minimax算法对这里进行改进。放置完棋子后,注意更新相关的域,包括棋盘上的黑棋子树以及NodeRecord数组。

(5)当10个黑棋子以放满,这时进入”step"阶段。由于NodeRecord中记录了所有黑棋子的坐标,因此通过选择一个0到9的随机数来决定移动哪个棋子。移动后需要将原有单元格清空,并且保留原有坐标(这是为了判断移动后的坐标做准备,首先清空是为了不影响原有坐标不影响新坐标的合法性判断,其次保留原有坐标是为了判断新坐标是否和老坐标相同,如果相同说明并没有走,因为这里暂时是以扫描的方式判断新坐标的,因此这步和老坐标的比较必不可少)。走完后,更新相关的域,包括NodeRecord,棋盘的状态。

具体代码如下:

/* MachinePlayer.java */

package player;

import java.util.Random;

import utils.Evaluation;
import gameboard.GameBoard;
import gameboard.NodeRecord;

/**
 *  An implementation of an automatic Network player.  Keeps track of moves
 *  made by both players.  Can select a move for itself.
 */
public class MachinePlayer extends Player {
	int color,serchDepth;
	GameBoard myGameBoard;
	public final static int BLACK = -1;
	public final static int WHITE = 1;
	public final static int EMPTY = 0;
	//保存已经放在棋盘上的棋子坐标
	private NodeRecord[] nodeRecord;
	private NodeRecord[] enemyNodeRecord;
	private int recordIndex, enemyIndex;
  // Creates a machine player with the given color.  Color is either 0 (black)
  // or 1 (white).  (White has the first move.)
  public MachinePlayer(int color) {
	  this.color = color;
	  this.serchDepth = 2;
	  myGameBoard = new GameBoard();
	  nodeRecord = new NodeRecord[10];
	  enemyNodeRecord = new NodeRecord[10];
	  recordIndex = 0;
	  enemyIndex = 0;
  }
  public int getColor() {
		return color;
  }
  public NodeRecord[] getNodeRecord() {
		return nodeRecord;
  }
  public NodeRecord[] getEnemyNodeRecord() {
		return enemyNodeRecord;
	}

  // Creates a machine player with the given color and search depth.  Color is
  // either 0 (black) or 1 (white).  (White has the first move.)
  public MachinePlayer(int color, int searchDepth) {
	  this.color = color;
	  this.serchDepth = searchDepth;
	  myGameBoard = new GameBoard();
	  nodeRecord = new NodeRecord[10];
	  enemyNodeRecord = new NodeRecord[10];
	  recordIndex = 0;
	  enemyIndex = 0;
  }

  // Returns a new move by "this" player.  Internally records the move (updates
  // the internal game board) as a move by "this" player.
  public Move chooseMove() {
	  
	  Move myMove = null;
	  //white moves
	  //white add move
	  if(this.color == WHITE){
		  if(myGameBoard.getWhiteNum() < 10){
			  if(myGameBoard.getWhiteNum() == 0){
				  myMove = new Move(0, 4);
				  myGameBoard.setBoard(0, 4, WHITE);
				  nodeRecord[recordIndex] = new NodeRecord(0, 4, WHITE);
				  recordIndex++;
			  }else if(isWLGoalEmpty()){
				  for(int vIndex = 1; vIndex < 7; vIndex++){
					  if(myGameBoard.isLeagleMove(0, vIndex, WHITE)){
						  myGameBoard.setBoard(0, vIndex, WHITE);
						  myMove = new Move(0, vIndex);
						  myGameBoard.setBoard(0, vIndex, WHITE);
						  nodeRecord[recordIndex] = new NodeRecord(0, vIndex, WHITE);
						  recordIndex++;
						  break;
					  }
				  }
			  //右边white goal空
			  }else if(isWRGoalEmpty()){
				  for(int vIndex = 1; vIndex < 7; vIndex++){
					  if(myGameBoard.isLeagleMove(7, vIndex, WHITE)){
						  myMove = new Move(7, vIndex);
						  myGameBoard.setBoard(7, vIndex, WHITE);
						  nodeRecord[recordIndex] = new NodeRecord(7, vIndex, WHITE);
						  recordIndex++;
						  break;
					  }
				  }
			  }else{
				  //随机选择内部点
//				  Random rand = new Random();
//				  rand.setSeed(System.currentTimeMillis());
//				  int hIndex = rand.nextInt(6) + 1;
//				  int vIndex = rand.nextInt(6) + 1;
//				  while(!myGameBoard.isLeagleMove(hIndex, vIndex, WHITE)){
//					  hIndex = rand.nextInt(6) + 1;
//					  hIndex = rand.nextInt(6) + 1;
//				  }
				  int hIndex = 0;
				  int vIndex = 1;
				  //扫描棋盘 ,找到合法的放置点
				  boolean flag = myGameBoard.isLeagleMove(hIndex, vIndex, WHITE);
				  while(flag == false){
					  for(int v = 1; v <= 6 ; v++){
						  for(int h = 0; h <= 7; h++){
							  flag = myGameBoard.isLeagleMove(h, v, WHITE);
							  if(flag == true){
								  hIndex = h;
								  vIndex = v;
								  break;
							  }
						  }
						  if(flag == true){
							  break;
						  }
					  }
				  }
				  myMove = new Move(hIndex, vIndex);
				  myGameBoard.setBoard(hIndex, vIndex, WHITE);
				  nodeRecord[recordIndex] = new NodeRecord(hIndex, vIndex, WHITE);
				  recordIndex++;
			  }
		  //White step moves
		  }else{
			  Random rand = new Random();
			  rand.setSeed(System.currentTimeMillis());
			  recordIndex = rand.nextInt(10);
//			  int hIndex = rand.nextInt(6) + 1;
//			  int vIndex = rand.nextInt(6) + 1;
//			  while(!myGameBoard.isLeagleMove(hIndex, vIndex, WHITE)){
//				  hIndex = rand.nextInt(6) + 1;
//				  hIndex = rand.nextInt(6) + 1;
//			  }
			  int oldHIndex = nodeRecord[recordIndex].gethIndex();
			  int oldVIndex = nodeRecord[recordIndex].getvIndex();
			  myGameBoard.setBoard(oldHIndex, oldVIndex, EMPTY);
			  int hIndex = 0;
			  int vIndex = 1;
			  //若旧坐标为(1,0),搜索起点应该从第二个节点开始,防止把棋子重新放回(1,0),没有此步骤程序则直接跳过下边的while循环
			  if(oldHIndex == 0 && oldVIndex == 1){
				  hIndex = 1;
			  }
			  
			  //扫描棋盘 ,找到合法的放置点
			  boolean flag = myGameBoard.isLeagleMove(hIndex, vIndex, WHITE);
			  while(flag == false){
				  for(int v = 1; v <= 6 ; v++){
					  for(int h = 0; h <= 7; h++){
						  flag = myGameBoard.isLeagleMove(h, v, WHITE);
						  if(flag == true){
							  //排除掉原有坐标,不允许走重复的步骤
							  if(h != oldHIndex || v != oldVIndex){
								  hIndex = h;
								  vIndex = v;
								  break;
							 //重新将标志置假,防止当棋子旧坐标为边缘点时内层循环跳出,但是此时坐标仍为旧坐标
							  }else{
								  flag = false;
							  }
							  
						  }
					  }
					  if(flag == true){
						  break;
					  }
//					  System.out.println("scaning......" + hIndex);
				  }
			  }
			  myMove = new Move(hIndex, vIndex, oldHIndex, oldVIndex);
			  nodeRecord[recordIndex].sethIndex(hIndex);
			  nodeRecord[recordIndex].setvIndex(vIndex);
			  nodeRecord[recordIndex].setColor(WHITE);
			  myGameBoard.setBoard(oldHIndex, oldVIndex, EMPTY);
			  myGameBoard.setBoard(hIndex, vIndex, WHITE);
		  }
	 //Black moves	  
	  }else{
		  if(myGameBoard.getBlackNum() < 10){
			  if(myGameBoard.getBlackNum() == 0){
				  myMove = new Move(4, 0);
				  myGameBoard.setBoard(4, 0, BLACK);
				  nodeRecord[recordIndex] = new NodeRecord(4, 0, BLACK);
				  recordIndex++;
			  }else if(isBUGoalEmpty()){
				  for(int hIndex = 1; hIndex < 7; hIndex++){
					  if(myGameBoard.isLeagleMove(hIndex, 0, BLACK)){
						  myMove = new Move(hIndex, 0);
						  myGameBoard.setBoard(hIndex, 0, BLACK);
						  nodeRecord[recordIndex] = new NodeRecord(hIndex, 0, BLACK);
						  recordIndex++;
						  break;
					  }
				  }
			  //下边black goal空
			  }else if(isBDGoalEmpty()){
				  for(int hIndex = 1; hIndex < 7; hIndex++){
					  if(myGameBoard.isLeagleMove(hIndex, 7, BLACK)){
						  myMove = new Move(hIndex, 7);
						  myGameBoard.setBoard(hIndex, 7, BLACK);
						  nodeRecord[recordIndex] = new NodeRecord(hIndex, 7, BLACK);
						  recordIndex++;
						  break;
					  }
				  }
			  }else{
				  //随机选择内部点
//				  Random rand = new Random();
//				  rand.setSeed(System.currentTimeMillis());
//				  int hIndex = rand.nextInt(6) + 1;
//				  int vIndex = rand.nextInt(6) + 1;
				  int hIndex = 1;
				  int vIndex = 0;
				  int i = 0;
				  //扫描棋盘 ,找到合法的放置点
				  boolean flag = myGameBoard.isLeagleMove(hIndex, vIndex, BLACK);
				  while(flag == false){
					  for(int v = 0; v <= 7 ; v++){
						  for(int h = 1; h <= 6; h++){
							  flag = myGameBoard.isLeagleMove(h, v, BLACK);
							  if(flag == true){
								  hIndex = h;
								  vIndex = v;
								  break;
							  }
						  }
						  if(flag == true){
							  break;
						  }
						  i++;
						  System.out.println("scaning......" + i);
						  System.out.println(flag);
					  }
				  }
//				  while(!myGameBoard.isLeagleMove(hIndex, vIndex, BLACK)){
//					  System.out.println("getRandom...........");
//					  hIndex = rand.nextInt(6) + 1;
//					  hIndex = rand.nextInt(6) + 1;  
//					  }
				  
				  
				  myMove = new Move(hIndex, vIndex);
				  //将对应坐标点设置为指定颜色  并且更新棋盘上对应棋子的数目
				  myGameBoard.setBoard(hIndex, vIndex, BLACK);
				  nodeRecord[recordIndex] = new NodeRecord(hIndex, vIndex, BLACK);
				  recordIndex++;
			  }
			  System.out.println(myGameBoard.getBlackNum());
		  //Black step moves, 需要仔细考虑重放的过程,扫描到原有的坐标后继续扫描
		  }else{
			  
			  Random rand = new Random();
			  rand.setSeed(System.currentTimeMillis());
			  recordIndex = rand.nextInt(10);
//			  int hIndex = rand.nextInt(6) + 1;
//			  int vIndex = rand.nextInt(6) + 1;
//			  while(!myGameBoard.isLeagleMove(hIndex, vIndex, BLACK)){
//				  hIndex = rand.nextInt(6) + 1;
//				  hIndex = rand.nextInt(6) + 1;
//				  System.out.println(hIndex);
//			  }
			  int oldHIndex = nodeRecord[recordIndex].gethIndex();
			  int oldVIndex = nodeRecord[recordIndex].getvIndex();
			  //将原有坐标点清空以免影响判断
			  myGameBoard.setBoard(oldHIndex, oldVIndex, EMPTY);
			  int hIndex = 1;
			  int vIndex = 0;
			  //若旧坐标为(1,0),搜索起点应该从第二个节点开始,防止把棋子重新放回(1,0),没有此步骤程序则直接跳过下边的while循环
			  if(oldHIndex == 1 && oldVIndex == 0){
				  hIndex = 2;
			  }
			  
			  //扫描棋盘 ,找到合法的放置点
			  boolean flag = myGameBoard.isLeagleMove(hIndex, vIndex, BLACK);
			  while(flag == false){
				  for(int v = 0; v <= 7 ; v++){
					  for(int h = 1; h <= 6; h++){
						  flag = myGameBoard.isLeagleMove(h, v, BLACK);
						  if(flag == true){
							  //排除掉原有坐标,不允许走重复的步骤
							  if(h != oldHIndex || v != oldVIndex){
								  hIndex = h;
								  vIndex = v;
								  break;
							 //重新将标志置假,防止当棋子旧坐标为边缘点时内层循环跳出,但是此时坐标仍为旧坐标
							  }else{
								  flag = false;
							  }
						  }
					  }
					  if(flag == true){
						  break;
					  }
//					  System.out.println("scaning......" + hIndex);
				  }
			  }
			  myMove = new Move(hIndex, vIndex, oldHIndex, oldVIndex);
			  nodeRecord[recordIndex].sethIndex(hIndex);
			  nodeRecord[recordIndex].setvIndex(vIndex);
			  nodeRecord[recordIndex].setColor(BLACK);
			  myGameBoard.setBoard(oldHIndex, oldVIndex, EMPTY);
			  myGameBoard.setBoard(hIndex, vIndex, BLACK);
			  
		  }
	  }
	  
    return myMove;
  } 
private boolean isBDGoalEmpty() {
	  for(int hIndex = 1; hIndex < 7; hIndex++){
			if(myGameBoard.getGameBoard(hIndex, 7) != EMPTY){
				return false;
			}
		}
		return true;
}

private boolean isBUGoalEmpty() {
	  for(int hIndex = 1; hIndex < 7; hIndex++){
			if(myGameBoard.getGameBoard(hIndex, 0) != EMPTY){
				return false;
			}
		}
		return true;
}

private boolean isWRGoalEmpty() {
	  for(int vIndex = 1; vIndex < 7; vIndex++){
			if(myGameBoard.getGameBoard(7, vIndex) != EMPTY){
				return false;
			}
		}
		return true;
}

private boolean isWLGoalEmpty() {
	for(int vIndex = 1; vIndex < 7; vIndex++){
		if(myGameBoard.getGameBoard(0, vIndex) != EMPTY){
			return false;
		}
	}
	return true;
}

  // If the Move m is legal, records the move as a move by the opponent
  // (updates the internal game board) and returns true.  If the move is
  // illegal, returns false without modifying the internal state of "this"
  // player.  This method allows your opponents to inform you of their moves.
  public boolean opponentMove(Move m) {
	  int opponentColor;
	  if(this.color == WHITE){
		  opponentColor = BLACK;
	  }else{
		  opponentColor = WHITE;
	  }
	  int hIndex, vIndex;
	  hIndex = m.x1;
	  vIndex = m.y1;
	  //在判断前将原来坐标点清空,防止影响判断
	  if(m.moveKind == Move.STEP){
		  myGameBoard.setBoard(m.x2, m.y2, EMPTY);
		  for(int i = 0; i < 10; i++){
			  if(enemyNodeRecord[i].gethIndex() == m.x2 && enemyNodeRecord[i].getvIndex() == m.y2){
				  enemyIndex = i;
			  }
		  }
	  }
	  if(myGameBoard.isLeagleMove(hIndex, vIndex, opponentColor)){
		  myGameBoard.setBoard(hIndex, vIndex, opponentColor);
		  if(m.moveKind == Move.ADD){
			  NodeRecord newNode = new NodeRecord(hIndex, vIndex, opponentColor);
			  enemyNodeRecord[enemyIndex] = newNode;
			  enemyIndex++;
		  }else{
			  enemyNodeRecord[enemyIndex].sethIndex(hIndex);
			  enemyNodeRecord[enemyIndex].setvIndex(vIndex);
		  }
		  return true;
	  //若不合法,将原有坐标还原,step move不成功
	  }else{
		  myGameBoard.setBoard(m.x2, m.y2, opponentColor);
	  }
    return false;
  }

  // If the Move m is legal, records the move as a move by "this" player
  // (updates the internal game board) and returns true.  If the move is
  // illegal, returns false without modifying the internal state of "this"
  // player.  This method is used to help set up "Network problems" for your
  // player to solve.
  public boolean forceMove(Move m) {
	  int hIndex, vIndex;
	  hIndex = m.x1;
	  vIndex = m.y1;
	  if(myGameBoard.isLeagleMove(hIndex, vIndex, this.color)){
		  myGameBoard.setBoard(hIndex, vIndex, this.color);
		  return true;
	  }
    return false;
  }

}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值