牧师过河的智能帮助实现

    实现的基本思想就是在游戏开始的时候先生成状态图,并且找到每个状态图到成功状态的路径,然后在游戏过程中只要先找到目前的状态,再前往下一个成功路径上的状态就行了。


以下是状态的类,要实现IEquatable<T>中的Equals接口,方便使用list中的内置方法而不至于出错。状态只是记录一边岸上的数据

public class status : IEquatable<status>
    {
        public status(int p, int d, bool b)
        {
            pnum = p;
            dnum = d;
            boat = b;
            children = new List<status>();
        }
        public bool isLegal()
        {
            if (dnum > pnum && pnum != 0)
                return false;
            if (3 - dnum > 3 - pnum && pnum != 3)
                return false;
            return true;
        }
        
        public bool equals(status other)
        {
            if (other.pnum == pnum && other.dnum == dnum && other.boat == boat)
                return true;
            else
                return false;
        }

        public bool Equals(status other)
        {
            return equals(other);
        }

        public int pnum;
        public int dnum;
        public bool boat;
        public List<status> children;
    }

在firstController中新声明的一些变量如下

Graph是保存图的邻接表,appendGraph是初始化图的时候用到的,rootStatus是游戏刚开始的状态,path是保存每个状态对应的最短路径的邻接表,visited是迪杰斯特拉算法找最短路径时要用到的,cnext标志UI上的next按钮能否出现,避免多次点击的问题。

public List<status> Graph = new List<status>();
    public List<status> appendGraph = new List<status>();
    public status rootstatus = new status(3, 3, true);
    public List<List<status>> path = new List<List<status>>();
    List<status> visited = new List<status>();
    bool cnext = true;


自动生成状态图

图所用的数据结构是邻接链表,基本思路就是先初始化一开始的状态,然后状态分成有船和无船两种,有船可以走两个牧师,两个魔鬼,一个牧师,一个魔鬼,一个牧师和一个魔鬼,而无船则是相反,是来几个牧师和魔鬼,据此就可以生成下一个状态,生成下一个状态之后再判断状态是否合法(即是否已经输了),合法后就假如到该原状态的儿子列表(即连接的节点列表),然后再检查新状态是否已经存在图中,不存在再加入到图的节点链表中。代码如下
void initGraph()
    {
        List<status> graph = new List<status>(Graph);
        do
        {
            appendGraph.Clear();
            foreach (status s in graph)
            {
                
                if (s.boat)
                {
                    if (s.pnum >= 2)
                    {
                        status newStatus = new status(s.pnum - 2, s.dnum, false);
                        if (newStatus.isLegal())
                        {
                            s.children.Add(newStatus);
                            afterLegal(newStatus);
                        }
                    }
                    if (s.dnum >= 2)
                    {
                        status newStatus = new status(s.pnum, s.dnum - 2, false);
                        if (newStatus.isLegal())
                        {
                            s.children.Add(newStatus);
                            afterLegal(newStatus);
                        }
                    }
                    if (s.pnum >= 1)
                    {
                        status newStatus = new status(s.pnum - 1, s.dnum, false);
                        if (newStatus.isLegal())
                        {
                            s.children.Add(newStatus);
                            afterLegal(newStatus);
                        }
                    }
                    if (s.dnum >= 1)
                    {
                        status newStatus = new status(s.pnum, s.dnum - 1, false);
                        if (newStatus.isLegal())
                        {
                            s.children.Add(newStatus);
                            afterLegal(newStatus);
                        }
                    }
                    if (s.pnum >= 1 && s.dnum >= 1)
                    {
                        status newStatus = new status(s.pnum - 1, s.dnum - 1, false);
                        if (newStatus.isLegal())
                        {
                            s.children.Add(newStatus);
                            afterLegal(newStatus);
                        }
                    }
                }
                else
                {
                    if(s.pnum <= 1)
                    {
                        status newStatus = new status(s.pnum + 2, s.dnum, true);
                        if (newStatus.isLegal())
                        {
                            s.children.Add(newStatus);
                            afterLegal(newStatus);
                        }
                    }
                    if(s.dnum <= 1)
                    {
                        status newStatus = new status(s.pnum, s.dnum + 2, true);
                        if (newStatus.isLegal())
                        {
                            s.children.Add(newStatus);
                            afterLegal(newStatus);
                        }
                    }
                    if(s.pnum <= 2)
                    {
                        status newStatus = new status(s.pnum + 1, s.dnum, true);
                        if (newStatus.isLegal())
                        {
                            s.children.Add(newStatus);
                            afterLegal(newStatus);
                        }
                    }
                    if(s.dnum <= 2)
                    {
                        if(s.pnum == 0 && s.dnum == 0)
                        {

                        }
                        else
                        {
                            status newStatus = new status(s.pnum, s.dnum + 1, true);
                            if (newStatus.isLegal())
                            {
                                s.children.Add(newStatus);
                                afterLegal(newStatus);
                            }
                        }
                        
                    }
                    if(s.pnum <= 2 && s.dnum <= 2)
                    {
                        status newStatus = new status(s.pnum + 1, s.dnum + 1, true);
                        if (newStatus.isLegal())
                        {
                            s.children.Add(newStatus);
                            afterLegal(newStatus);
                        }
                    }
                    
                }
            }
            graph = new List<status>(appendGraph);
            Graph.AddRange(appendGraph);
            
        } while (appendGraph.Count != 0);
        /*
        Debug.Log(Graph.Count);
        foreach (status s in Graph)
        {
            Debug.Log(s.pnum);
            Debug.Log(s.dnum);
            Debug.Log(s.boat);
        }
        */
    }

    public void afterLegal(status newStatus)
    {

        bool index = true;
        foreach (status s2 in Graph)
        {
            if (s2.equals(newStatus))
            {
                index = false;
                break;
            }
        }
        foreach(status s2 in appendGraph)
        {
            if(s2.equals(newStatus))
            {
                index = false;
                break;
            }
        }
        if (index)
            appendGraph.Add(newStatus);
    }

