/// <summary>
        /// Generate the dungeon graph and layout
        /// </summary>
        /// <param name="totalTrials">number of trials used if any of the graph or map generation fails</param>
        /// <param name="graphTrials">number of trials to generate graph before consider it a fail</param>
        /// <param name="mapTrials">number of trials to generate the layout before consider it a fail</param>
        /// <returns>the generated graph and layout</returns>
        public static DungeonResult GenerateDungeon(int totalTrials  = 100, int graphTrials = 100, int mapTrials      = 100,
                                                    int recipeLength = 1, Random randomGen  = null, string recipeName = "graphRecipe")
        {
            Graph resultGraph = null;

            LayoutGrammar.Map resultMap = null;

            for (int i = 0; i < totalTrials; i++)
            {
                var       grammarPath = "FloorGeneration/";
                Generator mg          = CreateGenerator(grammarPath, randomGen);
                for (int j = 0; j < graphTrials; j++)
                {
                    TextAsset graphStartAsset  = Resources.Load <TextAsset>(grammarPath + "graphStart");
                    TextAsset graphRecipeAsset = Resources.Load <TextAsset>(grammarPath + recipeName);
                    const int numNodes         = 4;
                    resultGraph = mg.GenerateDungeonFromString(graphStartAsset.text, graphRecipeAsset.text, numNodes,
                                                               recipeLength);

                    if (resultGraph != null && Helper.CheckIsSolvable(resultGraph, resultGraph.nodes[0]))
                    {
                        break;
                    }

                    resultGraph = null;
                }

                if (resultGraph == null)
                {
                    continue;
                }

                LayoutGrammar.Generator lg = new LayoutGrammar.Generator(randomGen);
                for (int j = 0; j < mapTrials; j++)
                {
                    resultMap = lg.GenerateDungeon(resultGraph);
                    if (resultMap != null && Helper.CheckIsSolvable(resultMap.Get2DMap(), resultMap.GetCell(0)))
                    {
                        break;
                    }

                    resultMap = null;
                }

                if (resultMap == null)
                {
                    continue;
                }

                break;
            }

            return(new DungeonResult(resultGraph, resultMap));
        }
        /// <summary>
        /// Generate a map layout that correspond to the input mission graph
        /// </summary>
        /// <param name="graph">the mission graph that need to be mapped to a 2D layout</param>
        /// <returns>a 2D layout of the mission graph</returns>
        public Map GenerateDungeon(MissionGraph.Graph graph)
        {
            Map result = new Map(this.random);

            result.InitializeCell(graph.nodes[0]);

            #region  make initial dungeon

            List <MissionGraph.Node>            open      = new List <MissionGraph.Node>();
            Dictionary <MissionGraph.Node, int> parentIDs = new Dictionary <MissionGraph.Node, int>();
            foreach (MissionGraph.Node child in graph.nodes[0].GetChildren())
            {
                open.Add(child);
                parentIDs.Add(child, 0);
            }

            HashSet <MissionGraph.Node> nodes = new HashSet <MissionGraph.Node>();
            nodes.Add(graph.nodes[0]);
            while (open.Count > 0)
            {
                MissionGraph.Node current = open[0];
                open.RemoveAt(0);
                if (nodes.Contains(current))
                {
                    continue;
                }

                nodes.Add(current);
                if (!result.AddCell(current, parentIDs[current]))
                {
                    return(null);
                }

                foreach (MissionGraph.Node child in current.GetChildren())
                {
                    if (!parentIDs.ContainsKey(child))
                    {
                        if (current.type == MissionGraph.NodeType.Lock ||
                            current.type == MissionGraph.NodeType.Puzzle)
                        {
                            parentIDs.Add(child, current.id);
                        }
                        else
                        {
                            parentIDs.Add(child, parentIDs[current]);
                        }
                    }

                    open.Add(child);
                }
            }

            #endregion

            #region make lever connections

            open.Clear();
            nodes.Clear();
            open.Add(graph.nodes[0]);
            while (open.Count > 0)
            {
                MissionGraph.Node current = open[0];
                open.RemoveAt(0);
                if (nodes.Contains(current))
                {
                    continue;
                }

                nodes.Add(current);
                foreach (MissionGraph.Node child in current.GetChildren())
                {
                    Cell from = result.GetCell(current.id);
                    Cell to   = result.GetCell(child.id);
                    if (current.type == MissionGraph.NodeType.Lever)
                    {
                        if (!result.MakeConnection(from, to, nodes.Count * nodes.Count))
                        {
                            return(null);
                        }
                    }

                    open.Add(child);
                }
            }

            #endregion

            return(result);
        }