Beispiel #1
0
        protected override void _runAlgorithm(IAlgorithmContext context)
        {
            DungeonTiles workingTiles = context.D.Tiles;

            // Implemented via http://weblog.jamisbuck.org/2015/1/15/better-recursive-division-algorithm

            if (this.WallStrategy != WallFormation.Boundaries)
            {
                throw new NotImplementedException();
            }

            // CLOBBER!
            workingTiles.SetAllToo(Tile.MoveType.Open_HORIZ, context.Mask);

            Stack <Subregion> subregions = new Stack <Subregion>();

            // 1. Collect all the cells in the maze into a single region.
            Subregion topRegion = new Subregion(workingTiles.GetTilesIn(context.Mask));

            subregions.Push(topRegion);

            while (subregions.Count > 0)
            {
                Subregion parentRegion = subregions.Pop();

                int roomSize = this.RoomSize - (this.RoomSizeVariability / 2) + context.R.Next(this.RoomSizeVariability);
                roomSize = roomSize < 1 ? 1 : roomSize;

                if (parentRegion.Tiles.Count <= roomSize)
                {
                    if (roomSize > 1)
                    {
                        if (this.GroupRooms)
                        {
                            workingTiles.Parent.CreateGroup(parentRegion.Tiles.ToHashSet(), TileCategory.Room);
                        }
                    }
                    continue;
                }

                // 2. Split the region into two, using the following process:
                List <Tile> S = new List <Tile>();
                Dictionary <Tile, Subregion_Split> tileSubregions = new Dictionary <Tile, Subregion_Split>();
                foreach (Tile t in parentRegion.Tiles)
                {
                    tileSubregions[t] = Subregion_Split.NONE;
                }

                //   2.1  Choose two cells from the region at random as “seeds”.
                //        Identify one as subregion A and one as subregion B.
                //        Then put them into a set S.
                Tile randomSeed_A = parentRegion.Tiles.PickRandomly(context.R);
                Tile randomSeed_B = randomSeed_A; // Shouldn't be equal when we're done
                while (randomSeed_A == randomSeed_B)
                {
                    randomSeed_B = parentRegion.Tiles.PickRandomly(context.R);
                }
                tileSubregions[randomSeed_A] = Subregion_Split.A;
                tileSubregions[randomSeed_B] = Subregion_Split.B;
                S.Add(randomSeed_A);
                S.Add(randomSeed_B);

                while (S.Count > 0)
                {
                    //   2.2  Choose a cell at random from S. Remove it from the set.
                    Tile currentTile = S.PullRandomly(context.R);

                    //   2.3  For each of that cell’s neighbors, if the neighbor
                    //        is not already associated with a subregion, add it to S,
                    //        and associate it with the same subregion as the cell itself.
                    IList <Tile> nextAdjacents = currentTile
                                                 .GetAdjacents()
                                                 .Where(t => parentRegion.Tiles.Contains(t))
                                                 .ToList();
                    foreach (Tile t in nextAdjacents)
                    {
                        if (tileSubregions[t] == Subregion_Split.NONE)
                        {
                            S.Add(t);
                            tileSubregions[t] = tileSubregions[currentTile];
                        }
                    }
                } //   2.4  Repeat 2.2 and 2.3 until the entire region has been split into two.

                // 3. Construct a wall between the two regions by identifying cells
                //    in one region that have neighbors in the other region. Leave a
                //    gap by omitting the wall from one such cell pair.
                List <Tuple <Tile, Tile> > wallBoundaries = new List <Tuple <Tile, Tile> >();

                wallBoundaries.AddRange(
                    tileSubregions.Keys
                    // Safe to filter with hard-coded A/B due to associative property
                    .Where(k => tileSubregions[k] == Subregion_Split.A)
                    .SelectMany <Tile, Tuple <Tile, Tile> >(
                        t => t.GetAdjacents()
                        .Where(adj => parentRegion.Tiles.Contains(adj))
                        .Where(sr => tileSubregions[sr] == Subregion_Split.B)
                        .Select(t2 => new Tuple <Tile, Tile>(t, t2))));

                // Leave as many gaps opened as requested
                int maxGaps = Math.Min(
                    this.GapCount,
                    (int)Math.Ceiling(wallBoundaries.Count * MaxGapProportion));
                for (int i = 0; i < maxGaps; ++i)
                {
                    wallBoundaries.PullRandomly(context.R);
                }

                foreach (Tuple <Tile, Tile> pair in wallBoundaries)
                {
                    Tile.MoveType touchingBoundary = workingTiles.GetCardinality(pair.Item1, pair.Item2);
                    pair.Item1.Physics = pair.Item1.Physics.CloseOff(touchingBoundary);
                }

                ISet <Tile> subregion_A = parentRegion.Tiles.Where(t => tileSubregions[t] == Subregion_Split.A).ToHashSet();
                ISet <Tile> subregion_B = parentRegion.Tiles.Where(t => tileSubregions[t] == Subregion_Split.B).ToHashSet();

                if (this.GroupForDebug)
                {
                    workingTiles.Parent.CreateGroup(subregion_A);
                    workingTiles.Parent.CreateGroup(subregion_B);
                }

                this.RunCallbacks(context);

                // Push new subregions to the stack
                subregions.Push(new Subregion(subregion_A));
                subregions.Push(new Subregion(subregion_B));
            } // 4. Repeat 2 and 3 for each subregion
        }