public static void Generate(ISettableMapView <bool> map, int crawlerChangeDirectionImprovement, int saveDeadEndChance) { // Implemented the logic from http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/ var crawlers = new List <Crawler>(); var empty = FindEmptySquare(map); var randomCounter = 0; var randomSuccess = false; while (empty != null) { Crawler crawler = new Crawler(); crawlers.Add(crawler); crawler.MoveTo(empty); var startedCrawler = true; var percentChangeDirection = 0; //Color randomCrawlerColor = Color.AliceBlue.GetRandomColor(SadConsole.Global.Random); while (crawler.Path.Count != 0) { // Dig this position map[crawler.CurrentPosition] = true; //map[crawler.CurrentPosition.X, crawler.CurrentPosition.Y].Background = randomCrawlerColor; // Get valid directions (basically is any position outside the map or not?) var points = AdjacencyRule.CARDINALS.NeighborsClockwise(crawler.CurrentPosition).ToArray(); var directions = AdjacencyRule.CARDINALS.DirectionsOfNeighborsClockwise(Direction.NONE).ToList(); var valids = new bool[4]; // Rule out any valids based on their position. // Only process NSEW, do not use diagonals for (var i = 0; i < 4; i++) { valids[i] = IsPointWallsExceptSource(map, points[i], directions[i] + 4); } // If not a new crawler, exclude where we came from if (!startedCrawler) { valids[directions.IndexOf(crawler.Facing + 4)] = false; } // Do we have any valid direction to go? if (valids[0] || valids[1] || valids[2] || valids[3]) { var index = 0; // Are we just starting this crawler? OR Is the current crawler facing direction invalid? if (startedCrawler || valids[directions.IndexOf(crawler.Facing)] == false) { // Just get anything index = GetDirectionIndex(valids); crawler.Facing = directions[index]; percentChangeDirection = 0; startedCrawler = false; } else { // Increase probablity we change direction percentChangeDirection += crawlerChangeDirectionImprovement; if (PercentageCheck(percentChangeDirection)) { index = GetDirectionIndex(valids); crawler.Facing = directions[index]; percentChangeDirection = 0; } else { index = directions.IndexOf(crawler.Facing); } } crawler.MoveTo(points[index]); } else { crawler.Backtrack(); } } empty = FindEmptySquare(map); } TrimDeadPaths(map, crawlers, saveDeadEndChance); }
/// <inheritdoc /> protected override IEnumerator <object?> OnPerform(GenerationContext context) { // Validate configuration if (CrawlerChangeDirectionImprovement > 100) { throw new InvalidConfigurationException(this, nameof(CrawlerChangeDirectionImprovement), "The value must be a valid percent (between 0 and 100)."); } // Logic implemented from http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/ // Get or create/add a wall-floor context component var wallFloorContext = context.GetFirstOrNew <ISettableGridView <bool> >( () => new ArrayView <bool>(context.Width, context.Height), WallFloorComponentTag ); // Get or create/add a tunnel list context component var tunnelList = context.GetFirstOrNew( () => new ItemList <Area>(), TunnelsComponentTag ); // Record spaces we've crawled to introduce changes. int spacesCrawled = 0; var crawlers = new List <Crawler>(); var empty = FindEmptySquare(wallFloorContext, RNG); while (empty != Point.None) { var crawler = new Crawler(); crawlers.Add(crawler); crawler.MoveTo(empty); var startedCrawler = true; ushort percentChangeDirection = 0; while (crawler.Path.Count != 0) { // Dig this position wallFloorContext[crawler.CurrentPosition] = true; // Get valid directions (basically is any position outside the map or not? var points = AdjacencyRule.Cardinals.NeighborsClockwise(crawler.CurrentPosition).ToArray(); var directions = AdjacencyRule.Cardinals.DirectionsOfNeighborsClockwise(Direction.None).ToList(); var validDirections = new bool[4]; // Rule out any valid directions based on their position. Only process cardinals, do not use diagonals for (var i = 0; i < 4; i++) { validDirections[i] = IsPointWallsExceptSource(wallFloorContext, points[i], directions[i] + 4); } // If not a new crawler, exclude where we came from if (!startedCrawler) { validDirections[directions.IndexOf(crawler.Facing + 4)] = false; } // Do we have any valid direction to go? if (validDirections[0] || validDirections[1] || validDirections[2] || validDirections[3]) { int index; // Are we just starting this crawler? OR Is the current crawler facing // direction invalid? if (startedCrawler || validDirections[directions.IndexOf(crawler.Facing)] == false) { // Just get anything index = GetDirectionIndex(validDirections, RNG); crawler.Facing = directions[index]; percentChangeDirection = 0; startedCrawler = false; } else { // Increase probability we change direction percentChangeDirection += CrawlerChangeDirectionImprovement; if (RNG.PercentageCheck(percentChangeDirection)) { index = GetDirectionIndex(validDirections, RNG); crawler.Facing = directions[index]; percentChangeDirection = 0; } else { index = directions.IndexOf(crawler.Facing); } } crawler.MoveTo(points[index]); spacesCrawled++; } else { crawler.Backtrack(); spacesCrawled++; } if (spacesCrawled >= 10) { yield return(null); spacesCrawled = 0; } } if (spacesCrawled > 0) { yield return(null); spacesCrawled = 0; } empty = FindEmptySquare(wallFloorContext, RNG); } // Add appropriate items to the tunnels list tunnelList.AddRange(crawlers.Select(c => c.AllPositions).Where(a => a.Count != 0), Name); }
/// <summary> /// Generates a maze in map using crawlers that walk the map carving tunnels. /// </summary> /// <param name="map">The map to modify.</param> /// <param name="rng">The RNG to use.</param> /// <param name="crawlerChangeDirectionImprovement"> /// Out of 100, how much to increase the chance of the crawler changing direction each step. /// Once it changes direction, the chance resets to 0 and increases by this amount. Defaults /// to 10. /// </param> /// <returns>A list of mazes that were generated.</returns> public static IEnumerable <MapArea> Generate(ISettableMapView <bool> map, IGenerator rng, int crawlerChangeDirectionImprovement = 10) { // Implemented the logic from http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/ if (rng == null) { rng = SingletonRandom.DefaultRNG; } var crawlers = new List <Crawler>(); var empty = FindEmptySquare(map, rng); while (empty != Coord.NONE) { Crawler crawler = new Crawler(); crawlers.Add(crawler); crawler.MoveTo(empty); var startedCrawler = true; var percentChangeDirection = 0; while (crawler.Path.Count != 0) { // Dig this position map[crawler.CurrentPosition] = true; // Get valid directions (basically is any position outside the map or not?) var points = AdjacencyRule.CARDINALS.NeighborsClockwise(crawler.CurrentPosition).ToArray(); var directions = AdjacencyRule.CARDINALS.DirectionsOfNeighborsClockwise(Direction.NONE).ToList(); var valids = new bool[4]; // Rule out any valids based on their position. Only process NSEW, do not use diagonals for (var i = 0; i < 4; i++) { valids[i] = IsPointWallsExceptSource(map, points[i], directions[i] + 4); } // If not a new crawler, exclude where we came from if (!startedCrawler) { valids[directions.IndexOf(crawler.Facing + 4)] = false; } // Do we have any valid direction to go? if (valids[0] || valids[1] || valids[2] || valids[3]) { var index = 0; // Are we just starting this crawler? OR Is the current crawler facing // direction invalid? if (startedCrawler || valids[directions.IndexOf(crawler.Facing)] == false) { // Just get anything index = GetDirectionIndex(valids, rng); crawler.Facing = directions[index]; percentChangeDirection = 0; startedCrawler = false; } else { // Increase probablity we change direction percentChangeDirection += crawlerChangeDirectionImprovement; if (PercentageCheck(percentChangeDirection, rng)) { index = GetDirectionIndex(valids, rng); crawler.Facing = directions[index]; percentChangeDirection = 0; } else { index = directions.IndexOf(crawler.Facing); } } crawler.MoveTo(points[index]); } else { crawler.Backtrack(); } } empty = FindEmptySquare(map, rng); } return(crawlers.Select(c => c.AllPositions).Where(a => a.Count != 0)); }