zoukankan      html  css  js  c++  java
  • 使用 C# 开发智能手机软件:推箱子(四)

    这是“使用 C# 开发智能手机软件:推箱子”系列文章的第四篇。

    在这篇文章中,介绍 Common/FindPath.cs 源程序文件。

    using System;
    using System.Drawing;
    using System.Collections.Generic;

    namespace Skyiv.Ben.PushBox.Common
    {
      
    /// <summary>
      
    /// 寻找最短路线
      
    /// </summary>
      static class FindPath
      {
        
    static Size[] offsets = { new Size(01), new Size(10), new Size(0-1), new Size(-10) };
        
    static Direction[] directions = { Direction.South, Direction.East, Direction.North, Direction.West };

        
    /// <summary>
        
    /// 寻找最短路线
        
    /// </summary>
        
    /// <param name="map">地图</param>
        
    /// <param name="from">出发点</param>
        
    /// <param name="to">目的地</param>
        
    /// <returns>最短路线</returns>
        public static Queue<Direction> Seek(ushort[,] map, Point from, Point to)
        {
          Queue
    <Direction> moveQueue = new Queue<Direction>(); // 路线
          int value; // 离目的地距离
          if (Seek(map, to, out value)) // 找到了一条路线
          {
            Point here 
    = from; // 出发点(即工人的位置)
            Point nbr = new Point(); // 四周的邻居
            for (value--; value > 0; value--// 逐步走向目的地
            {
              
    for (int i = 0; i < offsets.Length; i++)
              {
                nbr 
    = Fcl.Add(here, offsets[i]); // 開始寻找四周的邻居
                if (Block.Value(map[nbr.Y, nbr.X]) == value) // 就往这个方向走
                {
                  moveQueue.Enqueue(directions[i]); 
    // 路线向目的地延伸一步
                  break;
                }
              }
              here 
    = nbr; // 继续前进
            }
          }
          Block.CleanAllMark(map); 
    // 清除全部标志,恢复现场
          return moveQueue; // 所寻找的路线,假设无法到达目的地则为该路线的长度为零
        }

        
    /// <summary>
        
    /// 寻找最短路线,使用广度优先搜索
        
    /// </summary>
        
    /// <param name="map">地图</param>
        
    /// <param name="to">目的地</param>
        
    /// <param name="value">输出:路线的长度(加1)</param>
        
    /// <returns>是否成功</returns>
        static bool Seek(ushort[,] map, Point to, out int value)
        {
          Queue
    <Point> q = new Queue<Point>();
          Block.Mark(
    ref map[to.Y, to.X], 1); // 从目的地開始往回寻找出发点,目的地标记为1
          Point nbr = Point.Empty; // 四周的邻居
          for (; ; )
          {
            value 
    = Block.Value(map[to.Y, to.X]) + 1// 离开目的地的距离(加1),用作标记
            for (int i = 0; i < offsets.Length; i++)
            {
              nbr 
    = Fcl.Add(to, offsets[i]); // 開始寻找四周的邻居
              if (Block.IsMan(map[nbr.Y, nbr.X])) break// 到达出发点(即工人的位置)
              if (Block.IsBlank(map[nbr.Y, nbr.X])) // 能够走的路
              {
                Block.Mark(
    ref map[nbr.Y, nbr.X], value); // 标记,防止以后再走这条路
                q.Enqueue(nbr); // 增加队列,等待以后继续寻找
              }
            }
            
    if (Block.IsMan(map[nbr.Y, nbr.X])) break// 到达出发点
            if (q.Count == 0return false// 无法到达出发点
            to = q.Dequeue(); // 出队,继续寻找,这是广度优先搜索,由于前面已经把四周可以走的路所有增加队列中了.
          }
          
    return true// 找到一条路线
        }
      }
    }
        静态类 FindPath 是用来寻找工人移动到鼠标点击的目的地的最短路线的。她採用一种广度优先搜索算法,使用循环,没有使用递归,并且地图上已经搜索过的路线决不再走第二遍。

    该算法分两个阶段进行:首先是寻找并标记最短路线,由该类的第二个 Seek 方法实现。这个私有的方法返回一个布尔值表明是否成功。

    然后,假设在第一阶段中找到了一条路线,则依据第一阶段所做的标记生成最短路线并将该路线返回给调用者。我们来看几个实例:

        在该算法中,是从要到达的目的地開始往回寻找出发点。首先,将目的地标记为1,然后查看周围的四个邻居(按南、东、北、西的顺序)是否是“空白”(即“地”和“槽”,使用 Block.IsBlank 方法来推断),如是,则表明这是能够走的路,将其作上标记(使用 Block.Mark 方法,标记的数值等于离开目的地的距离加一),然后增加队列。这有两个作用,首先,标记过的单元格将不再被觉得是能够走的路。防止反复搜索。

    其次,在第二阶段中要依据标记的值来生成最短路线。

    假设发现周围的邻居中有一个是工人(用 Block.IsMan 方法来推断),说明到达出发点,则马上结束搜索,退出循环。返回成功。

    否则,就检查队列是否为空,假设为空,则说明无法到达出发点,返回失败。假设不为空,则出队,从这一点继续開始搜索。

    如此一直循环。
        这个算法是广度优先的,如上面的两个图所看到的。该算法是按标记的值从小到大进行遍历的,而该标记的值表示的是离开目的地的距离加一。


        第二个阶段。假设在第一阶段返回失败,则返回一条空的路线(长度为零)给调用者。否则,从出发点(即工人的位置)開始,查看周围的四个邻居(按南、东、北、西的顺序)。假设其标记的值(使用 Block.Value 方法来获得)为到目的地的距离加一(至少能够找到一个,可能有多个。能够任取一个,程序中使用第一个),就往这个方向走。

    如此一直循环。直到到达目的地。然后返回这条路线给调用者。
        从这里能够看出。为什么地图(即 ushort[,] map)要使用 ushort 而不使用 byte。由于在该算法须要在地图中作标记,并且标记的值还必须是到目的地的距离加一(假设仅仅须推断目的地是否可达,而不要求给出到达目的地的详细路线,则在算法中标记的值可所有都为1。这样用 byte 就足够了)。地图中总共同拥有八种类型的单元格,须要用三个二进位表示。而 byte 仅仅有八个二进位。那么。仅仅剩下五个二进位,25=32。也就是说,目的地在工人32步以外该算法就无能为力了。而 ushort 有十六个二进位,减去三个,还有十三个二进位,213=8192。这应该足够了。让我们看看下图吧:

        这是一个 9x9 的地图。共同拥有81个单元格,当中49个是空地。如果目的在地图的右上角(标记为1的地方),则工人须要48步才干到达目的地。依据计算。如果是 NxN (这里N是奇数)的地图,工人在最坏的情况下须要 (N2 - 1)/2 + N -1 步(走最短路线)才干到达目的地。

    这就是说,在 127x127 的地图上,工人最多仅仅须要 8190 步就能够到达目的地。这刚好在我们算法的范围之内。假设地图再大。我们的算法就可能(仅仅是可能,由于在大地图上普通情况下并不会出现超过 8192 步的最短路线)无能为力了。

  • 相关阅读:
    Chrome cookies folder
    Fat URLs Client Identification
    User Login Client Identification
    Client IP Address Client Identification
    HTTP Headers Client Identification
    The Personal Touch Client Identification 个性化接触 客户识别
    购物车 cookie session
    购物车删除商品,总价变化 innerHTML = ''并没有删除节点,内容仍存在
    453
    购物车-删除单行商品-HTMLTableElement.deleteRow()
  • 原文地址:https://www.cnblogs.com/llguanli/p/6881337.html
Copyright © 2011-2022 走看看