city-generation/Game/Assets/Scripts/Utils/Algorithms/GridTraverseAlgorithm.cs
2015-03-03 18:47:18 +02:00

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);
//}
}
}