A* pathfinding algorithm - got stuck calculating shortest path - c#

I'm trying to implement A* algorithm in order to find the shortest path in given grid.
My Node class:
public class Node : IComparable
{
public Node(int row, int col, Node previousNode = null, double distance = double.PositiveInfinity)
{
this.Row = row;
this.Col = col;
this.PreviousNode = previousNode;
this.Distance = distance;
}
public int Row { get; }
public int Col { get; }
public bool IsVisited { get; internal set; }
public double Distance { get; set; }
public int Weight { get; set; } = 1;
public double GScore { get; set; } = double.PositiveInfinity;
public double H { get; set; }
public double FScore => this.GScore + this.H;
public NodeType? NodeType { get; internal set; }
public Node PreviousNode { get; set; }
public override bool Equals(object obj)
{
var otherNode = obj as Node;
return this.Equals(otherNode);
}
protected bool Equals(Node other)
=> this.Row == other.Row && this.Col == other.Col;
public override int GetHashCode()
{
unchecked
{
return (this.Row * 397) ^ this.Col;
}
}
public int CompareTo(object obj)
{
var otherNode = obj as Node;
if (this.FScore == otherNode.FScore)
{
if (this.H >= otherNode.H)
{
return 1;
}
else if (this.H < otherNode.H)
{
return -1;
}
}
return this.FScore.CompareTo(otherNode.FScore);
}
}
A* algo class:
public override Result Execute(Node[,] grid, Node startNode, Node endNode)
{
var heap = new MinHeap<Node>();
var allSteps = new HashSet<Node>();
startNode.GScore = 0;
startNode.H = ManhattanDistance(startNode, endNode);
startNode.IsVisited = true;
heap.Add(startNode);
while (heap.Count != 0)
{
var currentNode = heap.Pop();
if (currentNode.NodeType == NodeType.Wall)
continue;
allSteps.Add(currentNode);
if (currentNode.Equals(endNode))
{
return new Result(allSteps, this.GetAllNodesInShortestPathOrder(currentNode));
}
var rowDirection = new[] { -1, +1, 0, 0 };
var columnDirection = new[] { 0, 0, +1, -1 };
for (int i = 0; i < 4; i++)
{
var currentRowDirection = currentNode.Row + rowDirection[i];
var currentColDirection = currentNode.Col + columnDirection[i];
if ((currentRowDirection < 0 || currentColDirection < 0)
|| (currentRowDirection >= grid.GetLength(0)
|| currentColDirection >= grid.GetLength(1)))
{
continue;
}
var nextNode = grid[currentRowDirection, currentColDirection];
AddNodeToHeap(currentNode, nextNode, endNode, heap);
}
}
return new Result(allSteps);
}
private void AddNodeToHeap(Node currentNode, Node nextNode, Node endNode, MinHeap<Node> heap)
{
if (nextNode.IsVisited || nextNode.GScore < currentNode.GScore)
return;
var g = currentNode.GScore + nextNode.Weight;
var h = ManhattanDistance(nextNode, endNode);
if (g + h < nextNode.FScore)
{
nextNode.GScore = g;
nextNode.H = h;
nextNode.PreviousNode = currentNode;
nextNode.IsVisited = true;
}
heap.Add(nextNode);
}
private static int ManhattanDistance(Node currentNode, Node endNode)
{
var dx = Math.Abs(currentNode.Row - endNode.Row);
var dy = Math.Abs(currentNode.Col - endNode.Col);
return dx + dy;
}
Custom MinHeap class:
public class MinHeap<T>
{
private readonly IComparer<T> comparer;
private readonly List<T> list = new List<T> { default };
public MinHeap()
: this(default(IComparer<T>))
{
}
public MinHeap(IComparer<T> comparer)
{
this.comparer = comparer ?? Comparer<T>.Default;
}
public MinHeap(Comparison<T> comparison)
: this(Comparer<T>.Create(comparison))
{
}
public int Count => this.list.Count - 1;
public void Add(T element)
{
this.list.Add(element);
this.ShiftUp(this.list.Count - 1);
}
public T Pop()
{
T result = this.list[1];
this.list[1] = this.list[^1];
this.list.RemoveAt(this.list.Count - 1);
this.ShiftDown(1);
return result;
}
private static int Parent(int i) => i / 2;
private static int Left(int i) => i * 2;
private static int Right(int i) => i * 2 + 1;
private void ShiftUp(int i)
{
while (i > 1)
{
int parent = Parent(i);
if (this.comparer.Compare(this.list[i], this.list[parent]) > 0)
{
return;
}
(this.list[parent], this.list[i]) = (this.list[i], this.list[parent]);
i = parent;
}
}
private void ShiftDown(int i)
{
for (int left = Left(i); left < this.list.Count; left = Left(i))
{
int smallest = this.comparer.Compare(this.list[left], this.list[i]) <= 0 ? left : i;
int right = Right(i);
if (right < this.list.Count && this.comparer.Compare(this.list[right], this.list[smallest]) <= 0)
{
smallest = right;
}
if (smallest == i)
{
return;
}
(this.list[i], this.list[smallest]) = (this.list[smallest], this.list[i]);
i = smallest;
}
}
}
The problem is that it doesn't find the optimal path when I have some weights on the map. For example:
Every square on the grid which is marked as a weight node has weight of 10 otherwise it's 1.
Here's example:
Example grid with 3 weight nodes - green node is start node, red node is end node and the dumbbell node is weight node.
When I run the algorithm I get the following result.
It's clearly visible that this is not the shortest path since the algorithm goes through the first node which has weight 1 and then the next node with weight 10 instead of just passing one 10 weight node. The shortest path should've been the red one which I've marked.
P.S I've managed to make it respect the weights by adding new heuristic function when calculating GCost and it now calculates the path but instead of one straight line I get some strange path:
Thank you in advance!

