private Direction FindValidConnections(Direction dir, Point loc)
        {
            var list = new List<Direction>();

            var startDir = dir;

            dir = dir.TurnLeft();
            for (var i = 0; i < DirectionHelpers.Directions.Count() - 1; i++)
            {
                var newLoc = dir.GetLocation(loc);

                if (newLoc.X >= 0 && newLoc.X < _cells.GetLength(0) && newLoc.Y >= 0 && newLoc.Y < _cells.GetLength(1))
                {
                    var cell = _cells[newLoc.X, newLoc.Y];
                    if (cell.Type == CellType.None || cell.Openings.Facing(dir))
                    {
                        list.Add(dir);
                    }
                }

                dir = dir.TurnRight();
            }

            var connectsToMake = list.Count > 0 ? _random.Next(1, list.Count + 1) : 0;
            var validConnections = list.Take(connectsToMake).Concat(new[] { startDir.TurnAround() }).ToArray();

            // close the openings in the neighbor cells that we didn't make
            foreach (var connectToUndo in list.Skip(connectsToMake))
            {
                var newLoc = connectToUndo.GetLocation(loc);
                var dirToUndo = connectToUndo.TurnAround();

                _cells[newLoc.X, newLoc.Y].Openings ^= dirToUndo;
            }

            return validConnections.ToDirectionFlag();
        }
        public void Generate(ITileMap map)
        {
            _random = new MersennePrimeRandom(_params.Seed);

            var w = map.Width / CellSize;
            var h = map.Height / CellSize;

            _cells = new Cell[w, h];

            var startLoc = new Point { X = w / 2, Y = h / 2 };
            _cells[startLoc.X, startLoc.Y] = Cell.FourWayRoom();

            if (_params.Exits)
            {
                _cells[startLoc.X, startLoc.Y].Attributes = AttributeType.Entry;
            }

            var unprocessed = new Queue<Point>();
            unprocessed.Enqueue(startLoc);

            while (unprocessed.Count > 0)
            {
                var location = unprocessed.Dequeue();
                var cell = _cells[location.X, location.Y];

                foreach (var opening in cell.Openings.ToDirectionsArray())
                {
                    var newLocation = opening.GetLocation(location);
                    var newCell = DetermineCellType(newLocation, opening);

                    if (newCell.Type != CellType.None)
                    {
                        _cells[newLocation.X, newLocation.Y] = ApplyAttributes(newCell);
                        unprocessed.Enqueue(newLocation);
                    }
                }
            }

            var secondExitPlaced = !_params.Exits;

            var chance = 10;
            for (var x = 0; x < _cells.GetLength(0); x++)
                for (var y = 0; y < _cells.GetLength(1); y++)
                {
                    var cell = _cells[x, y];

                    if (!secondExitPlaced)
                    {
                        var spawnExit = _random.Chance(chance);
                        if (cell.Type != CellType.None && ((x <= w * 0.15) || (x >= w * 0.65)) && spawnExit)
                        {
                            cell.Attributes = AttributeType.Exit;
                            secondExitPlaced = true;
                        }
                        else if (!spawnExit)
                        {
                            chance += (int)(chance * 0.25f);
                        }
                    }

                    cell.Fill(x, y, map, _params);
                }
        }
        // pick a cell type that will connect as many rooms as possible
        private Cell DetermineCellType(Point location, Direction direction)
        {
            var locationInBounds = location.X >= 0 && location.X < _cells.GetLength(0) && location.Y >= 0 && location.Y < _cells.GetLength(0);
            if (locationInBounds && _cells[location.X, location.Y].Type == CellType.None)
                return new Cell
                {
                    Type = _random.Chance(_params.RoomChance) ? CellType.Room : CellType.Corridor,
                    Openings = FindValidConnections(direction, location),
                    Attributes = AttributeType.None,
                };

            return default(Cell);
        }