-1

I've got a struct which contains a few int and bool members, and I'm looking to obtain the lowest value from the list (actually making an A* Search based Path Finder).

Basically, my object looks like this:

    public struct Tile
    {
        public int id;
        public int x;
        public int y;
        public int cost;
        public bool walkable;
        public int distanceLeft;
        public int parentid;
    }

And I want to get the item with the lowest distanceLeft. The list is declared like so:

        List<Structs.Tile> openList = new List<Structs.Tile>();

And values are assigned in this manner:

        while (pathFound == null)
        {
            foreach (Structs.Tile tile in map)
            {
                foreach (Structs.Tile tile1 in getSurroundingTiles(Current))
                {
                    if (tile1.x == tile.x && tile1.y == tile.y)
                    {
                        Structs.Tile curTile = tile1;
                        curTile.parentid = Current.id;
                        curTile.distanceLeft = (Math.Abs(tile.x - goalx) + Math.Abs(tile.y - goaly));
                        if (curTile.distanceLeft == 0)
                        {
                            pathFound = true;
                        }
                        openList.Add(curTile);
                    }
                }
            }
            foreach (Structs.Tile tile in openList)
            {

            }
        }

If I had to guess I'd say this is either very difficult or far more complex than I'm making it sound, or incredibly easy and I'm just confused.

I did think about scrolling through the list and comparing each item to its lower counterpart, but that seems unreasonable considering the age we're in, it just seems there would be a simpler way. I don't care for the order of the list, as I am assigning each item an index from which I can call it.

Thanks in advance!

robkriegerflow
  • 708
  • 5
  • 13
XtrmJosh
  • 779
  • 2
  • 12
  • 30
  • You should have a look at LINQ. – antonijn Feb 19 '13 at 21:19
  • Why do you use a struct? It does consume about 28 bytes which is much bigger than the pointer size of 4/8 bytes. Every method which does consume your struct needs to carry 28 bytes. If it would be class the comparison would be much faster. – Alois Kraus Feb 19 '13 at 21:30
  • It probably is a duplicate mbeckish, but I don't know Linq well enough to know that this is a Linq function, so my sincere apologies for this. I'd still like to retain this thread for a while though, as it does contain a response which I think could be useful to some people (others who may not know of Linq or the association referenced here). – XtrmJosh Feb 19 '13 at 21:34
  • Alois Kraus - I am not entirely aware of the pros and cons behind using a class, though I would've thought that declaring a new instance of a class would be more of a job than a new instance of a struct? If not, let me know and I'll remember this for future reference. Thanks massively for your advise, I appreciate it a lot! – XtrmJosh Feb 19 '13 at 21:35
  • There is a little article explaining this stuff: http://msdn.microsoft.com/en-us/magazine/cc301569.aspx – Alois Kraus Feb 19 '13 at 21:59

4 Answers4

5

The other answers explain how to do this with LINQ - however, they are all O(n) or slower. Using one of those methods will significantly slow down your pathfinding algorithm.

Instead, you should be using a proper data structure. Rather than a list, you should be storing your nodes in a Priority Queue to get (and remove) the minimum in O(log n).

See this question for a list of priority queues in .Net.

Community
  • 1
  • 1
BlueRaja - Danny Pflughoeft
  • 75,675
  • 28
  • 177
  • 259
2

There is not one LINQ extension method that returns the object with a minimum value, but you can write one yourself. The following class does what you want on any non-empty enumerable:

public static class MyExtensions
{
    public static TSource MinOf<TSource>(
        this IEnumerable<TSource> source,
        Func<TSource, int> selector)
    {
        // Get the enumerator.
        var enumerator = source.GetEnumerator();
        if (!enumerator.MoveNext())
            throw new InvalidOperationException("The source sequence is empty.");

        // Take the first value as a minimum.
        int minimum = selector(enumerator.Current);
        TSource current = enumerator.Current;

        // Go through all other values...
        while (enumerator.MoveNext())
        {
            int value = selector(enumerator.Current);
            if (value < minimum)
            {
                // A new minimum was found. Store it.
                minimum = value;
                current = enumerator.Current;
            }
        }

        // Return the minimum value.
        return current;
    }
}

Put it in a file in your project and call it like this:

openList.MinOf(tile => tile.distanceLeft);

This is more efficient than ordering the entire sequence (using OrderBy) and then taking the first value (using First).

Daniel A.A. Pelsmaeker
  • 40,431
  • 19
  • 96
  • 149
1

To get the Tile with the lowest distanceLeft, try this:

Tile tile = openList.OrderByAscending(t => t.distanceLeft).First();

Edit:

Doing this will return an IEnumerable<Tile> which will be sorted in ascending order - openList itself will not be modified.

nick_w
  • 13,990
  • 2
  • 47
  • 67
  • Will this modify the order of the list? If so, I think the Linq option may be favourable, I know I mentioned that I don't mind modifying the order, but I'd rather not for the sake of resources and what not, it seems it may be somewhat wasteful to do this. I greatly appreciate your response, nonetheless. Thanks! – XtrmJosh Feb 19 '13 at 21:36
  • @XtrmJosh See my edit. – nick_w Feb 19 '13 at 21:38
  • It does not modify the input list. Instead it creates some memory structure that sorts the entire list which is then all discarded except for the first element. – Daniel A.A. Pelsmaeker Feb 19 '13 at 21:42
  • It does seem more memory intensive than the alternative method here, but as I said I appreciate the input, thanks! – XtrmJosh Feb 20 '13 at 07:25
1

Or, if for some reason you can't use LINQ:

int lowest = 0;
for (int i = 1; i < openList.Count; ++i)
{
    if (openList[i].distanceLeft < openList[lowest].distanceLeft)
    {
        lowest = i;
    }
}
// lowest contains the index of the item with the lowest 'distanceLeft' value.
// You can return the item itself by indexing into the list.
var lowestItem = openList[lowest];
Jim Mischel
  • 122,159
  • 16
  • 161
  • 305
  • This returns the minimum value, not the object with the minimum value – BlueRaja - Danny Pflughoeft Feb 19 '13 at 21:41
  • Just to give a bit more clarification as to why you can't use Min() in this situation: `Min()` returns the object, but compares the objects itself (you can't specify a selector). `Min(lambda)` returns the datatype that the lambda selects, which in this case is an int. – gunr2171 Feb 19 '13 at 21:54
  • Actually, it was returning the index of the item with the minimum value. Now it returns the item itself. – Jim Mischel Feb 19 '13 at 21:55
  • @gunr2171: Yeah, saw that. My bad. It's odd that the interface is written that way. – Jim Mischel Feb 19 '13 at 21:56