#jdweng
I actually fixed the bug by implementing an additional method which adds additional weight to the GScore
blue squares - all steps which the algorithm took in order to find the shortest final path
A* Algo with weights
A* algo without weights
[
Dijkstra Algo with weights
Dijkstra Algo without weights
private (double weight, NodeDirection? Direction) GetDistanceAndDirection(Node nodeOne, Node nodeTwo)
{
var x1 = nodeOne.Row;
var y1 = nodeOne.Col;
var x2 = nodeTwo.Row;
var y2 = nodeTwo.Col;
if (x2 < x1 && y1 == y2)
{
switch (nodeOne.Direction)
{
case NodeDirection.Up:
return (1, NodeDirection.Up);
case NodeDirection.Right:
return (2, NodeDirection.Up);
case NodeDirection.Left:
return (2, NodeDirection.Up);
case NodeDirection.Down:
return (3, NodeDirection.Up);
}
}
else if (x2 > x1 && y1 == y2)
{
switch (nodeOne.Direction)
{
case NodeDirection.Up:
return (3, NodeDirection.Down);
case NodeDirection.Right:
return (2, NodeDirection.Down);
case NodeDirection.Left:
return (2, NodeDirection.Down);
case NodeDirection.Down:
return (1, NodeDirection.Down);
}
}
if (y2 < y1 && x1 == x2)
{
switch (nodeOne.Direction)
{
case NodeDirection.Up:
return (2, NodeDirection.Left);
case NodeDirection.Right:
return (3, NodeDirection.Left);
case NodeDirection.Left:
return (1, NodeDirection.Left);
case NodeDirection.Down:
return (2, NodeDirection.Left);
}
}
else if (y2 > y1 && x1 == x2)
{
switch (nodeOne.Direction)
{
case NodeDirection.Up:
return (2, NodeDirection.Right);
case NodeDirection.Right:
return (1, NodeDirection.Right);
case NodeDirection.Left:
return (3, NodeDirection.Right);
case NodeDirection.Down:
return (2, NodeDirection.Right);
}
}
return default;
}
and then AddNodeToHeapMethod()
private void AddNodeToHeap(Node currentNode, Node nextNode, Node endNode, MinHeap<Node> heap)
{
if (nextNode.IsVisited)
return;
var (additionalWeight, direction) = this.GetDistanceAndDirection(currentNode, nextNode);
var g = currentNode.GScore+ nextNode.Weight + additionalWeight;
var h = this.ManhattanDistance(nextNode, endNode);
if (g < nextNode.GScore)
{
nextNode.GScore= g;
nextNode.H = h;
nextNode.PreviousNode = currentNode;
nextNode.IsVisited = true;
}
currentNode.Direction = direction;
heap.Add(nextNode);
}

Related

Is there there such a thing as a negative index graph? Index was outside the bounds of the array

I have my unity project set up with a graph like this:
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}
if (obj is Point)
{
Point p = obj as Point;
return this.X == p.X && this.Y == p.Y;
}
return false;
}
public override int GetHashCode()
{
unchecked
{
int hash = 6949;
hash = hash * 7907 + X.GetHashCode();
hash = hash * 7907 + Y.GetHashCode();
return hash;
}
}
public override string ToString()
{
return "P(" + this.X + ", " + this.Y + ")";
}
}
public enum CellType
{
Empty,
Road,
Structure,
SpecialStructure,
None
}
public class Grid
{
public CellType[,] _grid;
private int _width;
public int Width { get { return _width; } }
private int _height;
public int Height { get { return _height; } }
public List<Point> _roadList = new List<Point>();
public List<Point> _specialStructure = new List<Point>();
public List<Point> _houseStructure = new List<Point>();
public Grid(int width, int height)
{
_width = width;
_height = height;
_grid = new CellType[width, height];
}
// Adding index operator to our Grid class so that we can use grid[][] to access specific cell from our grid.
public CellType this[int i, int j]
{
get
{
return _grid[i, j];
}
set
{
if (value == CellType.Road)
{
_roadList.Add(new Point(i, j));
}
if (value == CellType.SpecialStructure)
{
_specialStructure.Add(new Point(i, j));
}
if (value == CellType.Structure)
{
_houseStructure.Add(new Point(i, j));
}
_grid[i, j] = value;
}
}
And I'm instantiating a series of gameobjects using an L-System algorithm and adding their X and Z positions to the graph like this:
public Grid AddToGrid(Vector3Int position, string type)
{
//CellType parsed_enum = (CellType)System.Enum.Parse(typeof(CellType), type);
switch (type)
{
case "Structure":
placementGrid._houseStructure.Add(new Point((int)position.x, (int)position.z));
break;
case "SpecialStructure":
placementGrid._specialStructure.Add(new Point((int)position.x, (int)position.z));
break;
case "Road":
placementGrid._roadList.Add(new Point((int)position.x, (int)position.z));
break;
}
return placementGrid;
}
And then in one of my scripts I'm calling another function:
public List<Point> GetWakableAdjacentCells(int x, int y, bool isAgent)
{
List<Point> adjacentCells = GetAllAdjacentCells(x, y);
Debug.Log("Adjacent Cells"+ adjacentCells.Count);//3
Debug.Log("X"+x); //-1
Debug.Log("Y"+y);//1
for (int i = adjacentCells.Count - 1; i >= 0; i--)
{
Debug.Log(adjacentCells[i].X); //-1
Debug.Log(adjacentCells[i].Y);//2
if (IsCellWakable(_grid[adjacentCells[i].X, adjacentCells[i].Y], isAgent) == false)
{
adjacentCells.RemoveAt(i);
}
}
return adjacentCells;
}
But this says "index was outside the bounds of the array" at if condition. I've commented each relevant value next to the variables for ease.
The function this condition is checking is this:
public static bool IsCellWakable(CellType cellType, bool aiAgent = false)
{
Debug.Log("boo");
if (aiAgent)
{
return cellType == CellType.Road;
}
return cellType == CellType.Empty || cellType == CellType.Road;
}
What am I doing wrong?
is there a way to implement a grid in such a way way that minus values can be accessed?
Well you commented yourself
// -1
-> -1 can not be a valid index in c#.
If you want to have a wrap around you could probably do e.g.
public CellType this[int i, int j]
{
get
{
i = (i % _width) + _width) % _width;
j = (j % _height) + _height) % _height;
return _grid[i, j];
}
set
{
i = (i % _width) + _width) % _width;
j = (j % _height) + _height) % _height;
switch(value)
{
case CellType.Road:
_roadList.Add(new Point(i, j));
break;
case CellType.SpecialStructure:
_specialStructure.Add(new Point(i, j));
break;
case CellType.Structure:
_houseStructure.Add(new Point(i, j));
break;
}
_grid[i, j] = value;
}
}
This means of course that your grid is basically "infinite" and crossing the top boarder you will be at the bottom again etc.

