199 lines
6.5 KiB
C#
199 lines
6.5 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
namespace TransportGame.Utils.Algorithms
|
|
{
|
|
[Flags]
|
|
public enum TravelDirections
|
|
{
|
|
North = 0x1,
|
|
NorthEast = 0x2,
|
|
East = 0x4,
|
|
SouthEast = 0x8,
|
|
South = 0x10,
|
|
SouthWest = 0x20,
|
|
West = 0x40,
|
|
NorthWest = 0x80,
|
|
NESW = North | East | West | South,
|
|
All = North | NorthEast | East | SouthEast | South | SouthWest | West | NorthWest,
|
|
}
|
|
|
|
public abstract class GridTraverseAlgorithm<T>
|
|
{
|
|
protected class TraverseVisitItem<TItem>
|
|
{
|
|
public TItem Item { get; private set; }
|
|
public int X { get; private set; }
|
|
public int Y { get; private set; }
|
|
|
|
public TraverseVisitItem(TItem item, int x, int y)
|
|
{
|
|
Item = item;
|
|
X = x;
|
|
Y = y;
|
|
}
|
|
}
|
|
|
|
private static void GenerateDirections(out int[] dx, out int[] dy, TravelDirections dirs)
|
|
{
|
|
List<int> ddx = new List<int>();
|
|
List<int> ddy = new List<int>();
|
|
|
|
if ((dirs & TravelDirections.North) > 0)
|
|
{
|
|
ddx.Add(0);
|
|
ddy.Add(1);
|
|
}
|
|
if ((dirs & TravelDirections.NorthEast) > 0)
|
|
{
|
|
ddx.Add(1);
|
|
ddy.Add(1);
|
|
}
|
|
if ((dirs & TravelDirections.East) > 0)
|
|
{
|
|
ddx.Add(1);
|
|
ddy.Add(0);
|
|
}
|
|
if ((dirs & TravelDirections.SouthEast) > 0)
|
|
{
|
|
ddx.Add(1);
|
|
ddy.Add(-1);
|
|
}
|
|
if ((dirs & TravelDirections.South) > 0)
|
|
{
|
|
ddx.Add(0);
|
|
ddy.Add(-1);
|
|
}
|
|
if ((dirs & TravelDirections.SouthWest) > 0)
|
|
{
|
|
ddx.Add(-1);
|
|
ddy.Add(-1);
|
|
}
|
|
if ((dirs & TravelDirections.West) > 0)
|
|
{
|
|
ddx.Add(-1);
|
|
ddy.Add(0);
|
|
}
|
|
if ((dirs & TravelDirections.NorthWest) > 0)
|
|
{
|
|
ddx.Add(-1);
|
|
ddy.Add(1);
|
|
}
|
|
|
|
dx = ddx.ToArray();
|
|
dy = ddy.ToArray();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns element at specified position in the map
|
|
/// </summary>
|
|
/// <param name="x">X</param>
|
|
/// <param name="y">Y</param>
|
|
/// <returns>Element</returns>
|
|
protected abstract T ElementAt(int x, int y);
|
|
|
|
/// <summary>
|
|
/// Returns true if the specified coordinates are valid (e.g. inside the map)
|
|
/// </summary>
|
|
/// <param name="x">X</param>
|
|
/// <param name="y">Y</param>
|
|
/// <returns>True if coordinates are valid</returns>
|
|
protected abstract bool CoordinatesValid(int x, int y);
|
|
|
|
/// <summary>
|
|
/// Returns true if specified item on the map can be visited
|
|
/// </summary>
|
|
/// <param name="toVisit">Item to visit</param>
|
|
/// <param name="previous">Previous node</param>
|
|
/// <returns>True if can be visited</returns>
|
|
protected abstract bool CanVisit(TraverseVisitItem<T> toVisit, TraverseVisitItem<T> previous);
|
|
|
|
/// <summary>
|
|
/// Called when an item enqueued to be visited later
|
|
/// </summary>
|
|
/// <param name="toVisit">Item to visit</param>
|
|
/// <param name="current">Previous node</param>
|
|
/// <returns>True if can be visited</returns>
|
|
protected virtual void OnEnqueued(TraverseVisitItem<T> toVisit, TraverseVisitItem<T> previous)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called when an item is visited
|
|
/// </summary>
|
|
/// <param name="item">Visited item</param>
|
|
protected virtual void OnVisit(TraverseVisitItem<T> item)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Traverses a grid map
|
|
/// </summary>
|
|
/// <param name="originX">X coordinate of traversal starting point</param>
|
|
/// <param name="originY">Y coordinate of traversal starting point</param>
|
|
/// <param name="travelDirections">Possible traversal directions</param>
|
|
public virtual void Traverse(IEnumerable<int> originsX, IEnumerable<int> originsY, TravelDirections travelDirections = TravelDirections.NESW)
|
|
{
|
|
// Get directions
|
|
int[] dx, dy;
|
|
GenerateDirections(out dx, out dy, travelDirections);
|
|
|
|
// Queue
|
|
var toVisit = new Queue<TraverseVisitItem<T>>();
|
|
|
|
// Enqueue origins
|
|
var itX = originsX.GetEnumerator();
|
|
var itY = originsY.GetEnumerator();
|
|
|
|
while (itX.MoveNext() && itY.MoveNext())
|
|
{
|
|
if (CoordinatesValid(itX.Current, itY.Current))
|
|
{
|
|
var origin = new TraverseVisitItem<T>(ElementAt(itX.Current, itY.Current), itX.Current, itY.Current);
|
|
toVisit.Enqueue(origin);
|
|
}
|
|
}
|
|
|
|
// Visit nodes in queue
|
|
while (toVisit.Count > 0)
|
|
{
|
|
var item = toVisit.Dequeue();
|
|
|
|
// Visit item
|
|
OnVisit(item);
|
|
|
|
// Enqueue neighbours in each direction
|
|
for (int k = 0; k < dx.Length; k++)
|
|
{
|
|
int newx = item.X + dx[k];
|
|
int newy = item.Y + dy[k];
|
|
|
|
if (CoordinatesValid(newx, newy))
|
|
{
|
|
var newitem = new TraverseVisitItem<T>(ElementAt(newx, newy), newx, newy);
|
|
|
|
if (CanVisit(newitem, item))
|
|
{
|
|
toVisit.Enqueue(newitem);
|
|
OnEnqueued(newitem, item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///// <summary>
|
|
///// Traverses a grid map
|
|
///// </summary>
|
|
///// <param name="originX">X coordinate of traversal starting point</param>
|
|
///// <param name="originY">Y coordinate of traversal starting point</param>
|
|
///// <param name="travelDirections">Possible traversal directions</param>
|
|
//public virtual void Traverse(int originX, int originY, TravelDirections travelDirections = TravelDirections.NESW)
|
|
//{
|
|
// Traverse(Enumerable.Repeat(originX, 1), Enumerable.Repeat(originY, 1), travelDirections);
|
|
//}
|
|
}
|
|
}
|