Beispiel #1
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 #2
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);
        }
Beispiel #3
0
        /// <summary>
        /// Run the generator.
        /// </summary>
        private void Run()
        {
            cancellationTokenSource = new CancellationTokenSource();
            var ct = cancellationTokenSource.Token;

            task = Task.Run(() =>
            {
                try
                {
                    dumpFolder          = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds().ToString();
                    dumpCount           = 0;
                    var layoutGenerator = settings.LayoutGenerator;

                    if (layoutGenerator == null)
                    {
                        var defaultGenerator = new GraphBasedGeneratorGrid2D <int>(settings.LevelDescription);
                        defaultGenerator.InjectRandomGenerator(new Random(settings.RandomGeneratorSeed));
                        layoutGenerator = defaultGenerator;
                    }

                    // Set cancellation token
                    if (layoutGenerator is ICancellable cancellable)
                    {
                        cancellable.SetCancellationToken(ct);
                    }

                    infoStopwatch.Start();

                    // Register handler that shows generated layouts OnValid
                    layoutGenerator.OnValid += layout =>
                    {
                        if (!showFinalLayouts.Checked)
                        {
                            return;
                        }

                        lastEvent    = GeneratorEvent.OnValid;
                        layoutToDraw = layout;
                        mainPictureBox.BeginInvoke((Action)(() => mainPictureBox.Refresh()));
                        SleepWithFastCancellation((int)showFinalLayoutsTime.Value, ct);
                    };

                    // Register handler that shows generated layouts OnPartialValid
                    layoutGenerator.OnPartialValid += layout =>
                    {
                        if (!showPartialValidLayouts.Checked)
                        {
                            return;
                        }

                        lastEvent    = GeneratorEvent.OnPartialValid;
                        layoutToDraw = layout;
                        mainPictureBox.BeginInvoke((Action)(() => mainPictureBox.Refresh()));
                        SleepWithFastCancellation((int)showAcceptedLayoutsTime.Value, ct);
                    };

                    // Register handler that shows generated layouts OnPerturbed
                    layoutGenerator.OnPerturbed += layout =>
                    {
                        if (!showPerturbedLayouts.Checked)
                        {
                            return;
                        }

                        lastEvent    = GeneratorEvent.OnPerturbed;
                        layoutToDraw = layout;
                        mainPictureBox.BeginInvoke((Action)(() => mainPictureBox.Refresh()));
                        SleepWithFastCancellation((int)showPerturbedLayoutsTime.Value, ct);
                    };

                    // Register handler that counts iteration count
                    layoutGenerator.OnPerturbed += layout =>
                    {
                        lastEvent = GeneratorEvent.OnPerturbed;
                        iterationsCount++;
                        if (infoStopwatch.ElapsedMilliseconds >= 200)
                        {
                            BeginInvoke((Action)(UpdateInfoPanel));
                            infoStopwatch.Restart();
                        }
                    };

                    // Register handler that resets iteration count
                    layoutGenerator.OnValid += layout =>
                    {
                        lastEvent       = GeneratorEvent.OnValid;
                        iterationsCount = 0;
                        layoutsCount++;
                        BeginInvoke((Action)(UpdateInfoPanel));
                        infoStopwatch.Restart();
                    };

                    generatedLayouts = new List <LayoutGrid2D <int> >()
                    {
                    };

                    for (int i = 0; i < settings.NumberOfLayouts; i++)
                    {
                        generatedLayouts.Add(layoutGenerator.GenerateLayout());
                    }

                    isRunning = false;
                    BeginInvoke((Action)(UpdateInfoPanel));
                    BeginInvoke((Action)(OnFinished));
                }
                catch (Exception e)
                {
                    ShowExceptionAndClose(e);
                }
            }, ct);
        }
        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();
        }