Edward - 3 months ago 8
C# Question

# Two lists of coords returning most distinct minimal for one list based on distance function

I have a list of coords using Tuple, since I don't have access to the Drawing library to use 'Point'.

``````List<Tuple<int,int>> coords = new List<Tuple<int,int>>();
string[] movement = new string[temp.Count];
for(int i=1000; i<=8000; i=i+2300)
for(int j=1000; j<=15000; j=j+2000)
coords.RemoveAll(x=> 3500>= getDist(0,0,x.Item1,x.Item2) );
coords.RemoveAll(x=> 3500>= getDist(16000,9000,x.Item1,x.Item2) );
``````

I have player pieces in a list, two examples below.

``````List<int[]> player = new List<int[]>() {new int[]{0,726,1084,0,0,5},new int[]{2,1481,2208,0,0,-1} };
//piece numb, loc_X, loc_Y, teamID, state, value
``````

Turn-based movements must be figured, and when the state of a piece indicates scouting, I want to find the most minimal coord-point-set to each scouting piece, without two pieces going to the same set of coords. If I try to use a foreach loop like:

``````foreach(var myBust in temp) {
int minDist = coords.Select(x => getDist( x.Item1,x.Item2,myBust[1],myBust[2]) ).OrderBy(x => x).Distinct().First();
coords.RemoveAll(x => getDist(16000,9000,x.Item1,x.Item2) == minDist); }
``````

then I fall into an issue that the first piece configured may not be the closest to a point-set as compared to another scouting piece, which is why the foreach loop doesn't work or me. Therefore I want some type of linq/lambda statement that can return the said "Distinct" minimal coord-point in the coord list in comparison (by getDist and Min) to all the player pieces.

``````//Not sure why this doesn't give me what I am looking for
int minDist= coords.Zip(player, (x,y) => getDist(x.Item1,x.Item2,y[1],y[2])).Min();
``````

My dist method.

``````static int getDist(int x1, int y1, int x2, int y2)
{    return Convert.ToInt32( Math.Sqrt(Math.Pow(x1-x2,2) + Math.Pow(y1-y2,2) );}
``````

So for an answer I am looking for a way to run a function on two lists, which runs a function (but doesn't aggregate anything together), and can return whatever I want from one or both of those lists.

Based on the answer from @Jacob I came up with the below so far:

``````string[] movement = new string[player.Count];
List<int[]> temp = player;
while(temp.Any()){
HashSet<int> dists = new HashSet<int>();
foreach(var myBust in temp)
{ dists.UnionWith(coords.Select(x => getDist(x.Item1,x.Item2,myBust[1],myBust[2]) ));    }
foreach(var myBust in player)
{ if(coords.Exists(x => getDist( x.Item1,x.Item2,myBust[1],myBust[2]) == dists.Min() ) )
{
Tuple<int,int> result = coords.FindAll(x => getDist( x.Item1,x.Item2,myBust[1],myBust[2]) == dists.Min() ).First();
movement[player.IndexOf(myBust)] = "Move " + result.Item1 + " " + result.Item2;
Console.WriteLine("Player Number "+myBust[0]+" going a dist of "+dists.Min()+" to coords "+result.Item1+","+result.Item2);
coords.Remove(result);
temp.Remove(myBust);
}
}
}
``````

This gives the correct output of "Player Number 2 going a dist of 1871 to coords 3000,3300".
That is at least the first iteration through but then throws the error "Collection was modified; enumeration operation may not execute."
Any suggestions or modifications would be appreciated.

To find shortest paths without duplicating and starting with most minimal one first the code below worked.

``````List<Tuple<int,int,int,int,int>> coordSets = new List<Tuple<int,int,int,int,int>>();
List<int[]> temp = player.Where(x => x[4]==0 ).Where(x=> !needHelp.Any(y=> y[1]==x[0])).Where(x=> movement[player.IndexOf(x)]==null).ToList();