Unity3D | 智能牧师与魔鬼

P&D 过河游戏智能帮助实现,程序具体要求:

  • 实现状态图的自动生成
  • 讲解图数据在程序中的表示方法
  • 利用算法实现下一步的计算
游戏状态

游戏过程实际上是游戏状态之间的转换,状态记录的内容包括:

public class IState {
    public int leftPriests;
    public int leftDevils;
    public int rightPriests;
    public int rightDevils;
    public bool boat;//船在左岸true或右案false
    public IState parent;//上一个状态
}
  • 初始状态

    public static IState state = new IState(0, 0, 3, 3, false, null);
    
  • 结束状态

    public static IState endState = new IState(3, 3, 0, 0, true, null);
    
状态转换

游戏中我们可以通过点击移动人物和船来改变状态,一个状态向下一个状态的所有动作行为包括:

  • 船在左岸
    • 单人过河
      • 牧师过河
      • 魔鬼过河
    • 双人过河
      • 牧师 + 牧师过河
      • 牧师 + 魔鬼过河
      • 魔鬼 + 魔鬼过河
  • 船在右岸
    • 单人过河
      • 牧师过河
      • 魔鬼过河
    • 双人过河
      • 牧师 + 牧师过河
      • 牧师 + 魔鬼过河
      • 魔鬼 + 魔鬼过河

思路就是根据当前的状态和当前状态可能的下一个状态,找到能达到终点状态的一条路径。可以用BFS来实现:

  1. 当前状态进入队列。

  2. 队列不为空:

    • 队头出队。

    • 访问状态节点。

      • 结束:回溯至开始状态输出
      • 没有结束:计算下一个合法状态并压入队列。
public static IState bfs(IState start, IState end) {
      Queue<IState> found = new Queue<IState>();
      Queue<IState> visited = new Queue<IState>();
      IState temp = new IState(start.leftPriests, start.leftDevils, start.rightPriests, start.rightDevils, start.boat, null);//当前开始状态
      found.Enqueue(temp);//进入队列

      while (found.Count > 0) {//队列不为空
        temp = found.Peek();//队头出队
        if (temp == end) {//结束,回溯到开始状态并返回
          while (temp.parent != start) {
            temp = temp.parent;
          }
          return temp;
        }
        found.Dequeue();
        visited.Enqueue(temp);
        //计算下一个合法状态
        //在左岸
        if (temp.boat) {
          //移动一个牧师
          if (temp.leftPriests > 0) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = false;
            next.leftPriests--;
            next.rightPriests++;
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
          //移动一个魔鬼
          if (temp.leftDevils > 0) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = false;
            next.leftDevils--;
            next.rightDevils++;
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
          //移动1魔鬼1牧师
          if (temp.leftDevils > 0 && temp.leftDevils > 0) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = false;
            next.leftPriests--;
            next.leftDevils--;
            next.rightPriests++;
            next.rightDevils++;
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
          //移动两个魔鬼
          if (temp.leftDevils > 1) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = false;
            next.leftDevils -= 2;
            next.rightDevils += 2;
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
          //移动两个牧师
          if (temp.leftPriests > 1) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = false;
            next.leftPriests -= 2;
            next.rightPriests += 2;
            next.parent = new IState(temp);
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
        } else {//在右岸
          //移动一个牧师
          if (temp.rightPriests > 0) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = true;
            next.rightPriests--;
            next.leftPriests++;
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
          //移动一个魔鬼
          if (temp.rightDevils > 0) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = true;
            next.rightDevils--;
            next.leftDevils++;
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
          //移动1魔鬼1牧师
          if (temp.rightDevils > 0 && temp.rightDevils > 0) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = true;
            next.rightPriests--;
            next.rightDevils--;
            next.leftPriests++;
            next.leftDevils++;
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
          //移动两个魔鬼
          if (temp.rightDevils > 1) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = true;
            next.rightDevils -= 2;
            next.leftDevils += 2;
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
          //移动两个牧师
          if (temp.rightPriests > 1) {
            IState next = new IState(temp);
            next.parent = new IState(temp);
            next.boat = true;
            next.rightPriests -= 2;
            next.leftPriests += 2;
            if (next.valid() && !visited.Contains(next) && !found.Contains(next)) {
              found.Enqueue(next);
            }
          }
        }
      }
      return null;
    }
获取游戏状态

最后在点击过河的时候获取当前游戏状态。

void OnMouseDown() {
	if (gameObject.name == "boat") {
		action.moveBoat ();
		int from_priest = controller.fromCoast.getCharacterNum()[0];
    int from_devil = controller.fromCoast.getCharacterNum()[1];
    int to_priest = controller.toCoast.getCharacterNum()[0];
    int to_devil = controller.toCoast.getCharacterNum()[1];
    bool location = controller.boat.get_to_or_from() ==-1  ? true : false;
    int[] boatCount = controller.boat.getCharacterNum ();
		if (location) {	// boat at toCoast
			to_priest += boatCount[0];
			to_devil += boatCount[1];
		} else {	// boat at fromCoast
			from_priest += boatCount[0];
			from_devil += boatCount[1];
		}
    state = new IState(to_priest, to_devil, from_priest, from_devil, location , null);
	} else {
		action.characterIsClicked (characterController);
	}
}

演示视频

项目仓库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值