how to to multiply and divide in a static stack?

This is the static array I have been given in making a RPN calculator. From this code the RPN calculator adds and subtracts. Now I need to extend my code to multiply and divide but I cant I don't know how.
public class IntStack
{
private const int maxsize = 10;
private int top = 0;
private int[] array = new int[maxsize];
public void Push(int value)
{
array[top++] = value;
}
public int Pop()
{
return array[--top];
}
public int Peek()
{
return array[top - 1];
}
public bool IsEmpty()
{
return top == 0;
}
public bool IsFull()
{
return top == maxsize;
}
public string Print()
{
StringBuilder output = new StringBuilder();
for (int i = top - 1; i >= 0; i--)
output.Append(array[i] + Environment.NewLine);
return output.ToString();
}
}
Here are some methods you can add to your IntStack class that will perform the multiply and division operations. I've added minimal error checking.
public void Multiply()
{
if (array.Length < 2)
return;
var factor1 = Pop();
var factor2 = Pop();
Push(factor1 * factor2);
}
public void Divide()
{
if (array.Length < 2)
return;
var numerator = Pop();
var divisor = Pop();
if (divisor == 0) { // Return stack back to original state.
Push(divisor);
Push(numerator);
return;
}
Push(numerator / divisor);
}

C# Linked Lists (adding an element to a defined position)

