If constructing an object is entagled with different cases and conditions it's better to use factory.
That's not the case here.

Instead of this.
```python
def make_board(mines, size):
    '''function that uses my created Board methods to create the playing board'''
    board = Board(tuple([tuple([Cell(True,True,True) for i in range(size+1)]) for j in range(size+1)]))
    open_pos = list(range(size-1)*(size-1))
    for i in range(mines):
        new_pos = random.choice(open_pos) #randomly select from open positions
        open_pos.remove(new_pos) # take that new position out of the open one
        (row_coord,col_coord) = (new_pos % 9, new_pos // 9) # mod and floor div
        board.put_mine(row_coord,col_coord) #put the mine in the new random location
    return board
```
You can do this. Notice also how I've transformed your code from imperative into declarative and used `prefer composition over inheritance` by creating `self.cells`.

```python

def chunks(sequence, n):
    iters = (iter(iterable),) * n
    return zip(*iters)

class Board:
    '''class that creates the playing board'''

    def __init__(self, mines_count, height, width): #initializing
        size = height * width
        mines = chain(repeat(True, mines_count), repeat(False, size - mines_count))
        mines = rnd.shuffle(mines)
        cells = (Cell(is_mine) for is_mine in mines)
        self.cells = chunks(cells, width)
        self.playing = True
```

Use default values in initializer of Cell
```python

class Cell(object):
    """a class to deal with individual cells"""
    def __init__(self, can_see, flagged, is_mine): #initializing Cell class
        self.can_see = not can_see
        self.flagged = not flagged
        self.is_mine = is_mine

class Cell(object):
    '''a class to deal with individual cells'''
    def __init__(self, is_mine, visible=False, flagged=False): #initializing Cell class
        self.visible = visible
        self.flagged = flagged
        self.is_mine = is_mine
```

And `is_solved` can be expressed much shortly and more readable

```python
    def is_solved(self):
        for row in self:
            for cell in row:
                if not(cell.can_see or cell.flagged):
                    return False
        return True
```

```python
def is_solved(self):
        return all(c.visible and c.flagged for c in chain.from_iterable(self.cells))
```

No need for parenthesis in sequence unpacking and in conditionals too.
```python
for col_coord, cell in enumerate(row):
...
if cell.is_mine and not cell.flagged:

```

You're not finding neighbours they just are. Using `iterools`

```python
def find_neighbors(self, row_coord,col_coord):
    surr = ((-1,-1), (-1,0), (-1,1), (0,-1), (0,1), (1,-1), (1,0), (1,1))
    y = ((row_coord + surr_row, col_coord + surr_col) for (surr_row,surr_col) in surr)
    return y
    
def neighbors(self, row, col):
    return (
        self.cells[row + x][col + y]
        for x, y in product((-1, 0, 1), repeat=2)
        if (x, y) != (0, 0)
        and 0 <= row + x <= self.width
        and 0 <= col + y <= self.height
    ) 
```

And now this

```python
    def count_surroundings(self,row_coord,col_coord):
        count = 0
        for (surr_row,surr_col) in self.find_neighbors(row_coord,col_coord):
            if (self.is_in_range(surr_row,surr_col) and self[surr_row][surr_col].is_mine):
                count += 1
        return count
```

Becomes oneliners 

```python
    def surrounding_mines_count(self,row_coord,col_coord):
        return sum(cell.is_mine for cell in self.neighbors(row, col))
        
    def mines_left(self):
        return sum(c.is_mine and not c.flagged for c in chain.from_iterable(self.cells))


```