由A移动到B
假设垂直方向移动一个格子消耗为10,斜线方向移动一个格子消耗为14。
然后定义三个变量:
G:表示从起点A移动到当前方格的移动消耗
H:表示从当前方格移到到终点B的预计消耗
F = G + H
此时可以得到上图中的各个格子的信息。
实现思路:
1.加入开启列表。将A加入【开启列表】
2.遍历开启列表。找出F最小的,开始只有A
3.移除开启列表。从【开启列表】中移除A,加入【关闭列表】
4.遍历周围格子。获取当前表格周围的格子再遍历
5.周围点不在开启列表时。如果周围的格子不在开启列表,就将当前格子作为父节点,计算G,H,F,并将这个周围点加入到【开启列表】。
Tips:当起点为A时,上面已经完成了一次开启列表的遍历,下次遍历时,当前格子F最小就是格子1。
6.周围点在开启列表时判断G值。如果周围的格子在【开启列表】,那么就检查这条路径 ( 即经由当前方格到达它那里 ) 是否更好,更小的G值表示更好的路径,如果G更小,把它的父节点设置为当前格,重新计算G,F;如果没有更小的就不做处理。
Tips:此时如果走A12路径,2的G为20,G值>它原来的14,所以这里不做处理。此时1已经加入关闭列表,然后再继续遍历【开启列表】
Tips:当遍历到3时,此时如果走A123,那么当遍历到2_H时,此时当前的G=10+14=24结果是小于2_H当前的G值,那么此时2_H重新计算G,F最终变成上图3_H。
8.当把目标表格加入到【开启列表】。也就找到目标表格,如果没有找到目标表格,表示无路径
9.获取最终路径。从目标表格开始,获取每个格子的父节点,就是最终路径。
抽象过程
把起始格添加到 "开启列表"
do
{
寻找开启列表中F值最低的格子, 我们称它为当前格.
把它切换到关闭列表.
对当前格相邻的8格中的每一个
if (它不可通过 || 已经在 "关闭列表" 中)
{
什么也不做.
}
if (它不在开启列表中)
{
把它添加进 "开启列表", 把当前格作为这一格的父节点, 计算这一格的 FGH
if (它已经在开启列表中)
{
if (用G值为参考检查新的路径是否更好, 更低的G值意味着更好的路径)
{
把这一格的父节点改成当前格, 并且重新计算这一格的 GF 值.
}
} while( 目标格已经在 "开启列表", 这时候路径被找到)
如果开启列表已经空了, 说明路径不存在.
最后从目标格开始, 沿着每一格的父节点移动直到回到起始格, 这就是路径.
using System;using System.Collections.Generic;
using System.Linq;
namespace Maze
{
class Maze
{
public const int OBLIQUE = 14;
public const int STEP = 10;
public int[,] MazeArray { get; private set; }
List<Point> CloseList;
List<Point> OpenList;
public Maze(int[,] maze)
{
this.MazeArray = maze;
OpenList = new List<Point>(MazeArray.Length);
CloseList = new List<Point>(MazeArray.Length);
}
public Point FindPath(Point start, Point end, bool IsIgnoreCorner)
{
OpenList.Add(start);
while (OpenList.Count != 0)
{
//找出F值最小的点
var tempStart = OpenList.MinPoint();
OpenList.RemoveAt(0);
CloseList.Add(tempStart);
//找出它相邻的点
var surroundPoints = SurrroundPoints(tempStart, IsIgnoreCorner);
foreach (Point point in surroundPoints)
{
if (OpenList.Exists(point))
//计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F
FoundPoint(tempStart, point);
else
//如果它们不在开始列表里, 就加入, 并设置父节点,并计算GHF
NotFoundPoint(tempStart, end, point);
}
if (OpenList.Get(end) != null)
return OpenList.Get(end);
}
return OpenList.Get(end);
}
private void FoundPoint(Point tempStart, Point point)
{
var G = CalcG(tempStart, point);
if (G < point.G)
{
point.ParentPoint = tempStart;
point.G = G;
point.CalcF();
}
}
private void NotFoundPoint(Point tempStart, Point end, Point point)
{
point.ParentPoint = tempStart;
point.G = CalcG(tempStart, point);
point.H = CalcH(end, point);
point.CalcF();
OpenList.Add(point);
}
private int CalcG(Point start, Point point)
{
int G = (Math.Abs(point.X - start.X) + Math.Abs(point.Y - start.Y)) == 2 ? STEP : OBLIQUE;
int parentG = point.ParentPoint != null ? point.ParentPoint.G : 0;
return G + parentG;
}
private int CalcH(Point end, Point point)
{
int step = Math.Abs(point.X - end.X) + Math.Abs(point.Y - end.Y);
return step * STEP;
}
//获取某个点周围可以到达的点
public List<Point> SurrroundPoints(Point point, bool IsIgnoreCorner)
{
var surroundPoints = new List<Point>(9);
for(int x = point.X -1; x <= point.X+1;x++)
for (int y = point.Y - 1; y <= point.Y + 1; y++)
{
if (CanReach(point,x, y,IsIgnoreCorner))
surroundPoints.Add(x, y);
}
return surroundPoints;
}
//在二维数组对应的位置不为障碍物
private bool CanReach(int x, int y)
{
return MazeArray[x, y] == 0;
}
public bool CanReach(Point start, int x, int y, bool IsIgnoreCorner)
{
if (!CanReach(x, y) || CloseList.Exists(x, y))
return false;
else
{
if (Math.Abs(x - start.X) + Math.Abs(y - start.Y) == 1)
return true;
//如果是斜方向移动, 判断是否 "拌脚"
else
{
if (CanReach(Math.Abs(x - 1), y) && CanReach(x, Math.Abs(y - 1)))
return true;
else
return IsIgnoreCorner;
}
}
}
}
//Point 类型
public class Point
{
public Point ParentPoint { get; set; }
public int F { get; set; } //F=G+H
public int G { get; set; }
public int H { get; set; }
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public void CalcF()
{
this.F = this.G + this.H;
}
}
//对 List<Point> 的一些扩展方法
public static class ListHelper
{
public static bool Exists(this List<Point> points, Point point)
{
foreach (Point p in points)
if ((p.X == point.X) && (p.Y == point.Y))
return true;
return false;
}
public static bool Exists(this List<Point> points, int x, int y)
{
foreach (Point p in points)
if ((p.X == x) && (p.Y == y))
return true;
return false;
}
public static Point MinPoint(this List<Point> points)
{
points = points.OrderBy(p => p.F).ToList();
return points[0];
}
public static void Add(this List<Point> points, int x, int y)
{
Point point = new Point(x, y);
points.Add(point);
}
public static Point Get(this List<Point> points, Point point)
{
foreach (Point p in points)
if ((p.X == point.X) && (p.Y == point.Y))
return p;
return null;
}
public static void Remove(this List<Point> points, int x, int y)
{
foreach (Point point in points)
{
if (point.X == x && point.Y == y)
points.Remove(point);
}
}
}
}
using System;
namespace Maze
{
class Program
{
static void Main(string[] args)
{
int[,] array = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1},
{ 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1},
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1},
{ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1},
{ 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1},
{ 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};
Maze maze = new Maze(array);
Point start = new Point(1, 1);
Point end = new Point(6, 10);
var parent = maze.FindPath(start, end, false);
Console.WriteLine("Print path:");
while (parent != null)
{
Console.WriteLine(parent.X + ", " + parent.Y);
parent = parent.ParentPoint;
}
Console.ReadLine();
}
}
}
namespace Maze
{
class Program
{
static void Main(string[] args)
{
int[,] array = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{ 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1},
{ 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1},
{ 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1},
{ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1},
{ 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1},
{ 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1},
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
};
Maze maze = new Maze(array);
Point start = new Point(1, 1);
Point end = new Point(6, 10);
var parent = maze.FindPath(start, end, false);
Console.WriteLine("Print path:");
while (parent != null)
{
Console.WriteLine(parent.X + ", " + parent.Y);
parent = parent.ParentPoint;
}
Console.ReadLine();
}
}
}