[Algorithm]Maze Prim算法与A*寻路算法(中)
来源:互联网 发布:淘宝网秋冬运动套装 编辑:程序博客网 时间:2024/05/29 09:40
附上全文连接:
[Algorithm]Maze Prim算法与A*寻路算法(上)
[Algorithm]Maze Prim算法与A*寻路算法(中)
[Algorithm]Maze Prim算法与A*寻路算法(下)
文章中的所有源码下载链接附在“[Algorithm]Maze Prim算法与A*寻路算法(上) ”文章中。
上一篇中大致讲了下内容,接下来将要讲的是Maze Prim算法。
先附上一段Wiki的讲解:https://en.wikipedia.org/wiki/Maze_generation_algorithm#Randomized_Prim.27s_algorithm
其实算法是思路很简单就是不断的找墙拆墙的过程。大致流程如下:
1. 让迷宫所有节点均为墙,即没有路可走。
2. 选择一个偶数列和偶数行的节点设置为通路,然后把它的邻节点(邻墙)(上下左右,不要斜对角的=。=)存入邻节点列表中。
3. 如果邻节点列表中有数据,则执行以下循环。
——1. 从列表中随机选择一个邻节点,如果它相对与对应通路节点的对面节点不是通路的话,则执行以下循环。
————1.把该节点和对面节点设置为通路。
————2.把对面节点的邻节点加入列表。
——2.如果对面的节点已经是通路,则将其移除列表。
程序的大致流程如上,接下来就源码讲解,因为C# 控制台的版本可以直接运行,则以这个工程讲解。
“MazeCSharpTest”工程中只用看“Program.cs”即可,所有实现均在这个文件中。
需要定义节点类型枚举“WallType”和节点信息类“WallInfo”。
public enum WallType{ None = 0, Up = 1, Down = 2, Left = 4, Right = 8, UpDown = 3, LeftRight = 12, LeftUp = 5, LeftDown = 6, RightUp = 9, RightDown = 10, LeftUpRight = 13, UpRightDown = 11, RigthDownLeft = 14, DownLeftUp = 7, LeftUpRightDown = 15}public class WallInfo{ public int x = -1; public int y = -1; /// <summary> /// 是否有墙 /// </summary> public bool hasWall = true; /// <summary> /// 是否是寻路路径点 /// </summary> public bool isFindRoad = false; /// <summary> /// 墙的类型 /// </summary> public WallType type = WallType.None; /// <summary> /// 状态 /// </summary> public int flag = 0; public void Set(int x, int y, bool hasWall = true, int flag = 0, bool isFindRoad = false, WallType type = WallType.None) { this.x = x; this.y = y; this.hasWall = hasWall; this.type = type; this.flag = flag; this.isFindRoad = isFindRoad; } public override string ToString() { return "[WallInfo:x=" + x + " y=" + y + " hasWall=" + hasWall + " type=" + type + " flag=" + flag + "]"; }}private class BlockWallInfo{ public WallInfo info { get; private set; } public WallType wallDirect { get; private set; } public BlockWallInfo(WallInfo info, WallType wallDirect) { this.info = info; this.wallDirect = wallDirect; } public override string ToString() { return "[BlockWallInfo:wallDirect=" + wallDirect + " info=" + info + "]"; }}
“WallType”枚举用于对墙的类型进行分类,如该墙的上左下有邻墙的话,则该墙的类型为“DownLeftUp”,更加形象点就是“┤”。
“WallInfo”中对“ToString”进行了重写,这有便于查看数据。“flag ”未使用,为扩展信息,当然如果有需要,可以在这个类中继续添加。“isFindRoad ”用于寻路后的路径节点,现不做讲解。
“BlockWallInfo”用于创建Prim迷宫时使用的墙信息。“wallDirect ”为对面的对面节点所在方向。
下面的创建流程中的主要代码。
1.初始化迷宫,将节点全部初始化为墙,且更新节点数及坐标。
private void InitList(){ int nLerp = _x * _y - _maze.Count; if (nLerp > 0) {//添加 while (nLerp > 0) { _maze.Add(new WallInfo()); --nLerp; } } else if (nLerp < 0) {//移除 _maze.RemoveRange(0, -nLerp); } //初始化 //设置第一个 _maze[0].Set(0, 0); //设置其他 for (int n = 1, length = _maze.Count; n < length; n++) { _maze[n].Set(n % _x, n / _x); }}
通过这种做法,可以重复使用前面创建的对象,起到优化的作用。
2.创建迷宫。
/// <summary>/// 创建迷宫/// </summary>/// <param name="w">宽(>0)</param>/// <param name="h">高(>0)</param>/// <param name="randSeed">随机数</param>/// <returns>是否创建成功</returns>public bool Create(int w, int h, int randSeed){ if (w < 1 || h < 1) { return false; } //带墙的格子数 _x = w * 2 + 1; _y = h * 2 + 1; //初始化墙列表 InitList(); //初始化随机数 if (_randSeed != randSeed || _rand == null) { _rand = new Random(randSeed); } //生成迷宫 RandomPrim(); // //更新类型 UpdateType(); return true;}private void UpdateType(){ for (int y = 0; y < _y; y++) { for (int x = 0; x < _x; x++) { if (!GetWallInfo(x, y).hasWall) { GetWallInfo(x, y).type = WallType.None; continue; } WallType type = WallType.None; //Left if (x - 1 >= 0 && GetWallInfo(x - 1, y).hasWall) { type |= WallType.Left; } //Right if (x + 1 < _x && GetWallInfo(x + 1, y).hasWall) { type |= WallType.Right; } //Down if (y - 1 >= 0 && GetWallInfo(x, y - 1).hasWall) { type |= WallType.Down; } //Up if (y + 1 < _y && GetWallInfo(x, y + 1).hasWall) { type |= WallType.Up; } GetWallInfo(x, y).type = type; } }}
输入的宽和高都不是实际的,实际的宽和高都是乘2加1了的。
将Prim算法单独写在一个函数中的好处是,如果还有其他的迷宫算法的话,可以再写一个方法将其替换即可。总之,这个函数起到的作用就是处理已经初始化好的迷宫列表“_maze”。
“UpdateType”作用是在创建完成之后更新节点信息类中的节点类型,位运算还是很实用的:-D。
3.接下来是重点中的重点,RandomPrim算法的实现。
/// <summary>/// 随机普里姆算法生成迷宫/// </summary>private void RandomPrim(){ //墙数组下标极限 int nWidthLimit = _x - 2; int nHeightLimit = _y - 2; //起点 int nTarX = 1; int nTarY = 1; //标记起点 GetWallInfo(nTarX, nTarY).hasWall = false; List<BlockWallInfo> neighBlockWallInfo = new List<BlockWallInfo>(); XmWallInfo tempWall = null; //记录邻墙,初始化需要拆的墙 if (nTarY < nHeightLimit) { tempWall = GetWallInfo(nTarX, nTarY + 1);//Up if (tempWall != null) { neighBlockWallInfo.Add(new BlockWallInfo(tempWall, XmWallType.Up));//记录邻墙及其所在方向 } } if (nTarY > 1) { tempWall = GetWallInfo(nTarX, nTarY - 1);//Down if (tempWall != null) { neighBlockWallInfo.Add(new BlockWallInfo(tempWall, XmWallType.Down)); } } if (nTarX < nWidthLimit) { tempWall = GetWallInfo(nTarX + 1, nTarY);//Right if (tempWall != null) { neighBlockWallInfo.Add(new BlockWallInfo(tempWall, XmWallType.Right)); } } if (nTarX > 1) { tempWall = GetWallInfo(nTarX - 1, nTarY);//Left if (tempWall != null) { neighBlockWallInfo.Add(new BlockWallInfo(tempWall, XmWallType.Left)); } } int nIndex = 0; BlockWallInfo tempBlackWall = null; XmWallInfo tempWall2 = null; while (neighBlockWallInfo.Count > 0)//是否还有未拆的墙 { //随机选择一面墙 nIndex = _rand.Next(neighBlockWallInfo.Count); //找出此墙对面的目标格 tempBlackWall = neighBlockWallInfo[nIndex]; switch (tempBlackWall.wallDirect) { case XmWallType.Up: nTarX = tempBlackWall.info.x; nTarY = tempBlackWall.info.y + 1; break; case XmWallType.Down: nTarX = tempBlackWall.info.x; nTarY = tempBlackWall.info.y - 1; break; case XmWallType.Left: nTarX = tempBlackWall.info.x - 1; nTarY = tempBlackWall.info.y; break; case XmWallType.Right: nTarX = tempBlackWall.info.x + 1; nTarY = tempBlackWall.info.y; break; } tempWall = GetWallInfo(nTarX, nTarY);//获取目标格 if (tempWall != null && tempWall.hasWall) { //连通目标格 tempWall.hasWall = false; tempBlackWall.info.hasWall = false; //添加目标格的邻格 if (nTarY > 1)//Down { tempWall = GetWallInfo(nTarX, nTarY - 1);//获取目标格下面的邻墙 if (tempWall != null && tempWall.hasWall) { tempWall2 = GetWallInfo(nTarX, nTarY - 2);//获取目标格下面邻墙下面的目标格 if (tempWall2 != null && tempWall2.hasWall) { neighBlockWallInfo.Add(new BlockWallInfo(tempWall, XmWallType.Down));//添加邻墙及其方向 } } } if (nTarY < nHeightLimit)//Up { tempWall = GetWallInfo(nTarX, nTarY + 1); if (tempWall != null && tempWall.hasWall) { tempWall2 = GetWallInfo(nTarX, nTarY + 2); if (tempWall2 != null && tempWall.hasWall) { neighBlockWallInfo.Add(new BlockWallInfo(tempWall, XmWallType.Up)); } } } if (nTarX < nWidthLimit)//Right { tempWall = GetWallInfo(nTarX + 1, nTarY); if (tempWall != null && tempWall.hasWall) { tempWall2 = GetWallInfo(nTarX + 2, nTarY); if (tempWall2 != null && tempWall2.hasWall) { neighBlockWallInfo.Add(new BlockWallInfo(tempWall, XmWallType.Right)); } } } if (nTarX > 1)//Left { tempWall = GetWallInfo(nTarX - 1, nTarY); if (tempWall != null && tempWall.hasWall) { tempWall2 = GetWallInfo(nTarX - 2, nTarY); if (tempWall2 != null && tempWall2.hasWall) { neighBlockWallInfo.Add(new BlockWallInfo(tempWall, XmWallType.Left)); } } } } //移除此墙 neighBlockWallInfo.RemoveAt(nIndex); tempWall = null; tempWall2 = null; tempBlackWall = null; }}
算法中主要有一个需要访问的邻节点列表“neighBlockWallInfo ”。
首先会将(1,1)坐标的节点设置为通路即“hasWall”为false,然后将其周边邻节点添加到列表中。这样就初始化了邻节点列表。
然后循环判断列表是否为空,如果不为空,则随机取一个邻节点,并通过“BlockWallInfo ”中的“wallDirect”属性,取得对面的节点。
如果对面的节点不为空且“hasWall”为true即未打通,则打通该邻节点及对面的节点,均设置“hasWall”为false。并将对面节点的周边符合要求的邻节点存入列表中。
最后将这个已将访问了的节点移除列表。
当邻节点列表没有数据、循环退出的时候就是迷宫创建完成。
程序大致就是酱紫!如果有更好的实现方式或疑问请留言~
- [Algorithm]Maze Prim算法与A*寻路算法(中)
- [Algorithm]Maze Prim算法与A*寻路算法(上)
- [Algorithm]Maze Prim算法与A*寻路算法(下)
- POJ3026--Prim算法--Borg Maze
- POJ 3026 Borg Maze (Prim Algorithm)
- 三大迷宫生成算法 (Maze generation algorithm) -- 深度优先,随机Prim,递归分割
- 【算法】A star algorithm
- 数据结构与算法(c++)--prim算法
- (prim算法)A - 畅通工程
- POJ 3026 Borg Maze 图论 prim算法(最小生成树)+BFS算法(广度优先搜索)
- (算法设计技巧与分析)prim
- prim算法(普里姆算法)
- Prim算法(普里姆算法)
- 最小生成树(Prim算法与Kruskal算法)
- 贪心算法——普林姆算法(Greedy Algorithm-Prim's Algorithm)
- Prim算法 (改进)
- hdu1863(prim算法)
- 普里姆(Prim)算法
- jQuery13(相对元素的练习)
- 如何成为一个技术大牛
- 机器学习基石笔记-感知机
- linux下fdisk建盘
- N-Queens 题解
- [Algorithm]Maze Prim算法与A*寻路算法(中)
- poj 2983 Is the Information Reliable?(差分约束)
- java的几种对象(PO,VO,DAO,BO,POJO)解释
- 传智播客168期JavaEE就业班(第五天 xml约束与解析)
- Hadoop中空间数据的存储(二)
- 【Python 笔记】神奇的匿名函数
- 如何处理大量数据并发操作
- 如何优化tomcat配置(从内存、并发、缓存4个方面)优化
- Xutils HttpUtils上传文件的实现