/// <summary>
        /// Generates a map on a 2D array of TileType.
        /// The map varies in size and length and may include a mix of any of the following:
        /// split pathing, tower plots, and decoration.
        /// </summary>
        /// <param name="size">Size of the map</param>
        /// <param name="length">Length of the path</param>
        /// <param name="splitPath">Whether to add split pathing or not</param>
        /// <param name="towerPercent">Percent of empty spaces adjacent to path tiles for tower plots</param>
        /// <param name="decorPercent">Percent of empty space in map for decoration tiles</param>
        /// <returns>A 2D array of TileType</returns>
        public static TileType[,] GenerateMap(MapSize size, MapLength length, bool splitPath, float towerPercent, float decorPercent)
        {
            StartUp();
            TowerPercent = towerPercent;
            DecorPercent = decorPercent;
            bool goodMap = false;
            bool goodSplitPath = true;
            int startEndDistance = 0;
            int numOfTowers = 0;

            // Generate the map
            do
            {
                // initialize grid
                InitializeGrid();
                // create path according to desired length
                goodMap = MakePath(size, length);
                // check the start and end point distance
                startEndDistance = StartEndDistance();
                // if split pathing is desired
                if (splitPath)
                    goodSplitPath = GenerateSplitPath();
            } while (!goodMap || !goodSplitPath || startEndDistance < (int)size / 2);

            // calculate number of desired towers
            numOfTowers = CalculateTowerAmount(TowerPercent);
            // check if user wants more than 90% of empty space
            if (TowerPercent > TOO_MANY_TILES) PlaceTowerSequentially();
            // else random placement
            else
                while (TowerCount() < numOfTowers) SingleTower();

            // Place decorative objects
            // check if user wants more than 90% of empty space
            if (DecorPercent > TOO_MANY_TILES) PlaceDecorSequentially();
            else PlaceDecor2(CalculateDecorAmount(DecorPercent));

            return GetMapAsTileType();
        }
        public void Enforce()
        {
            if (this.SeedType == SeedType.Random)
            {
                this.Seed = "";
                var r = new Random();
                for (int i = 0; i < 6; i++)
                {
                    var val = r.Next(36);
                    if (val < 10)
                    {
                        this.Seed += ((char)('0' + val)).ToString();
                    }
                    else
                    {
                        this.Seed += ((char)('a' + val - 10)).ToString();
                    }
                }
            }
            switch (this.Rules)
            {
            case Ruleset.A:
                this.SetNormalMaps();
                this.RepeatRooms  = false;
                this.EnterUnknown = false;
                this.Algorithm    = LogicType.Pathway;
                this.Length       = MapLength.Short;
                this.Dashes       = NumDashes.One;
                this.Difficulty   = Difficulty.Normal;
                this.Lights       = ShineLights.Hubs;
                this.Darkness     = Darkness.Never;
                break;

            case Ruleset.B:
                this.SetNormalMaps();
                this.RepeatRooms  = false;
                this.EnterUnknown = false;
                this.Algorithm    = LogicType.Pathway;
                this.Length       = MapLength.Medium;
                this.Dashes       = NumDashes.Two;
                this.Difficulty   = Difficulty.Normal;
                this.Lights       = ShineLights.Hubs;
                this.Darkness     = Darkness.Never;
                break;

            case Ruleset.C:
                this.SetNormalMaps();
                this.RepeatRooms  = false;
                this.EnterUnknown = false;
                this.Algorithm    = LogicType.Pathway;
                this.Length       = MapLength.Medium;
                this.Dashes       = NumDashes.One;
                this.Difficulty   = Difficulty.Expert;
                this.Lights       = ShineLights.Hubs;
                this.Darkness     = Darkness.Vanilla;
                break;

            case Ruleset.D:
                this.SetNormalMaps();
                this.RepeatRooms  = false;
                this.EnterUnknown = false;
                this.Algorithm    = LogicType.Pathway;
                this.Length       = MapLength.Long;
                this.Dashes       = NumDashes.Two;
                this.Difficulty   = Difficulty.Expert;
                this.Lights       = ShineLights.Hubs;
                this.Darkness     = Darkness.Vanilla;
                break;
            }
        }
        /// <summary>
        /// Calls the method to generate tile path.
        /// Parameters for map generation are dependant upon map size and path length desired
        /// </summary>
        /// <param name="size">Desired map size</param>
        /// <param name="length">Desired path length</param>
        /// <returns>true or false, whether a good map was generated</returns>
        private static bool MakePath(MapSize size, MapLength length)
        {
            bool map = false;

            // check for map size
            switch (size)
            {
                // small
                case MapSize.Small:
                    // check length
                    switch (length)
                    {
                        // short
                        case MapLength.Short:
                            map = RandomMapGenerator(10, 20, 20);
                            break;
                        // medium
                        case MapLength.Medium:
                            map = RandomMapGenerator(20, 35, 20);
                            break;
                        // long
                        case MapLength.Long:
                            map = RandomMapGenerator(30, 50, 20);
                            break;
                        default:
                            break;
                    }
                    break;
                // medium
                case MapSize.Medium:
                    switch (length)
                    {
                        case MapLength.Short:
                            map = RandomMapGenerator(20, 24, 20);
                            break;
                        case MapLength.Medium:
                            map = RandomMapGenerator(35, 50, 20);
                            break;
                        case MapLength.Long:
                            map = RandomMapGenerator(50, 75, 20);
                            break;
                        default:
                            break;
                    }
                    break;
                // large
                case MapSize.Large:
                    switch (length)
                    {
                        case MapLength.Short:
                            map = RandomMapGenerator(25, 35, 20);
                            break;
                        case MapLength.Medium:
                            map = RandomMapGenerator(50, 70, 20);
                            break;
                        case MapLength.Long:
                            map = RandomMapGenerator(75, 100, 20);
                            break;
                        default:
                            break;
                    }
                    break;
            }
            return map;
        }