public static bool IsCyclic(
            this PM_Maze maze
            )
        {
            List <Vec2i> nodes = maze.CellsPositions_All_List();

            // Mark all the vertices as not visited
            // and not part of recursion stack
            HashSet <Vec2i> visitedNodes = new HashSet <Vec2i>();

            // Call the recursive helper function
            // to detect cycle in different DFS trees
            foreach (var node in nodes)
            {
                // Don't recur for u if already visited
                if (visitedNodes.Contains(node) == false)
                {
                    if (IsCyclic_Helper(maze, node, visitedNodes, new Vec2i(-1, -1)))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
        public static int Num_Steps_NecessaryToReachAllCells(
            this PM_Maze maze,
            Vec2i root
            )
        {
            var allNodes = maze.CellsPositions_All_List();

            Dictionary <Vec2i, Vec2i> predecessors = new Dictionary <Vec2i, Vec2i>();
            Dictionary <Vec2i, int>   depths       = new Dictionary <Vec2i, int>();

            depths.Add(root, 0);

            foreach (var node in allNodes)
            {
                predecessors.Add(node, node);
            }

            HashSet <Vec2i> visited_Nodes = new HashSet <Vec2i>()
            {
                root
            };

            Queue <Vec2i> searchQueue = new Queue <Vec2i>();

            searchQueue.Enqueue(root);

            while (searchQueue.Count > 0)
            {
                var current = searchQueue.Dequeue();

                var          neighbors          = maze.Cells_Connected_To_Cell__List(current);
                List <Vec2i> unvisitedNeighbors = neighbors.FindAll(x => visited_Nodes.Contains(x) == false);

                foreach (var neighbor in unvisitedNeighbors)
                {
                    predecessors[neighbor] = current;
                    searchQueue.Enqueue(neighbor);
                    visited_Nodes.Add(neighbor);
                    depths.Add(neighbor, depths[current] + 1);
                }
            }

            int maxDepth = 0;

            foreach (var node in allNodes)
            {
                if (depths[node] > maxDepth)
                {
                    maxDepth = depths[node];
                }
            }

            return(maxDepth);
        }
        public static HashSet <Vec2i> BFS_ReachableCells_Set(
            this PM_Maze maze,
            Vec2i root,
            bool includeRoot
            )
        {
            var allNodes = maze.CellsPositions_All_List();

            Dictionary <Vec2i, Vec2i> predecessors = new Dictionary <Vec2i, Vec2i>();

            foreach (var node in allNodes)
            {
                predecessors.Add(node, node);
            }

            HashSet <Vec2i> visited_Nodes = new HashSet <Vec2i>()
            {
                root
            };

            Queue <Vec2i> searchQueue = new Queue <Vec2i>();

            searchQueue.Enqueue(root);

            while (searchQueue.Count > 0)
            {
                var current = searchQueue.Dequeue();

                var          neighbors          = maze.Cells_Connected_To_Cell__List(current);
                List <Vec2i> unvisitedNeighbors = neighbors.FindAll(x => visited_Nodes.Contains(x) == false);

                foreach (var neighbor in unvisitedNeighbors)
                {
                    predecessors[neighbor] = current;
                    searchQueue.Enqueue(neighbor);
                    visited_Nodes.Add(neighbor);
                }
            }

            if (includeRoot)
            {
                return(visited_Nodes);
            }
            else
            {
                visited_Nodes.Remove(root);
                return(visited_Nodes);
            }
        }
        /// <summary>
        /// Method that searches whether the destination cell is reachable from the root cell.
        /// It searches the graph using a BFS algorithm.
        /// </summary>
        /// <param name="maze"></param>
        /// <param name="root"></param>
        /// <param name="destination"></param>
        /// <returns></returns>
        public static bool Q_Is_Reachable_BFS(this PM_Maze maze, Vec2i root, Vec2i destination)
        {
            if (root.Equals(destination))
            {
                return(true);
            }

            List <Vec2i> all_nodes = maze.CellsPositions_All_List();

            Dictionary <Vec2i, bool> visited_nodes = new Dictionary <Vec2i, bool>();

            foreach (var n in all_nodes)
            {
                visited_nodes.Add(n, false);
            }

            Queue <Vec2i> searchQueue = new Queue <Vec2i>();

            searchQueue.Enqueue(root);
            visited_nodes[root] = true;

            while (searchQueue.Count > 0)
            {
                var          current            = searchQueue.Dequeue();
                var          neighbors          = maze.Cells_Connected_To_Cell__List(current);
                List <Vec2i> unvisitedNeighbors = neighbors.FindAll(x => visited_nodes[x] == false);

                foreach (var neighbor in unvisitedNeighbors)
                {
                    searchQueue.Enqueue(neighbor);
                    visited_nodes[neighbor] = true;

                    if (neighbor.Equals(destination))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
        public static HashSet <Vec2i> BFS_ReachableNodes_MaximumSteps(
            this PM_Maze maze,
            Vec2i root,
            bool includeRoot,
            int maximumSteps
            )
        {
            // start the bfs search...

            Dictionary <Vec2i, Vec2i> predecessors = new Dictionary <Vec2i, Vec2i>();
            Dictionary <Vec2i, int>   numSteps     = new Dictionary <Vec2i, int>();

            var allNodes = maze.CellsPositions_All_List();

            //T dummyNode = T.Dummy();
            foreach (var node in allNodes)
            {
                predecessors.Add(node, node);
                numSteps.Add(node, int.MaxValue);
            }

            HashSet <Vec2i> visited_Nodes = new HashSet <Vec2i>()
            {
                root
            };

            Queue <Vec2i> searchQueue = new Queue <Vec2i>();

            searchQueue.Enqueue(root);
            numSteps[root] = 0;

            while (searchQueue.Count > 0)
            {
                var current = searchQueue.Dequeue();
                if (numSteps[current] < maximumSteps)
                {
                    HashSet <Vec2i> unvisitedNeighbors = maze.Cells_Connected_To_Cell__Set(current);
                    unvisitedNeighbors.ExceptWith(visited_Nodes);

                    foreach (var neighbor in unvisitedNeighbors)
                    {
                        predecessors[neighbor] = current;
                        numSteps[neighbor]     = numSteps[current] + 1;
                        searchQueue.Enqueue(neighbor);
                        visited_Nodes.Add(neighbor);
                    }
                }
            }

            HashSet <Vec2i> reachable_within_steps = new HashSet <Vec2i>();

            foreach (var node in visited_Nodes)
            {
                if (numSteps[node] <= maximumSteps)
                {
                    reachable_within_steps.Add(node);
                }
            }

            if (includeRoot)
            {
                return(reachable_within_steps);
            }
            else
            {
                reachable_within_steps.Remove(root);
                return(reachable_within_steps);
            }
        }
        public static List <Vec2i> BFS_ShortestPath(
            this PM_Maze maze,
            Vec2i root,
            Vec2i destination
            )
        {
            if (root.Equals(destination))
            {
                throw new System.ArgumentException("root equals destination");
            }

            Dictionary <Vec2i, Vec2i> predecessors = new Dictionary <Vec2i, Vec2i>();

            var allCells = maze.CellsPositions_All_List();

            foreach (var node in allCells)
            {
                predecessors.Add(node, node);
            }

            List <Vec2i> visitedNodes = new List <Vec2i>();

            Queue <Vec2i> searchQueue = new Queue <Vec2i>();

            searchQueue.Enqueue(root);
            visitedNodes.Add(root);
            bool foundDestination = false;

            while (searchQueue.Count > 0 && foundDestination == false)
            {
                var          current            = searchQueue.Dequeue();
                var          neighbors          = maze.Cells_Connected_To_Cell__List(current);
                List <Vec2i> unvisitedNeighbors = neighbors.FindAll(x => visitedNodes.Contains(x) == false);

                foreach (var neighbor in unvisitedNeighbors)
                {
                    predecessors[neighbor] = current;
                    searchQueue.Enqueue(neighbor);
                    visitedNodes.Add(neighbor);

                    if (neighbor.Equals(destination))
                    {
                        foundDestination = true;
                        break;
                    }
                }
            }

            List <Vec2i> shortestPath = new List <Vec2i>();

            bool pathFinished        = false;
            var  currentPathPosition = destination;

            shortestPath.Add(currentPathPosition);
            while (pathFinished == false)
            {
                var predecessor = predecessors[currentPathPosition];
                if (predecessor.Equals(currentPathPosition) == false)
                {
                    shortestPath.Add(predecessor);
                    currentPathPosition = predecessor;
                }
                else
                {
                    pathFinished = true;
                }
            }

            shortestPath.Reverse();

            if (
                shortestPath.Contains(root)
                &&
                shortestPath.Contains(destination)
                )
            {
                return(shortestPath);
            }

            return(new List <Vec2i>());
        }