public Maze Build()
    {
        Maze maze = new Maze(mazeWidth, mazeHeight);

        // Build a full spanning tree maze first
        System.Random random = new System.Random();

        ISet <CellPosition> visitedCells = new HashSet <CellPosition>();
        List <WallPosition> walls        = new List <WallPosition>();

        CellPosition p = new CellPosition(random.Next() % mazeWidth, random.Next() % mazeHeight);

        foreach (CellPosition neighbour in p.Neighbours())
        {
            if (!IsValidCellPosition(maze, neighbour))
            {
                continue;
            }
            walls.Add(new WallPosition(p, neighbour));
        }
        visitedCells.Add(p);

        while (walls.Count > 0)
        {
            int          wallIdx = random.Next() % walls.Count;
            WallPosition wall    = walls[wallIdx];
            walls.RemoveAt(wallIdx);

            if (visitedCells.Contains(wall.next))
            {
                continue;
            }

            if (wall.cell.x == wall.next.x)
            {
                maze.cells[wall.cell.x, Math.Min(wall.cell.y, wall.next.y)] |= Maze.Passage.Top;
            }
            else
            {
                maze.cells[Math.Min(wall.cell.x, wall.next.x), wall.cell.y] |= Maze.Passage.Right;
            }

            p = wall.next;
            foreach (CellPosition neighbour in p.Neighbours())
            {
                if (!IsValidCellPosition(maze, neighbour))
                {
                    continue;
                }
                walls.Add(new WallPosition(p, neighbour));
            }
            visitedCells.Add(p);
        }

        // Then reduce number of walls to wallCount
        List <WallPosition> leftoverWalls = new List <WallPosition>();

        for (int x = 0; x < maze.width; x++)
        {
            for (int y = 0; y < maze.height; y++)
            {
                if (x < maze.width - 1 && ((maze.cells[x, y] & Maze.Passage.Right) == Maze.Passage.None))
                {
                    leftoverWalls.Add(new WallPosition(new CellPosition(x, y), new CellPosition(x + 1, y)));
                }
                if (y < maze.height - 1 && ((maze.cells[x, y] & Maze.Passage.Top) == Maze.Passage.None))
                {
                    leftoverWalls.Add(new WallPosition(new CellPosition(x, y), new CellPosition(x, y + 1)));
                }
            }
        }
        while (leftoverWalls.Count > wallCount)
        {
            int          wallIdx = random.Next() % leftoverWalls.Count;
            WallPosition wall    = leftoverWalls[wallIdx];

            if (wall.cell.x == wall.next.x)
            {
                maze.cells[wall.cell.x, Math.Min(wall.cell.y, wall.next.y)] |= Maze.Passage.Top;
            }
            else
            {
                maze.cells[Math.Min(wall.cell.x, wall.next.x), wall.cell.y] |= Maze.Passage.Right;
            }

            leftoverWalls.RemoveAt(wallIdx);
        }

        return(maze);
    }