PotatoPlan/Game1/Sources/Pathing/A-Star/Astar.cs

233 lines
8.8 KiB
C#
Raw Permalink Normal View History

2020-05-03 13:05:05 +02:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
2020-05-09 15:54:53 +02:00
using C5;
using System.Diagnostics;
2020-05-03 13:05:05 +02:00
class Astar
{
private Vector2 tractorPos;
private Vector2 housePos;
2020-05-07 22:09:45 +02:00
private static Crops[,] crops;
2020-05-03 13:05:05 +02:00
private Vector2 Size;
2020-05-03 16:35:46 +02:00
private Vector2 targetPos;
private int Rotation;
2020-05-03 13:05:05 +02:00
2020-05-07 22:09:45 +02:00
public void update(Crops[,] newCrops, Vector2 newSize, Vector2 newTractorPos, Vector2 newHousePos, int rotation)
2020-05-03 13:05:05 +02:00
{
tractorPos = new Vector2((int)newTractorPos.X, (int)newTractorPos.Y);
housePos = new Vector2((int)newHousePos.X, (int)newHousePos.Y);
crops = newCrops;
Size = newSize;
Rotation = rotation;
2020-05-03 13:05:05 +02:00
}
2020-05-07 22:09:45 +02:00
public void update(Crops[,] newCrops, Vector2 newSize, Vector2 newTractorPos, Vector2 newHousePos, Vector2 newTargetPos, int rotation)
{
tractorPos = new Vector2((int)newTractorPos.X, (int)newTractorPos.Y);
housePos = new Vector2((int)newHousePos.X, (int)newHousePos.Y);
crops = newCrops;
Size = newSize;
Rotation = rotation;
targetPos = newTargetPos;
}
// Get all adjacent nodes
2020-05-08 00:35:53 +02:00
public List<Nodes> GetAdjacentNodes(Vector2 currentPos)
2020-05-03 13:05:05 +02:00
{
2020-05-03 16:35:46 +02:00
var adjacentNodes = new List<Nodes>()
{
2020-05-10 21:16:24 +02:00
new Nodes(new Vector2(currentPos.X, currentPos.Y - 1), 0),
2020-05-03 23:40:40 +02:00
new Nodes(new Vector2(currentPos.X + 1, currentPos.Y), 1),
2020-05-10 21:16:24 +02:00
new Nodes(new Vector2(currentPos.X, currentPos.Y + 1), 2),
2020-05-03 23:40:40 +02:00
new Nodes(new Vector2(currentPos.X - 1, currentPos.Y), -1),
2020-05-03 16:35:46 +02:00
};
2020-05-03 23:40:40 +02:00
//check if out of range
for (int i = 3; i >= 0; i--)
2020-05-03 17:06:03 +02:00
{
if (adjacentNodes[i].getCords().X < 0 || adjacentNodes[i].getCords().Y < 0)
adjacentNodes.Remove(adjacentNodes[i]);
else
{
2020-05-03 19:30:41 +02:00
if (adjacentNodes[i].getCords().X > Size.X - 1 || adjacentNodes[i].getCords().Y > Size.Y - 1)
2020-05-03 17:06:03 +02:00
adjacentNodes.Remove(adjacentNodes[i]);
}
}
// return if not an obstacle
2020-05-03 16:35:46 +02:00
return adjacentNodes.Where(
2020-05-05 16:27:45 +02:00
item => (crops[(int)item.getCords().X, (int)item.getCords().Y].getStatus()) != 0).ToList();
2020-05-03 16:35:46 +02:00
}
2020-05-03 23:40:40 +02:00
// Heuristic function, Manhattan method.
2020-05-03 16:35:46 +02:00
public int ComputeHScore(Vector2 currentNode, Vector2 endNote)
{
return (int)(Math.Abs(endNote.X - currentNode.X) + Math.Abs(endNote.Y - currentNode.Y));
}
// Rotation Cost
2020-05-03 23:40:40 +02:00
public int CalculateRotationCost(int currDir, int newDir)
{
if (currDir == newDir)
return 0;
else if (Math.Abs(currDir - newDir) == 1 || Math.Abs(currDir - newDir) == 3)
2020-05-10 13:51:19 +02:00
return 3;
else if (Math.Abs(currDir - newDir) == 0 || Math.Abs(currDir - newDir) == 2)
2020-05-10 23:11:15 +02:00
return 9;
2020-05-03 23:40:40 +02:00
return 0;
}
// Convert rotation used by sprite, to get direction of first node in next path
public int ConvertRotation()
{
int rotation = 0;
2020-05-09 15:54:53 +02:00
if (Rotation > 135 && Rotation < 225)
rotation = 0;
2020-05-09 15:54:53 +02:00
else if (Rotation > 225 && Rotation < 315)
rotation = 1;
2020-05-09 15:54:53 +02:00
else if (Rotation > 315 && Rotation < 45)
rotation = 2;
2020-05-09 15:54:53 +02:00
else if (Rotation > 45 && Rotation < 135)
rotation = -1;
return rotation;
}
// Main function of A* algorithm
2020-05-07 22:09:45 +02:00
public Path FindPath(bool flipArray)
2020-05-03 16:35:46 +02:00
{
int g = 0;
int direction = ConvertRotation();
2020-05-03 17:06:03 +02:00
Path path = new Path();
2020-05-03 22:28:52 +02:00
MinHeap openList = new MinHeap();
MinHeap closedList = new MinHeap();
2020-05-03 17:52:35 +02:00
Nodes target = new Nodes(targetPos);
Nodes startPos = new Nodes(tractorPos, direction);
2020-05-03 16:35:46 +02:00
Nodes current = null;
2020-05-03 22:28:52 +02:00
openList.Insert(startPos);
2020-05-03 16:35:46 +02:00
2020-05-03 22:28:52 +02:00
while (openList.GetSize() > 0)
2020-05-03 16:35:46 +02:00
{
2020-05-03 22:28:52 +02:00
current = openList.getMin();
closedList.Insert(current);
openList.removeMin();
2020-05-03 23:40:40 +02:00
direction = current.getDirection();
2020-05-03 16:35:46 +02:00
2020-05-03 22:28:52 +02:00
if (current.getCords() == target.getCords())
break;
2020-05-03 16:35:46 +02:00
var adjacentNodes = GetAdjacentNodes(current.getCords());
foreach (var adjacentNode in adjacentNodes)
2020-05-03 16:35:46 +02:00
{
if (closedList.Exists(adjacentNode.getCords())) // check if adjacent node is on closed list, if it is, skip it
2020-05-03 16:35:46 +02:00
continue;
g = current.getG() + crops[(int)adjacentNode.getCords().X, (int)adjacentNode.getCords().Y].getCostOnMovement() + CalculateRotationCost(direction, adjacentNode.getDirection()); // calculate g - cost from start point
if (!(openList.Exists(adjacentNode.getCords()))) // if adjacent node is not on open list, add it
2020-05-03 16:35:46 +02:00
{
adjacentNode.setG(g);
2020-05-03 17:52:35 +02:00
adjacentNode.setH(ComputeHScore(adjacentNode.getCords(), target.getCords()));
2020-05-03 16:35:46 +02:00
adjacentNode.calculateF();
adjacentNode.setParent(current);
2020-05-03 22:28:52 +02:00
openList.Insert(adjacentNode);
2020-05-03 16:35:46 +02:00
}
else
{
if (g + adjacentNode.getH() < adjacentNode.getF()) // check if adjacent node is a better path than the current one
2020-05-03 16:35:46 +02:00
{
adjacentNode.setG(g);
adjacentNode.calculateF();
adjacentNode.setParent(current);
}
}
2020-05-03 23:40:40 +02:00
2020-05-03 16:35:46 +02:00
}
}
// backtrack to create path
2020-05-03 17:52:35 +02:00
while (current != null)
2020-05-03 16:35:46 +02:00
{
path.AddNode(current);
current = current.getParent();
}
2020-05-07 22:09:45 +02:00
if (flipArray)
path = path.FlipArray();
2020-05-03 22:28:52 +02:00
openList.deleteHeap();
closedList.deleteHeap();
2020-05-09 15:54:53 +02:00
2020-05-03 16:35:46 +02:00
return path;
2020-05-03 13:05:05 +02:00
}
2020-05-03 16:35:46 +02:00
2020-05-10 21:16:24 +02:00
public bool isReachable(Crops[,] crops, Vector2 targetPos, Vector2 start)
{
Rotation = 0;
int g = 0;
int direction = ConvertRotation();
Path path = new Path();
MinHeap openList = new MinHeap();
MinHeap closedList = new MinHeap();
Nodes target = new Nodes(targetPos);
Nodes startPos = new Nodes(tractorPos, direction);
Nodes current = null;
openList.Insert(startPos);
while (openList.GetSize() > 0)
{
current = openList.getMin();
closedList.Insert(current);
openList.removeMin();
direction = current.getDirection();
if (current.getCords() == target.getCords())
break;
var adjacentNodes = GetAdjacentNodes(current.getCords());
foreach (var adjacentNode in adjacentNodes)
{
if (closedList.Exists(adjacentNode.getCords())) // check if adjacent node is on closed list, if it is, skip it
continue;
g = current.getG() + crops[(int)adjacentNode.getCords().X, (int)adjacentNode.getCords().Y].getCostOnMovement() + CalculateRotationCost(direction, adjacentNode.getDirection()); // calculate g - cost from start point
if (!(openList.Exists(adjacentNode.getCords()))) // if adjacent node is not on open list, add it
{
adjacentNode.setG(g);
adjacentNode.setH(ComputeHScore(adjacentNode.getCords(), target.getCords()));
adjacentNode.calculateF();
adjacentNode.setParent(current);
openList.Insert(adjacentNode);
}
else
{
if (g + adjacentNode.getH() < adjacentNode.getF()) // check if adjacent node is a better path than the current one
{
adjacentNode.setG(g);
adjacentNode.calculateF();
adjacentNode.setParent(current);
}
}
}
}
2020-05-10 23:11:15 +02:00
while (current != null)
{
path.AddNode(current);
current = current.getParent();
}
2020-05-10 21:16:24 +02:00
openList.deleteHeap();
closedList.deleteHeap();
2020-05-10 23:11:15 +02:00
if (path.getByIndex(0).getCords() != targetPos)
2020-05-10 21:16:24 +02:00
return false;
else
return true;
}
}