/// <summary>
    /// Run the generator and export the result.
    /// </summary>
    public LayoutGrid2D <int> Run()
    {
        LayoutGrid2D <int> layout = null;

        var levelDescription = GetLevelDescription();
        var generator        = new GraphBasedGeneratorGrid2D <int>(levelDescription);
        var i = 0;

        while (i < maxAttemptsToGenerateLevel)
        {
            try
            {
                layout = generator.GenerateLayout();
                break;
            }
            catch
            {
                i++;
                if (i >= maxAttemptsToGenerateLevel)
                {
                    throw;
                }
            }
        }


        return(layout);
    }
        /// <summary>
        /// Run the generator.
        /// </summary>
        public void Run()
        {
            var levelDescription = GetLevelDescription();

            var generator = new GraphBasedGeneratorGrid2D <Room>(levelDescription);
            var layout    = generator.GenerateLayout();

            var drawer = new DungeonDrawer <Room>();

            drawer.DrawLayoutAndSave(layout, "basics.png", new DungeonDrawerOptions()
            {
                Width  = 1000,
                Height = 1000,
            });

            #region no-clean

            var roomTemplates = levelDescription
                                .GetGraph().Vertices
                                .Select(levelDescription.GetRoomDescription)
                                .Where(x => x.IsCorridor == false)
                                .SelectMany(x => x.RoomTemplates)
                                .Distinct()
                                .ToList();
            var corridorRoomTemplates = levelDescription
                                        .GetGraph().Vertices
                                        .Select(levelDescription.GetRoomDescription)
                                        .Where(x => x.IsCorridor)
                                        .SelectMany(x => x.RoomTemplates)
                                        .Distinct()
                                        .ToList();
            var roomTemplatesDrawer = new RoomTemplateDrawer();
            var roomTemplatesBitmap = roomTemplatesDrawer.DrawRoomTemplates(roomTemplates, new DungeonDrawerOptions()
            {
                Width             = 2000,
                Height            = 1600,
                PaddingPercentage = 0.05f,
                FontSize          = 2,
                EnableHatching    = false,
            });
            roomTemplatesBitmap.Save(ExamplesGenerator.AssetsFolder + "/room_templates.png");

            var corridorRoomTemplatesBitmap = roomTemplatesDrawer.DrawRoomTemplates(corridorRoomTemplates, new DungeonDrawerOptions()
            {
                Width           = 1200,
                Height          = 350,
                FontSize        = 0.7f,
                PaddingAbsolute = 100,
                ShowRoomNames   = false,
                EnableHatching  = false,
            });
            corridorRoomTemplatesBitmap.Save(ExamplesGenerator.AssetsFolder + "/corridor_room_templates.png");

            #endregion
        }
Beispiel #3
0
        private void DrawOutput(RectangleF rect)
        {
            var level   = generator.GenerateLayout();
            var drawing = dungeonDrawer.DrawLayout(level, new DungeonDrawerOptions()
            {
                Width  = (int)rect.Width,
                Height = (int)rect.Height,
            });

            graphics.DrawImage(drawing, rect);
        }
        public void Run()
        {
            var levelDescription = GetLevelDescription();
            var generator        = new GraphBasedGeneratorGrid2D <int>(levelDescription);
            var layout           = generator.GenerateLayout();

            var drawer = new DungeonDrawer <int>();
            var bitmap = drawer.DrawLayout(layout, new DungeonDrawerOptions()
            {
                Height = 1000,
                Width  = 1000,
            });

            bitmap.Save("minimum_room_distance.png");
        }
Beispiel #5
0
        private IGeneratorRunner GetGeneratorRunnerWithInit(LevelDescriptionGrid2D <TNode> levelDescription)
        {
            var configuration = this.configuration.SmartClone();

            configuration.RoomsCanTouch = levelDescription.MinimumRoomDistance == 0;

            var layoutDrawer  = new SVGLayoutDrawer <TNode>();
            var seedGenerator = new Random();

            return(new LambdaGeneratorRunner(() =>
            {
                var stopwatch = new Stopwatch();
                stopwatch.Start();

                var layoutGenerator = new GraphBasedGeneratorGrid2D <TNode>(levelDescription, configuration);
                layoutGenerator.InjectRandomGenerator(new Random(seedGenerator.Next()));

                var simulatedAnnealingArgsContainer = new List <SimulatedAnnealingEventArgs>();

                void SimulatedAnnealingEventHandler(object sender, SimulatedAnnealingEventArgs eventArgs)
                {
                    simulatedAnnealingArgsContainer.Add(eventArgs);
                }

                layoutGenerator.OnSimulatedAnnealingEvent += SimulatedAnnealingEventHandler;
                var layout = layoutGenerator.GenerateLayout();
                layoutGenerator.OnSimulatedAnnealingEvent -= SimulatedAnnealingEventHandler;

                stopwatch.Stop();

                var additionalData = new AdditionalRunData <TNode>()
                {
                    SimulatedAnnealingEventArgs = simulatedAnnealingArgsContainer,
                    //GeneratedLayoutSvg =
                    //    layout != null ? layoutDrawer.DrawLayout(layout, 800, forceSquare: true) : null,
                    // GeneratedLayout = layout,
                };

                var generatorRun = new GeneratorRun <AdditionalRunData <TNode> >(layout != null, stopwatch.ElapsedMilliseconds,
                                                                                 layoutGenerator.IterationsCount, additionalData);

                return generatorRun;
            }));
        }
