patterncsharpModerate
"Simple" pathfinding algorithm
Viewed 0 times
pathfindingsimplealgorithm
Problem
I decided to write a pathfinding algorithm in order to expose myself to the world of pathfinding and for me to further understand more advanced algorithms such as A*. I chose C# due to its flexibility compared to other languages such as Java.
I will start with the basic objects and work up to the more complex ones.
vec2.cs:
Tile.cs:
Map.cs:
```
class Map
{
Tile[,] map;
public Map(Tile[,] t) {
this.map = t;
}
public int X { get { return map.GetLength(0); } }
public int Y { get { return map.GetLength(1); } }
public Tile this[int x, int y] {
get { return map[x, y]; }
set { map[x, y] = value; }
}
public static Map makeMap(string[] map)
{
Tile[,] t = new Tile[map.Length, map[0].Length];
for(int i = 0; i < map.Length; i++)
for (int j = 0; j < map[0].Length; j++)
{
char c = map[i].ToCharArray()[j];
t[i, j] = new Tile { xy = new vec2(i, j
I will start with the basic objects and work up to the more complex ones.
vec2.cs:
struct vec2
{
public int x, y;
public vec2(int x, int y)
{
this.x = x;
this.y = y;
}
public static bool operator== (vec2 a, vec2 b) {
return a.x == b.x && a.y == b.y;
}
public static bool operator !=(vec2 a, vec2 b)
{
return !(a == b);
}
public static vec2 operator+ (vec2 a, vec2 b)
{
return new vec2 { x = a.x + b.x, y = a.y + b.y };
}
//Distance formula
public static double operator* (vec2 a, vec2 b) {
return Math.Sqrt( Math.Pow(b.x-a.x,2) + Math.Pow(b.y-a.y,2) );
}
}Tile.cs:
struct Tile
{
public enum TileType
{
FREE, OBSTACLE
}
public TileType ttype;
public vec2 xy;
public int x {get {return xy.x;}}
public int y {get {return xy.y;}}
public static bool operator== (Tile a, Tile b) {
return a.x == b.x && a.y == b.y && a.ttype == b.ttype;
}
public static bool operator !=(Tile a, Tile b)
{
return !(a == b);
}
}Map.cs:
```
class Map
{
Tile[,] map;
public Map(Tile[,] t) {
this.map = t;
}
public int X { get { return map.GetLength(0); } }
public int Y { get { return map.GetLength(1); } }
public Tile this[int x, int y] {
get { return map[x, y]; }
set { map[x, y] = value; }
}
public static Map makeMap(string[] map)
{
Tile[,] t = new Tile[map.Length, map[0].Length];
for(int i = 0; i < map.Length; i++)
for (int j = 0; j < map[0].Length; j++)
{
char c = map[i].ToCharArray()[j];
t[i, j] = new Tile { xy = new vec2(i, j
Solution
Let's just review this vector; there's plenty to talk about here.
This may seem like nit picking but hey, first, you asked, and second, build strong foundations in your helper types. If they are weak, you will build a weak, buggy program on top of them. Make them do exactly what they say on the tin. This is a 2-vector of integers, so make it follow the conventions of a 2-vector of integers.
Follow C# naming conventions. No abbrvs! Use PascalCase for names.
Mutable fields in a struct are badness; so many bugs come from this. Public fields in general in C# are considered to be a bad practice. Use properties backed by private fields. Set them once. Never change them. If you need to change a vector then you make a new vector, same way that if you need to change an integer, you don't turn 12 into 17 when you add 5. 12 stays the same.
The implementation is fine, but I would always override
This is "make an empty vector and then mutate it". You already have a constructor for that:
is better.
NO NO NO. Do not make cute overloads of operators! Ever! The product of two vectors is not the distance between the points they point at when they both begin at the origin! The product of two vectors is either the dot product or the cross product, and this is neither of them.
You're representing a mathematical object here. Use the conventions of mathematics that we're all familiar with. You need the distance between two vectors? Subtract them and take the magnitude.
So let's start over.
The C# compiler generates the field, getter and setter for you. In C# 6 you can elide the
The
Whether you use
The point here is that we now have one-stop shopping for equality.
You can use this method to implement
Seems legit.
As a debugging aid:
I note that all of these methods are a lot more concise in C# 6, without losing comprehensibility:
so if you can write these in C# 6, do so.
Now we could also use unary subtraction and binary subtraction:
And magnitude:
Again, much shorter in C# 6:
And now we can implement distance:
We could also use an explicit zero:
All right. I'll give some thoughts on the rest of your implementation in another answer; this is enough for one answer.
This may seem like nit picking but hey, first, you asked, and second, build strong foundations in your helper types. If they are weak, you will build a weak, buggy program on top of them. Make them do exactly what they say on the tin. This is a 2-vector of integers, so make it follow the conventions of a 2-vector of integers.
struct vec2Follow C# naming conventions. No abbrvs! Use PascalCase for names.
public int x, y;Mutable fields in a struct are badness; so many bugs come from this. Public fields in general in C# are considered to be a bad practice. Use properties backed by private fields. Set them once. Never change them. If you need to change a vector then you make a new vector, same way that if you need to change an integer, you don't turn 12 into 17 when you add 5. 12 stays the same.
public static bool operator== (vec2 a, vec2 b) {The implementation is fine, but I would always override
Equals when overriding ==. Even in this case where it is not strictly necessary I think it is wise.public static vec2 operator+ (vec2 a, vec2 b)
{
return new vec2 { x = a.x + b.x, y = a.y + b.y };
}This is "make an empty vector and then mutate it". You already have a constructor for that:
new vec2(x : a.x + b.x, y : a.y + b.y );is better.
//Distance formula
public static double operator* (vec2 a, vec2 b) {
return Math.Sqrt( Math.Pow(b.x-a.x,2) + Math.Pow(b.y-a.y,2) );
}NO NO NO. Do not make cute overloads of operators! Ever! The product of two vectors is not the distance between the points they point at when they both begin at the origin! The product of two vectors is either the dot product or the cross product, and this is neither of them.
You're representing a mathematical object here. Use the conventions of mathematics that we're all familiar with. You need the distance between two vectors? Subtract them and take the magnitude.
So let's start over.
struct Vector2
{
public int X { get; private set; }
public int Y { get; private set; }The C# compiler generates the field, getter and setter for you. In C# 6 you can elide the
private set;.public Vector2(int x, int y) : this()
{
X = x;
Y = y;
}The
this() tells the compiler that it should initialize the hidden fields to zero, which the compiler likes to happen before instance properties are accessed.public static bool Equals(Vector2 a, Vector2 b)
{
return (a.X == b.X) & (a.Y == b.Y);
}Whether you use
& or && here doesn't really matter. Remember, checking to see if the previous comparison produced false and skipping the second comparison might be more work than simply doing the second comparison! The point here is that we now have one-stop shopping for equality.
public static bool operator== (Vector2 a, Vector2 b)
{
return Equals(a, b);
}
public static bool operator !=(Vector2 a, Vector2 b)
{
return !Equals(a, b);
}
public bool Equals(Vector2 a)
{
return Equals(this, a);
}You can use this method to implement
IEquatable which might be a good idea.public override bool Equals(object a)
{
return a is Vector2 && Equals(this, (Vector2)a);
}
public override int GetHashCode()
{
return X + 17 * Y;
}Seems legit.
As a debugging aid:
public override string ToString()
{
return "(" + X + "," + Y + ")";
}I note that all of these methods are a lot more concise in C# 6, without losing comprehensibility:
public override string ToString() => $"({X},{Y})";so if you can write these in C# 6, do so.
public static Vector2 operator+ (Vector2 a, Vector2 b)
{
return new Vector2(a.X + b.X, a.Y + b.Y);
}Now we could also use unary subtraction and binary subtraction:
public static Vector2 operator- (Vector2 a, Vector2 b)
{
return new Vector2(a.X - b.X, a.Y - b.Y);
}
public static Vector2 operator- (Vector2 a)
{
return new Vector2(-a.X, -a.Y);
}And magnitude:
public double Magnitude
{
get
{
return Math.Sqrt(X * X + Y * Y);
}
}Again, much shorter in C# 6:
public double Magnitude => Sqrt(X * X, Y * Y);And now we can implement distance:
public static double Distance(Vector2 a, Vector2 b)
{
return (a - b).Magnitude;
}
public double Distance (Vector2 a)
{
return Distance(this, a);
}We could also use an explicit zero:
public readonly static Vector2 Zero = default(Vector2);All right. I'll give some thoughts on the rest of your implementation in another answer; this is enough for one answer.
Code Snippets
struct vec2public int x, y;public static bool operator== (vec2 a, vec2 b) {public static vec2 operator+ (vec2 a, vec2 b)
{
return new vec2 { x = a.x + b.x, y = a.y + b.y };
}new vec2(x : a.x + b.x, y : a.y + b.y );Context
StackExchange Code Review Q#114512, answer score: 10
Revisions (0)
No revisions yet.