寻找最短路径到达最终状态

思路就是用迪杰斯特拉算法就行,然后把每个状态对应的最短路径保存到新的邻接表中

public void initShortestPath()
    {
        int[] dis = new int[Graph.Count];
        status src = Graph[Graph.Count - 1];
        for(int i = 0; i < dis.Length; i++)
        {
            dis[i] = 9999999;
            List<status> pa = new List<status>();
            pa.Add(src);
            path.Add(pa);
        }
        dis[Graph.IndexOf(src)] = 0;
        foreach(status s in src.children)
        {
            dis[Graph.IndexOf(s)] = 1;
        }
        
        visited.Add(src);
        while(visited.Count != Graph.Count)
        {
            status shortest = rootstatus;
            int shortdis = 99999999;
            for(int i = 0; i < dis.Length; i++)
            {
                if(shortdis > dis[i] && notVisit(Graph[i]))
                {
                    shortest = Graph[i];
                    shortdis = dis[i];
                }
            }
            
            visited.Add(shortest);
            foreach(status s in shortest.children)
            {
                if(notVisit(s) && dis[Graph.IndexOf(s)] > dis[Graph.IndexOf(shortest)] + 1)
                {
                    dis[Graph.IndexOf(s)] = dis[Graph.IndexOf(shortest)] + 1;
                    path[Graph.IndexOf(s)].Clear();
                    path[Graph.IndexOf(s)].AddRange(path[Graph.IndexOf(shortest)]);
                    path[Graph.IndexOf(s)].Add(shortest);
                }
            }
        }
        
        /*
        for (int i = 0; i < dis.Length; i++)
        {
            string str = "";
            str += Graph[i].pnum.ToString() + "P" + Graph[i].dnum.ToString() + "D" + Graph[i].boat.ToString() + " " + dis[i].ToString();
            Debug.Log(str);
        }
        */
        foreach(List<status> ls in path)
        {
            ls.Reverse();
        }
        /*
        foreach(List<status> ls in path)
        {
            string str = "";
            
            foreach (status s in ls)
            {
                str += s.pnum.ToString() + "P" + s.dnum.ToString() + "D" + s.boat.ToString() + "b ->";
            }
            Debug.Log(str);

        }
        */
    }

    public bool notVisit(status other)
    {
        foreach(status s in visited)
        {
            if (s.equals(other))
                return false;
        }
        return true;
    }

状态移动

代码如下,next是提供给IUserAction的接口函数,在移动的过程中,为了避免上船下船动作和船移动的同时执行,还有避免一个过程还没结束再次点击Next下一个状态的情况,使用了协程来等待。
public void next()
    {
        status current = findCurrentStatus();
        status nextStatus = path[Graph.IndexOf(current)][0];
        StartCoroutine(moveFromStatus(current, nextStatus));
        cnext = false;
    }

    public bool canNext()
    {
        return cnext;
    }

    private int frame = 70;
    IEnumerator moveFromStatus(status first, status second)
    {
        if(boat.get_pcount() > 0 || boat.get_dcount() > 0)
        {
            int Num = boat.get_pcount() + boat.get_dcount();
            for (int i = 0; i < Num; i++)
            {
                for (int j = 0; j < frame; j++)
                {
                    yield return null;
                }
                movePD(boat.getPD());
            }
            for (int j = 0; j < frame; j++)
            {
                yield return null;
            }
        }
        if(first.boat)
        {
            int pnum = first.pnum - second.pnum;
            int dnum = first.dnum - second.dnum;
            if(pnum > 0)
            {
                for (int i = 0; i < pnum; i++)
                {
                    movePD(right_coast.getOneP());
                }
            }
            if(dnum > 0)
            {
                for (int i = 0; i < dnum; i++)
                {
                    movePD(right_coast.getOneD());
                }
            }
            
        }
        else
        {
            int pnum = second.pnum - first.pnum;
            int dnum = second.dnum - first.dnum;
            if(pnum > 0)
            {
                for (int i = 0; i < pnum; i++)
                {
                    movePD(left_coast.getOneP());
                }
            }
            if (dnum > 0)
            {
                for (int i = 0; i < dnum; i++)
                {
                    movePD(left_coast.getOneD());
                }
            }
            
        }
        for (int i = 0; i < frame; i++)
        {
            yield return null;
        }
        moveBoat();

        int boatPDNum = boat.get_pcount() + boat.get_dcount();
        for (int i = 0; i < boatPDNum; i++)
        {
            for (int j = 0; j < frame; j++)
            {
                yield return null;
            }
            movePD(boat.getPD());
        }
        for (int j = 0; j < frame; j++)
        {
            yield return null;
        }
        cnext = true;
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值