Beispiel #6
0
        public override IEnumerator Process()
        {
            var levelDescription = Payload.LevelDescription;

            if (config.Timeout <= 0)
            {
                throw new ArgumentException($"{nameof(config.Timeout)} must be greater than 0", nameof(config.Timeout));
            }

            var rootGameObject = config.RootGameObject;

            // If the root game objects was not set in the config, we do the following:
            // 1. Check if there already exists a game objects with a name reserved for the generated level
            // 2. Otherwise, we create a new empty game object
            if (rootGameObject == null)
            {
                rootGameObject = GameObject.Find("Generated Level");

                if (rootGameObject == null)
                {
                    rootGameObject = new GameObject("Generated Level");
                }
            }

            // We delete all the children from the root game object - we do not want to combine levels from different runs of the algorithm
            foreach (var child in rootGameObject.transform.Cast <Transform>().ToList())
            {
                child.transform.parent = null;
                PostProcessUtils.Destroy(child.gameObject);
            }

            // The LevelDescription class must be converted to MapDescription
            var levelDescriptionGrid2D = levelDescription.GetLevelDescription();

            levelDescriptionGrid2D.MinimumRoomDistance            = 1;
            levelDescriptionGrid2D.RoomTemplateRepeatModeOverride = GeneratorUtils.GetRepeatMode(config.RepeatModeOverride);

            var configuration = new GraphBasedGeneratorConfiguration <RoomBase>()
            {
                EarlyStopIfTimeExceeded = TimeSpan.FromMilliseconds(config.Timeout),
            };

            // We create the instance of the dungeon generator and inject the correct Random instance
            var generator = new GraphBasedGeneratorGrid2D <RoomBase>(levelDescriptionGrid2D, configuration);

            generator.InjectRandomGenerator(Payload.Random);

            // Run the generator in a different class so that the computation is not blocking
            LayoutGrid2D <RoomBase> layout = null;
            var task = Task.Run(() => layout = generator.GenerateLayout());

            while (!task.IsCompleted)
            {
                yield return(null);
            }

            // Throw an exception when a timeout is reached
            // TODO: this should be our own exception and not a generic exception
            if (layout == null)
            {
                if (task.Exception != null)
                {
                    if (task.Exception.InnerException != null)
                    {
                        throw task.Exception.InnerException;
                    }

                    throw task.Exception;
                }
                else
                {
                    throw new TimeoutException();
                }
            }

            // Transform the level to its Unity representation
            var generatedLevel = GeneratorUtils.TransformLayout(layout, levelDescription, rootGameObject);

            var stats = new GeneratorStats()
            {
                Iterations = generator.IterationsCount,
                TimeTotal  = generator.TimeTotal,
            };

            Debug.Log($"Layout generated in {stats.TimeTotal / 1000f:F} seconds");
            Debug.Log($"{stats.Iterations} iterations needed, {stats.Iterations / (stats.TimeTotal / 1000d):0} iterations per second");

            ((IGraphBasedGeneratorPayload)Payload).GeneratedLevel = generatedLevel;
            Payload.GeneratorStats = stats;

            yield return(null);
        }
        private void AddResults <TRoom>(StringBuilder output, IExampleGrid2D <TRoom> example)
        {
            var sourceCode       = sourceCodeParser.GetMethod("GetResults", false);
            var results          = example.GetResults().ToList();
            var codeBlockHandler = new CodeBlockHandler(output);

            output.AppendLine("## Results");
            output.AppendLine();

            var resultsCounter = 0;

            foreach (var line in sourceCode)
            {
                var trimmed = line.Trim();

                if (trimmed.StartsWith("//md"))
                {
                    codeBlockHandler.Exit();

                    if (trimmed.Length > 5)
                    {
                        trimmed = trimmed.Remove(0, 5);
                    }
                    else
                    {
                        trimmed = trimmed.Remove(0, 4);
                    }

                    output.AppendLine(trimmed);
                }
                else if (trimmed.Contains("yield") && !trimmed.Contains("yield break"))
                {
                    codeBlockHandler.Exit();

                    var levelDescription = results[resultsCounter];
                    var initStopwatch    = new Stopwatch();
                    initStopwatch.Start();
                    var generator = new GraphBasedGeneratorGrid2D <TRoom>(levelDescription);
                    generator.InjectRandomGenerator(new Random(0));
                    initStopwatch.Stop();

                    var layoutDrawer = new GraphBasedGenerator.Grid2D.Drawing.SVGLayoutDrawer <TRoom>();
                    var oldMapDrawer = new DungeonDrawer <TRoom>();

                    var times = new List <long>();

                    for (int i = 0; i < 4; i++)
                    {
                        var generatorStopwatch = new Stopwatch();
                        generatorStopwatch.Start();
                        var level = generator.GenerateLayout();
                        generatorStopwatch.Stop();

                        times.Add(generatorStopwatch.ElapsedMilliseconds);

                        Console.WriteLine(generatorStopwatch.ElapsedMilliseconds + initStopwatch.ElapsedMilliseconds);

                        var svg = layoutDrawer.DrawLayout(level, 800, forceSquare: true, flipY: true, fixedFontSize: 30);
                        File.WriteAllText(Path.Combine(AssetsFolder, $"{resultsCounter}_{i}.svg"),
                                          svg);

                        var bitmap = oldMapDrawer.DrawLayout(level, new DungeonDrawerOptions()
                        {
                            Width             = 2000,
                            Height            = 2000,
                            PaddingPercentage = 0.1f,
                        });
                        bitmap.Save(Path.Combine(AssetsFolder, $"{resultsCounter}_{i}.png"));
                    }

                    var summaryDrawer = new GeneratorSummaryDrawer <TRoom>();
                    var summary       = summaryDrawer.Draw(levelDescription, 5000, generator);
                    summary.Save(Path.Combine(AssetsFolder, $"{resultsCounter}_summary.png"));

                    output.AppendLine();
                    output.AppendLine("<Gallery cols={2}>");
                    for (int i = 0; i < 4; i++)
                    {
                        output.AppendLine(
                            $"<GalleryImage src={{require('./{example.Options.DocsFileName}/{resultsCounter}_{i}.png').default}} />");
                    }

                    output.AppendLine("</Gallery>");

                    output.AppendLine();
                    output.AppendLine("<div style={{ textAlign: 'center', marginTop: '-15px' }}>");
                    output.AppendLine();
                    output.AppendLine($"*Average time to generate the level: {((times.Average() + initStopwatch.ElapsedMilliseconds) / 1000).ToString("F", CultureInfo.InvariantCulture)}s ({((initStopwatch.ElapsedMilliseconds) / 1000d).ToString("F", CultureInfo.InvariantCulture)}s init, {((times.Average()) / 1000).ToString("F", CultureInfo.InvariantCulture)}s generation itself)*");
                    output.AppendLine();
                    output.AppendLine("</div>");
                    output.AppendLine();

                    resultsCounter++;
                }
                else if (codeBlockHandler.IsInside)
                {
                    output.AppendLine(line);
                }
                else if (!string.IsNullOrEmpty(trimmed))
                {
                    codeBlockHandler.Enter();
                    output.AppendLine(line);
                }
                else
                {
                    output.AppendLine();
                }
            }

            codeBlockHandler.Exit();
        }
