//Source : http://en.wikipedia.org/wiki/Pathfinding //TODO: Still needs improvement //A good way to improve both this algorithm and the a* one, would be to load the Nodes when the map is created, //and keep them either in TileMap or in each tile. Or something like that. /// <summary> /// Find the closest element or the chosen type from the from point. /// </summary> /// <typeparam name="T">Type of the object we are looking for.</typeparam> /// <param name="from">The position we're starting from.</param> /// <param name="validate">A lambda operation to validate if the object found is good. If the lamdba operation fails, the algorithm will look for another one. Note : Won't be tested if null.</param> /// <returns>Return the closest object found. If none are found, returns null.</returns> public static T FindClosest <T>(Point from, Func <T, bool> validate = null, bool includeChilds = false) where T : GameObject { int nbElements = TileMap.Width * TileMap.Height; int nbSolidItems = 0; List <DijkstraNode> nodes = new List <DijkstraNode>(); for (int i = 0; i < nbElements; i++) { int x = i % TileMap.Width; int y = (int)Math.Floor((double)(i / TileMap.Width)); nodes.Add(new DijkstraNode() { Distance = int.MaxValue, Position = new Point(x, y) }); if (!IsWalkable(new Point(x, y))) { nbSolidItems++; } } DijkstraNode start = nodes.FirstOrDefault(x => x.Position == from); start.Distance = 0; List <DijkstraNode> Q = nodes.ToList(); //We don't want to check the solid items in the path, but want to keep them //because we may be looking for them. while (Q.Count - nbSolidItems > 0) { //TODO : Improve it? //DijkstraNode u = // Q.Where(x => IsWalkable(x.Position)) //Get all non-solid items // .MinBy(o => o.Distance); //What is better : MinBy or Aggregate? //.Aggregate((curmin, x) => (curmin == null || x.Distance < curmin.Distance ? x : curmin)); //Get the one with the smallest value DijkstraNode u = Q[0]; for (int i = 1; i < Q.Count; i++) { DijkstraNode n = Q[i]; if (IsWalkable(n.Position) && n.Distance < u.Distance) { u = n; } } Q.Remove(u); if (u.Distance == int.MaxValue) { break; } u.SetNeighbors(nodes); foreach (DijkstraNode v in u.Neighbors) { T obj = TileMap.Tiles[v.Position.X, v.Position.Y].GetFirstObject <T>(includeChilds); if (obj != null) { if (validate == null || validate.Invoke(obj)) { return(obj); } } int alt = u.Distance + 1; if (alt < v.Distance) { v.Distance = alt; v.Previous = u; } } } return(null); }