Our teacher asked us to implement the class LinkedList. I was able to implement everything he asked for. But he gave us a bonus question that I was not able to solving.
He asked us to implement this fonction:
public void add(float x, int pos)
{
// Add x at the position pos, pos = 0 refer to the first element.
}
He also demanded that his code verifies the following UnitTest:
public class UnitTest1
{
private MyList l;
public UnitTest1()
{
l = new MyList();
for (int i = 0; i < 10; ++i)
{
l.add(i * i);
}
for (int i = 0; i < 10; ++i)
{
l.add(i * i);
}
}
[TestMethod]
public void TestAdd()
{
Assert.AreEqual(l.count(), 20);
}
[TestMethod]
public void TestGet()
{
for (int i = 0; i < 10; ++i)
{
Assert.AreEqual(l.get(i), (9 - i) * (9 - i));
}
for (int i = 10; i < 20; ++i)
{
Assert.AreEqual(l.get(i), (19 - i) * (19 - i));
}
}
[TestMethod]
public void TestFind()
{
int k;
for (int i = 0; i < 100; ++i)
{
for (k = 0; k < 10; ++k)
{
if (k * k == i)
{
Assert.AreEqual(l.find(i), true);
break;
}
}
if (k == 10)
{
Assert.AreEqual(l.find(i), false);
}
}
}
[TestMethod]
public void TestStats()
{
Assert.AreEqual(l.max(), 81);
float s = 0;
for (int i = 0; i < 10; ++i)
{
s += i * i + i * i;
}
Assert.AreEqual(l.sum(), s);
Assert.AreEqual(l.average(), s / 20);
}
[TestMethod]
public void TestCountValue()
{
MyList l1 = new MyList();
for (int i = 0; i < 10; ++i)
{
l1.add(i);
l1.add(i * i);
}
Assert.AreEqual(l1.count(-1), 0);
Assert.AreEqual(l1.count(0), 2);
Assert.AreEqual(l1.count(1), 2);
Assert.AreEqual(l1.count(2), 1);
Assert.AreEqual(l1.count(3), 1);
Assert.AreEqual(l1.count(4), 2);
Assert.AreEqual(l1.count(5), 1);
Assert.AreEqual(l1.count(6), 1);
Assert.AreEqual(l1.count(7), 1);
Assert.AreEqual(l1.count(8), 1);
Assert.AreEqual(l1.count(9), 2);
Assert.AreEqual(l1.count(10), 0);
Assert.AreEqual(l1.count(16), 1);
}
[TestMethod]
public void TestRemoveFirst()
{
MyList l1 = new MyList();
for (int i = 0; i < 10; ++i)
{
l1.add(i);
l1.add(i * i);
}
Assert.AreEqual(l1.count(81), 1);
l1.removeFirst();
Assert.AreEqual(l1.count(), 19);
Assert.AreEqual(l1.count(81), 0);
Assert.AreEqual(l1.count(-1), 0);
Assert.AreEqual(l1.count(0), 2);
Assert.AreEqual(l1.count(1), 2);
Assert.AreEqual(l1.count(2), 1);
Assert.AreEqual(l1.count(3), 1);
Assert.AreEqual(l1.count(4), 2);
Assert.AreEqual(l1.count(5), 1);
Assert.AreEqual(l1.count(6), 1);
Assert.AreEqual(l1.count(7), 1);
Assert.AreEqual(l1.count(8), 1);
Assert.AreEqual(l1.count(9), 2);
Assert.AreEqual(l1.count(10), 0);
Assert.AreEqual(l1.count(16), 1);
}
[TestMethod]
public void TestInsert()
{
MyList l1 = new MyList();
for (int i = 9; i >= 0; --i)
{
l1.add(i);
}
for (int i = 0; i <= 10; ++i)
{
l1.add(i, 2 * i);
}
for (int i = 0; i < 10; ++i)
{
Assert.AreEqual(l1.get(2 * i), i, "i=" + i);
Assert.AreEqual(l1.get(2 * i + 1), i);
}
Assert.AreEqual(l1.get(20), 10);
}
}
That's what I was capable of:
public class MyList
{
class Element
{
public float value;
public Element next;
}
Element first;
public MyList()
{
first = null;
}
public void add(float x)
{
Element e = new Element();
e.value = x;
e.next = first;
first = e;
}
public float get(int i)
{
if (first == null)
{
throw new Exception("Empty list... no elements inside");
}
Element tmp = first;
for (int j = 0; j < i; ++j)
{
tmp = tmp.next;
if (tmp == null)
{
throw new Exception("...");
}
}
return tmp.value;
}
public void print()
{
Element e = first;
while (e != null)
{
Console.WriteLine(e.value);
e = e.next;
}
}
public bool find(float x)
{
Element e = first;
while (e != null)
{
if (e.value == x)
{
return true;
}
e = e.next;
}
return false;
}
public float max()
{
float G = 0;
for (Element e = first; e != null; e = e.next)
{
if (e.value > G)
{
G = e.value;
}
}
return G;
}
public int count()
{
Element e = first;
int c = 0;
while (e != null)
{
c++;
e = e.next;
}
return c;
}
public int count(float x)
{
int c = 0;
for (Element e = first; e != null; e = e.next)
{
if (e.value == x)
{
c++;
}
}
return c;
}
public float sum()
{
float S = 0;
for (Element e = first; e != null; e = e.next)
{
S += e.value;
}
return S;
}
public float average()
{
return sum() / count();
}
public void removeFirst()
{
Element e = first;
first = e.next;
}
public void add(float x, int pos)
{
//I have absolutely no idea how to implement this fonction.
}
}
It will be similar to the code in the get() method.
Break your problem down into smaller problems
Does pos = 0?
if so, create a new root and point it to the old root
if not, loop (pos) times and create a new element. Then set the next property of the new element to the next property of the current element. Set the current element next property to your new element
.
public void add(float x, int pos)
{
if(pos == 0)
{
// create a new root and point the new root to the existing root
}
else
{
Element tmp = first;
for(int i = 0; i < pos; i++)
{
tmp = tmp.next;
}
// create new element
// set new element next property to tmp.next
// set tmp.next to new element
}
}

