1
0
Fork 0
mirror of https://github.com/Ellpeck/MLEM.git synced 2024-11-16 18:53:12 +01:00
MLEM/MLEM/Pathfinding/AStar.cs
2019-08-06 15:33:45 +02:00

114 lines
No EOL
3.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
namespace MLEM.Pathfinding {
public static class AStar {
private static readonly Point[] AdjacentDirections = {
new Point(1, 0),
new Point(-1, 0),
new Point(0, 1),
new Point(0, -1)
};
private static readonly Point[] AllDirections = AdjacentDirections.Concat(new[] {
new Point(1, 1),
new Point(-1, 1),
new Point(1, -1),
new Point(-1, -1)
}).ToArray();
public static Stack<Point> FindPath(Point start, Point goal, int defaultCost, GetCost getCost, int maxTries = 10000, bool allowDiagonals = false) {
var open = new List<PathPoint>();
var closed = new List<PathPoint>();
open.Add(new PathPoint(start, goal, null, 0, defaultCost));
var count = 0;
while (open.Count > 0) {
PathPoint current = null;
var lowestF = int.MaxValue;
foreach (var point in open)
if (point.F < lowestF) {
current = point;
lowestF = point.F;
}
if (current == null)
break;
open.Remove(current);
closed.Add(current);
if (current.Pos.Equals(goal))
return CompilePath(current);
var dirsUsed = allowDiagonals ? AllDirections : AdjacentDirections;
foreach (var dir in dirsUsed) {
var neighborPos = current.Pos + dir;
var cost = getCost(neighborPos);
if (cost < int.MaxValue) {
var neighbor = new PathPoint(neighborPos, goal, current, cost, defaultCost);
if (!closed.Contains(neighbor)) {
var alreadyIndex = open.IndexOf(neighbor);
if (alreadyIndex < 0) {
open.Add(neighbor);
} else {
var alreadyNeighbor = open[alreadyIndex];
if (neighbor.G < alreadyNeighbor.G) {
open.Remove(alreadyNeighbor);
open.Add(neighbor);
}
}
}
}
}
count++;
if (count >= maxTries)
break;
}
return null;
}
private static Stack<Point> CompilePath(PathPoint current) {
var path = new Stack<Point>();
while (current != null) {
path.Push(current.Pos);
current = current.Parent;
}
return path;
}
public delegate int GetCost(Point pos);
}
public class PathPoint {
public readonly PathPoint Parent;
public readonly Point Pos;
public readonly int F;
public readonly int G;
public PathPoint(Point pos, Point goal, PathPoint parent, int terrainCostForThisPos, int defaultCost) {
this.Pos = pos;
this.Parent = parent;
this.G = (parent == null ? 0 : parent.G) + terrainCostForThisPos;
var manhattan = (Math.Abs(goal.X - pos.X) + Math.Abs(goal.Y - pos.Y)) * defaultCost;
this.F = this.G + manhattan;
}
public override bool Equals(object obj) {
if (obj == this)
return true;
return obj is PathPoint point && point.Pos.Equals(this.Pos);
}
public override int GetHashCode() {
return this.Pos.GetHashCode();
}
}
}