Edward - 6 months ago 29

C# Question

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.Add(Tuple.Create(j,i));

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.

Answer

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();
coords.ForEach(x => temp.ForEach(y => coordSets.Add(Tuple.Create(y[1],y[2],x[0],x[1],getDist(x[0],x[1],y[1],y[2])))));
for(int i =0; i<temp.Count; i++)
{
Tuple<int,int,int,int,int> coordSet = coordSets.OrderBy(x=>x.Item5).First();
movement[player.FindIndex(x=> x[1]==coordSet.Item1 && x[2]==coordSet.Item2)] = "Move " + coordSet.Item3 + " " + coordSet.Item4;
coordSets.RemoveAll(x=> x.Item1==coordSet.Item1 && x.Item2==coordSet.Item2);
coordSets.RemoveAll(x=> x.Item3==coordSet.Item3 && x.Item4==coordSet.Item4);
}
```