diff --git a/contents/flood_fill/code/csharp/FloodFill.cs b/contents/flood_fill/code/csharp/FloodFill.cs new file mode 100644 index 000000000..1d9adcfad --- /dev/null +++ b/contents/flood_fill/code/csharp/FloodFill.cs @@ -0,0 +1,168 @@ +using System.Linq; +using System.Diagnostics; +using System.Collections.Generic; + + +namespace Graphics +{ + abstract class FloodFill + { + // A simple point on the 2 dimensional integer plane. + private class Point2I + { + // Coordinates + public int x; + public int y; + + public Point2I(int x, int y) + { + this.x = x; + this.y = y; + } + } + + // Utility object for comparing List> objects. + private class FloatListEqualityComparer : IEqualityComparer> + { + public bool Equals(List one, List two) + { + return ReferenceEquals(one, two) || one != null && two != null && one.SequenceEqual(two); + } + + public int GetHashCode(List list) + { + return list.GetHashCode(); + } + } + + private static bool InBounds(Point2I size, Point2I loc) + { + return loc.x >= 0 && loc.y >= 0 && loc.x < size.x && loc.y < size.y; + } + + private static List FindNeighbors(ref List> grid, Point2I loc, float oldValue) + { + var possibleNeighbors = new List + { + new Point2I(loc.x, loc.y + 1), + new Point2I(loc.x + 1, loc.y), + new Point2I(loc.x, loc.y - 1), + new Point2I(loc.x - 1, loc.y) + }; + var neighbors = new List(); + var size = new Point2I(grid[0].Count, grid.Count); + + foreach (Point2I possibleNeighbor in possibleNeighbors) + { + var x = possibleNeighbor.x; + var y = possibleNeighbor.y; + if (!InBounds(size, possibleNeighbor)) { + continue; + } + if (grid[x][y].Equals(oldValue)) { + neighbors.Add(possibleNeighbor); + } + } + return neighbors; + } + + private static void RecursiveFill(ref List> grid, Point2I loc, float oldValue, float newValue) + { + if (oldValue.Equals(newValue)) { + return; + } + grid[loc.x][loc.y] = newValue; + + var possibleNeighbors = FindNeighbors(ref grid, loc, oldValue); + foreach (Point2I possibleNeighbor in possibleNeighbors) { + RecursiveFill(ref grid, possibleNeighbor, oldValue, newValue); + } + } + + private static void QueueFill(ref List> grid, Point2I loc, float oldValue, float newValue) + { + if (oldValue.Equals(newValue)) { + return; + } + + var queue = new Queue(); + queue.Enqueue(loc); + grid[loc.x][loc.y] = newValue; + + while (queue.Count > 0) + { + var currentLoc = queue.Dequeue(); + var possibleNeighbors = FindNeighbors(ref grid, currentLoc, oldValue); + foreach (Point2I neighbor in possibleNeighbors) + { + grid[neighbor.x][neighbor.y] = newValue; + queue.Enqueue(neighbor); + } + } + } + + private static void StackFill(ref List> grid, Point2I loc, float oldValue, float newValue) + { + if (oldValue.Equals(newValue)) { + return; + } + + var stack = new Stack(); + stack.Push(loc); + + while (stack.Count > 0) + { + var currentLoc = stack.Pop(); + + var x = currentLoc.x; + var y = currentLoc.y; + + if (grid[x][y].Equals(oldValue)) + { + grid[x][y] = newValue; + var possibleNeighbors = FindNeighbors(ref grid, currentLoc, oldValue); + foreach (Point2I neighbor in possibleNeighbors) { + stack.Push(neighbor); + } + } + } + } + + + private static void Main(string[] args) + { + // All neighbouring zeros, adjacent to (1, 1), must be replaced with 3 in the end result. + var grid = new List> + { + new List(){0, 0, 1, 0, 0}, + new List(){0, 0, 1, 0, 0}, + new List(){0, 0, 1, 0, 0}, + new List(){8, 0, 1, 1, 1}, + new List(){0, 0, 0, 0, 0} + }; + var solutionGrid = new List> + { + new List(){3, 3, 1, 0, 0}, + new List(){3, 3, 1, 0, 0}, + new List(){3, 3, 1, 0, 0}, + new List(){8, 3, 1, 1, 1}, + new List(){3, 3, 3, 3, 3} + }; + var startingPoint = new Point2I(1, 1); + var gridComparator = new FloatListEqualityComparer(); + + var testGrid = new List>(grid); + RecursiveFill(ref testGrid, startingPoint, 0, 3); + Debug.Assert(testGrid.SequenceEqual(solutionGrid, gridComparator), "Recursive Flood Fill Failed"); + + testGrid = new List>(grid); + QueueFill(ref testGrid, startingPoint, 0, 3); + Debug.Assert(testGrid.SequenceEqual(solutionGrid, gridComparator), "Queue Flood Fill Failed"); + + testGrid = new List>(grid); + StackFill(ref testGrid, startingPoint, 0, 3); + Debug.Assert(testGrid.SequenceEqual(solutionGrid, gridComparator), "Stack Flood Fill Failed"); + + } + } +} diff --git a/contents/flood_fill/flood_fill.md b/contents/flood_fill/flood_fill.md index ed84683a6..7d6aad813 100644 --- a/contents/flood_fill/flood_fill.md +++ b/contents/flood_fill/flood_fill.md @@ -96,6 +96,8 @@ In code, this might look like this: [import:10-25, lang="python"](code/python/flood_fill.py) {% sample lang="coco" %} [import:15-20, lang="coconut"](code/coconut/flood_fill.coco) +{% sample lang="cs" %} +[import:43-67, lang="csharp"](code/csharp/FloodFill.cs) {% endmethod %} @@ -118,6 +120,8 @@ In code, it might look like this: [import:55-63, lang="python"](code/python/flood_fill.py) {% sample lang="coco" %} [import:52-61, lang:"coconut"](code/coconut/flood_fill.coco) +{% sample lang="cs" %} +[import:69-80, lang="csharp"](code/csharp/FloodFill.cs) {% endmethod %} The above code continues recursing through available neighbors as long as neighbors exist, and this should work so long as we are adding the correct set of neighbors. @@ -135,6 +139,8 @@ Additionally, it is possible to do the same type of traversal by managing a stac [import:27-36, lang="python"](code/python/flood_fill.py) {% sample lang="coco" %} [import:23-34, lang:"coconut"](code/coconut/flood_fill.coco) +{% sample lang="cs" %} +[import:104-129, lang="csharp"](code/csharp/FloodFill.cs) {% endmethod %} This is ultimately the same method of traversal as before; however, because we are managing our own data structure, there are a few distinct differences: @@ -180,6 +186,8 @@ The code would look something like this: [import:38-53, lang="python"](code/python/flood_fill.py) {% sample lang="coco" %} [import:36-49, lang:"coconut"](code/coconut/flood_fill.coco) +{% sample lang="cs" %} +[import:82-102, lang="csharp"](code/csharp/FloodFill.cs) {% endmethod %} Now, there is a small trick in this code that must be considered to make sure it runs optimally. @@ -264,6 +272,8 @@ After, we will fill in the left-hand side of the array to be all ones by choosin [import:, lang="python"](code/python/flood_fill.py) {% sample lang="coco" %} [import, lang="coconut"](code/coconut/flood_fill.coco) +{% sample lang="cs" %} +[import, lang="csharp"](code/csharp/FloodFill.cs) {% endmethod %}