Beispiel #8
0
        public void Run()
        {
            #region hidden

            var levelDescription = new BasicsExample().GetLevelDescription();

            #endregion

            //md In this tutorial, we will see how to save and load level descriptions and generated layouts.

            //md ## Level descriptions (JSON)
            //md To save a level description to a JSON file, we can call the [`SaveToJson()`][LevelDescriptionGrid2D.SaveToJson(String, Boolean)] method:

            levelDescription.SaveToJson("levelDescription.json");

            //md And to load a level description from a JSON file, we can use the [`LoadFromJson()`][LevelDescriptionGrid2D.LoadFromJson(String)] method:

            levelDescription = LevelDescriptionGrid2D <int> .LoadFromJson("levelDescription.json");

            //md By default, the JSON serializer is configured to preserve references to objects. That means that when it first encounters an object, it assigns a unique id to id and when it encounters the same object later, it only references that id and does not serialize the object itself. This is good for when we want load the level description back in C# later, but it may cause problems if we want to use the JSON outside C#. Therefore, it is possible to disable this feature:

            levelDescription.SaveToJson("levelDescription.json", preserveReferences: false);

            //md > **Note:**: API reference for the `LevelDescriptionGrid2D` class can be found [here][LevelDescriptionGrid2D].

            //md ## Layouts (JSON)

            #region hidden

            levelDescription = new BasicsExample().GetLevelDescription();
            var generator = new GraphBasedGeneratorGrid2D <int>(levelDescription);
            var layout    = generator.GenerateLayout();

            #endregion

            //md It is possible to save a generated layout to JSON and then load it back:

            layout.SaveToJson("layout.json");
            layout = LayoutGrid2D <int> .LoadFromJson("layout.json");

            //md And it is also possible to disable the `preserveReferences` feature:

            layout.SaveToJson("layout.json", preserveReferences: false);

            //md > **Note:**: API reference for the `LayoutGrid2D` class can be found [here][LayoutGrid2D].

            //md ## Layouts (PNG)

            #region hidden

            layout = generator.GenerateLayout();

            #endregion

            //md It is possible to save a generated layout as a PNG. To do that, we have to create an instance of the `DungeonDrawer` class:

            var dungeonDrawer = new DungeonDrawer <int>();

            //md Then we can save the layout as a PNG:

            dungeonDrawer.DrawLayoutAndSave(layout, "dungeon.png", new DungeonDrawerOptions()
            {
                Width  = 2000,
                Height = 2000,
            });

            //md The dungeon drawer produces images that can be seen in all the examples in this documentation. The API reference of the `DungeonDrawerOptions` class can be found [here][DungeonDrawerOptions].
        }