K-means with initial centers

I'm doing K-means clustering of n points and k centers.
To start off, here's my code so far:
A class for a point:
public class Point
{
public int Id { get; set; }
public double X { get; set; }
public double Y { get; set; }
public Point()
{
Id = -1;
X = -1;
Y = -1;
}
public Point(int id, double x, double y)
{
this.Id = id;
this.X = x;
this.Y = y;
}
public static double FindDistance(Point pt1, Point pt2)
{
double x1 = pt1.X, y1 = pt1.Y;
double x2 = pt2.X, y2 = pt2.Y;
double distance = Math.Sqrt(Math.Pow(x2 - x1, 2.0) + Math.Pow(y2 - y1, 2.0));
return (distance);
}
}
A class for data:
public class PointCollection : List<Point>
{
public Point Centroid { get; set; }
public PointCollection()
: base()
{
Centroid = new Point();
}
public void AddPoint(Point p)
{
this.Add(p);
UpdateCentroid();
}
public Point RemovePoint(Point p)
{
Point removedPoint = new Point(p.Id, p.X, p.Y);
this.Remove(p);
UpdateCentroid();
return (removedPoint);
}
public void UpdateCentroid()
{
double xSum = (from p in this select p.X).Sum();
double ySum = (from p in this select p.Y).Sum();
Centroid.X = (xSum / (double)this.Count);
Centroid.Y = (ySum / (double)this.Count);
}
}
Main class:
public class KMeans
{
public static List<PointCollection> DoKMeans(PointCollection points, int clusterCount)
{
List<PointCollection> allClusters = new List<PointCollection>();
List<List<Point>> allGroups = ListUtility.SplitList<Point>(points, clusterCount);
foreach (List<Point> group in allGroups)
{
PointCollection cluster = new PointCollection();
cluster.AddRange(group);
allClusters.Add(cluster);
}
int movements = 1;
while (movements > 0)
{
movements = 0;
foreach (PointCollection cluster in allClusters)
{
for (int pointIndex = 0; pointIndex < cluster.Count; pointIndex++)
{
Point point = cluster[pointIndex];
int nearestCluster = FindNearestCluster(allClusters, point);
if (nearestCluster != allClusters.IndexOf(cluster))
{
if (cluster.Count > 1)
{
Point removedPoint = cluster.RemovePoint(point);
allClusters[nearestCluster].AddPoint(removedPoint);
movements += 1;
}
}
}
}
}
return (allClusters);
}
public static int FindNearestCluster(List<PointCollection> allClusters, Point point)
{
double minimumDistance = 0.0;
int nearestClusterIndex = -1;
for (int k = 0; k < allClusters.Count; k++)
{
double distance = Point.FindDistance(point, allClusters[k].Centroid);
if (k == 0)
{
minimumDistance = distance;
nearestClusterIndex = 0;
}
else if (minimumDistance > distance)
{
minimumDistance = distance;
nearestClusterIndex = k;
}
}
return (nearestClusterIndex);
}
}
And finally helping function for list split:
public static List<List<T>> SplitList<T>(List<T> items, int groupCount)
{
List<List<T>> allGroups = new List<List<T>>();
int startIndex = 0;
int groupLength = (int)Math.Round((double)items.Count / (double)groupCount, 0);
while (startIndex < items.Count)
{
List<T> group = new List<T>();
group.AddRange(items.GetRange(startIndex, groupLength));
startIndex += groupLength;
if (startIndex + groupLength > items.Count)
{
groupLength = items.Count - startIndex;
}
allGroups.Add(group);
}
if (allGroups.Count > groupCount && allGroups.Count > 2)
{
allGroups[allGroups.Count - 2].AddRange(allGroups.Last());
allGroups.RemoveAt(allGroups.Count - 1);
}
return (allGroups);
}
So, now I'm trying to write a second method for the main class, which would accept a predefined starting centres. I have trouble grasping that though, as I can't find anything on the internet where the k-means algorithm would use initial centers. Can someone point me in a direction of such guide or give me any ideas how to modify the code? Thanks.
Edit: Maybe something more why am I trying to do this: I try to write LBG algorithm using k-means, like so https://onlinecourses.science.psu.edu/stat557/node/67
I can access the computed centers for splitting in each of the steps with my code, however I need to find a way to feed them back to the k-means class. Like, if I calculated the starting centre, i need to put this centre and another offset by epsilon into k-means algorithm.
Edit2: Code now in English(I hope)
Found a solution, maybe someone will use:
public static List<PointCollection> DoKMeans(PointCollection points, int clusterCount, Point[] startingCentres)
{
// code...
int ctr = 0;
foreach (List<Point> group in allGroups)
{
PointCollection cluster = new PointCollection();
cluster.c.X = startingCentres[ctr].X;
cluster.c.Y = startingCentres[ctr].Y;
cluster.AddRange(group);
allClusters.Add(cluster);
}
// rest of code the same
}

