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 { protected class TraverseVisitItem { 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 ddx = new List(); List ddy = new List(); 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(); } /// /// Returns element at specified position in the map /// /// X /// Y /// Element protected abstract T ElementAt(int x, int y); /// /// Returns true if the specified coordinates are valid (e.g. inside the map) /// /// X /// Y /// True if coordinates are valid protected abstract bool CoordinatesValid(int x, int y); /// /// Returns true if specified item on the map can be visited /// /// Item to visit /// Previous node /// True if can be visited protected abstract bool CanVisit(TraverseVisitItem toVisit, TraverseVisitItem previous); /// /// Called when an item enqueued to be visited later /// /// Item to visit /// Previous node /// True if can be visited protected virtual void OnEnqueued(TraverseVisitItem toVisit, TraverseVisitItem previous) { } /// /// Called when an item is visited /// /// Visited item protected virtual void OnVisit(TraverseVisitItem item) { } /// /// Traverses a grid map /// /// X coordinate of traversal starting point /// Y coordinate of traversal starting point /// Possible traversal directions public virtual void Traverse(IEnumerable originsX, IEnumerable originsY, TravelDirections travelDirections = TravelDirections.NESW) { // Get directions int[] dx, dy; GenerateDirections(out dx, out dy, travelDirections); // Queue var toVisit = new Queue>(); // 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(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(ElementAt(newx, newy), newx, newy); if (CanVisit(newitem, item)) { toVisit.Enqueue(newitem); OnEnqueued(newitem, item); } } } } } ///// ///// Traverses a grid map ///// ///// X coordinate of traversal starting point ///// Y coordinate of traversal starting point ///// Possible traversal directions //public virtual void Traverse(int originX, int originY, TravelDirections travelDirections = TravelDirections.NESW) //{ // Traverse(Enumerable.Repeat(originX, 1), Enumerable.Repeat(originY, 1), travelDirections); //} } }