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; })); }
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); }
/// <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(); }