C# Class Method to loop through a List created in the same Class

To start off I am new to C# and I am in need of some help. I a class that contains a list. I can set the items in the list from the application but not from the class(where it needs to be done). I am also needing to move an event to class as well. This works in the app as well. Any help with this is greatly appreciated. Below is the code from my class:
namespace CarRace
{
class Cars
{
public string Name { get; set; }
public int StartPOS { get; set; }
public int Speed { get; set; }
public int CurPOS { get; set; }
public double Location { get; set; }
public Cars(string Name, int StartPOS, int Speed, int CurPOS, long Location)
{
this.Name = Name;
this.StartPOS = StartPOS;
this.Speed = Speed;
this.CurPOS = 0;
this.Location = 0;
}
public static int CompareCurrentLocation(Cars c1, Cars c2)
{
return c2.Location.CompareTo(c1.Location);
}
}
}
I am needing to add this to the class:
if (File.Exists("NewRace.xml"))
{
XDocument doc = XDocument.Load("NewRace.xml");
var nCars = doc.Descendants("Car").Select(x => new Cars("", 0, 0, 0, 0)
{
Name = x.Attribute("Name").Value,
StartPOS = int.Parse(x.Attribute("StartPOS").Value),
Speed = int.Parse(x.Attribute("Speed").Value),
CurPOS = 0
});
}
And this:
int p = 1;
int prevPOS = 1;
DateTime? PrevTime;
double dist = 0;
double PrevLoc = 0;
if (i == 0)
{
return;
}
foreach (var car in RaceList)
{
dist = (((car.Speed * 1609.344) * 1) / 1609.344) / 3600;
car.Location = car.Location + dist;
}
Comparison<Cars> compLoc = Cars.CompareCurrentLocation;
RaceList.Sort(compLoc);
PrevTime = DateTime.Now;
foreach (var car in RaceList)
{
if (car.Location != PrevLoc)
{
car.CurPOS = p;
}
else
{
car.CurPOS = prevPOS;
}
prevPOS = car.CurPOS;
PrevLoc = car.Location;
p++;
}
Thanks
Thanks for all of your replies. I tried the car.speed/3600 but only get
back 0 for that so I did the long way. I did get everything working
the way I needed it to.
That's because you are doing an integer division (car.Speed is of type int) so the fractional part of the result is discarded. Since the car's speed is less than 3600 this would hence always result in zero. You can avoid this by casting car.Speed to a double first - this should produce the result you want:
dist = (double)car.Speed / 3600;
Thanks for all of your replies. I tried the car.speed/3600 but only get back 0 for that so I did the long way. I did get everything working the way I needed it to. I am a rookie at this and any tips are greatly appreciated. Below is the code I have updated to.
namespace CarRace
{
class Cars
{
public string Name { get; set; }
public int StartPOS { get; set; }
public int CurPOS { get; set; }
public int Speed { get; set; }
public double Location { get; set; }
public string Make { get; set; }
private static List<Cars> CarList;
private static string ProgPart;
public Cars(string Name, int StartPOS, int CurPOS, int Speed, double Location, string Make)
{
this.Name = Name;
this.StartPOS = StartPOS;
this.CurPOS = CurPOS;
this.Speed = Speed;
this.Location = Location;
this.Make = Make;
}
public static List<Cars> FillList()
{
return CarList;
}
public static void Main()
{
try
{
bool Prog = false;
//Get Part to display
while (!Prog)
{
Console.WriteLine("Select the part you would like to test.(1 or 2)");
ProgPart = Console.ReadLine();
if (ProgPart == "1" || ProgPart == "2")
{
Prog = true;
}
}
Console.WriteLine("----------------------------------------");
//Read XML File and set the CarList
if (File.Exists("NewRace.xml"))
{
XDocument doc = XDocument.Load("NewRace.xml");
var nCars = doc.Descendants("Car").Select(x => new Cars("", 0, 0, 0, 0, "")
{
Name = x.Attribute("Name").Value,
StartPOS = int.Parse(x.Element("StartPOS").Value),
CurPOS = 0,
Speed = int.Parse(x.Element("Speed").Value),
Location = double.Parse(x.Element("Location").Value),
Make = x.Attribute("Make").Value
});
CarList = new List<Cars>();
foreach (var car1 in nCars)
{
if (ProgPart == "1")
{
CarList.Add(new Cars(car1.Name, car1.StartPOS, car1.CurPOS, car1.Speed, car1.Location, car1.Make));
}
else
{
CarList.Add(new Cars(car1.Name, car1.StartPOS, car1.CurPOS, 0, car1.Location, car1.Make));
}
}
}
else
{
Console.WriteLine("File Not Found!");
Console.ReadKey();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static int CompareCurrentLocation(Cars c1, Cars c2)
{
return c2.Location.CompareTo(c1.Location);
}
public static Boolean UpdateLeaderBoard(long i)
{
try
{
int p = 1;
int prevPOS = 1;
DateTime? PrevTime;
double dist = 0;
double prevLocation = 0;
if (i == 0)
{
return false;
}
foreach (var car in CarList)
{
if (ProgPart == "2")
{
switch (car.Make)
{
case "300ZX":
//Slow continuous gain of speed by percentage after 60 with top speed of 320.
if (i <= 15)
{
car.Speed = car.Speed + (60 / 10);
}
else if (car.Speed < 320)
{
double percent = (double)(car.Speed * .1);
if ((Convert.ToInt32(car.Speed + percent)) > 320)
{
car.Speed = 320;
}
else
{
car.Speed = Convert.ToInt32(car.Speed + (percent));
}
}
break;
case "Camero":
//0-60 4 seconds 60-220 20 seconds Top 220
if (i <= 4)
{
car.Speed = car.Speed + (60 / 4);
}
else if (i <= 20)
{
car.Speed = car.Speed + 10;
}
break;
case "Mustang":
//0-top(210) 25 seconds
if (car.Speed <= 200)
{
car.Speed = car.Speed + (200 / 20);
}
break;
case "VW Bug":
//Constant Speed
car.Speed = 165;
break;
default:
Console.WriteLine("Make not found");
break;
}
}
//Set Cars Current Location
dist = (((car.Speed * 1609.344) * 1) / 1609.344) / 3600;
car.Location = car.Location + dist;
}
//Sort list by location
Comparison<Cars> compLoc = Cars.CompareCurrentLocation;
CarList.Sort(compLoc);
PrevTime = DateTime.Now;
//Set the Current Position
foreach (var car in CarList)
{
if (car.Location != prevLocation)
{
car.CurPOS = p;
}
else
{
car.CurPOS = prevPOS;
}
prevPOS = car.CurPOS;
prevLocation = car.Location;
p++;
}
return true;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return false;
}
}
}
}

Categories

Resources