Beispiel #9
0
        /// <summary>
        /// Run the generator and export the result.
        /// </summary>
        public void Run()
        {
            //md ## Generating the level
            //md To generate the level, we need to create an instance of the `GraphBasedGenerator<TRoom>` class. As we use integers to identify individual rooms, we will substitute the generic type parameter with `int` and pass the level description to the constructor of the generator.

            //md_hide-next
            var levelDescription = GetLevelDescription();

            var generator = new GraphBasedGeneratorGrid2D <int>(levelDescription);

            //md When we have an instance of the generator, we simply call the `GenerateLayout()` method and wait until the generator finds a valid layout based on our level description.

            var layout = generator.GenerateLayout();

            //md The result contains information about all the rooms in the level such as outline of the room or its position.

            //md ## Saving the result
            //md If we want to quickly visualize the result, we can use the `DungeonDrawer<TRoom>` class and export the layout as a PNG image.

            var drawer = new DungeonDrawer <int>();

            drawer.DrawLayoutAndSave(layout, "basics.png", new DungeonDrawerOptions()
            {
                Width  = 2000,
                Height = 2000,
            });

            #region hidden no-clean

            var roomTemplates = levelDescription
                                .GetGraph().Vertices
                                .Select(levelDescription.GetRoomDescription)
                                .Where(x => x.IsCorridor == false)
                                .SelectMany(x => x.RoomTemplates)
                                .Distinct()
                                .ToList();
            var roomTemplatesDrawer = new RoomTemplateDrawer();
            var roomTemplatesBitmap = roomTemplatesDrawer.DrawRoomTemplates(roomTemplates, new DungeonDrawerOptions()
            {
                Width           = 1200,
                Height          = 600,
                PaddingAbsolute = 100,
                FontSize        = 1,
                EnableHatching  = false
            });
            roomTemplatesBitmap.Save(ExamplesGenerator.AssetsFolder + "/room_templates.png");

            var graphDrawer = new GraphDrawer <int>();
            var graphBitmap = graphDrawer.DrawGraph(levelDescription, new Dictionary <int, Vector2Int>()
            {
                { 0, new Vector2Int(0, 0) },
                { 1, new Vector2Int(0, -1) },
                { 2, new Vector2Int(1, -1) },
                { 3, new Vector2Int(1, 0) },
                { 4, new Vector2Int(-1, 0) },
            }, new DungeonDrawerOptions()
            {
                Width           = 2500,
                Height          = 700,
                PaddingAbsolute = 80,
                FontSize        = 3,
            });
            graphBitmap.Save(ExamplesGenerator.AssetsFolder + "/graph.png");

            #endregion
        }