You can capsule the whole data-storage away into a class. It handles all the "book-keeping" and you simply use A to ... and 1 to ... to change the X.
Internally it uses a simple 1-dim list:
class Field:
    def __init__(self, rows, cols, init_piece="x"):
        self.rows = rows
        self.cols = cols
        self.field = [init_piece] * rows * cols
    def place_at(self, row, col, piece):
        """Changes one tile on the field. Does all the reverse-engineering to compute 
        1-dim place of A..?,1..? given tuple of coords."""
        def validation():
            """Raises error when out of bounds."""
            error = []
            if not (isinstance(row,str) and len(row) == 1 and row.isalpha()):
                error.append("Use rows between A and {}".format(chr(ord("A") + 
                                                                    self.rows - 1)))
            if not (0 < col <= self.cols):
                error.append("Use columns between 1 and {}".format(self.cols))
            if error:
                error = ["Invalid row/column: {}/{}".format(row,col)] + error
                raise ValueError('\n- '.join(error))
        validation()
        row = ord(row.upper()[0]) - ord("A")  
        self.field[row * self.cols + col - 1] = piece
    def print_field(self):
        """Prints the playing field."""
        for c in range(self.rows - 1,-1,-1):
            ch = chr(ord("A") + c)
            print("{:<4} ".format(ch), end = "")
            print(("{:>2} " * self.cols).format(*self.field[c * self.cols:
                                                 (c + 1) * self.cols], sep = "  "))
        print("{:<4} ".format(""), end = "")
        print(("{:>2} " * self.cols).format(*range(1,self.cols + 1)))
Then you can use it like so:
rows = 10
cols = 15
f = Field(rows,cols)
f.print_field()
# this uses A...? and 1...? to set things
for r,c in [(0,0),("A",1),("ZZ",99),("A",99),("J",15)]:
    try:
        f.place_at(r,c,"i")  # set to 'i'
    except ValueError as e:
        print(e) 
f.print_field()
Output (before):
J     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
I     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
H     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
G     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
F     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
E     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
D     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
C     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
B     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
A     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
      1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
Output (setting things && after):
Invalid row/column: 0/0
- Use rows between A and J
- Use columns between 1 and 15
Invalid row/column: ZZ/99
- Use rows between A and J
- Use columns between 1 and 15
Invalid row/column: A/99
- Use columns between 1 and 15
J     x  x  x  x  x  x  x  x  x  x  x  x  x  x  i
I     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
H     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
G     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
F     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
E     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
D     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
C     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
B     x  x  x  x  x  x  x  x  x  x  x  x  x  x  x
A     i  x  x  x  x  x  x  x  x  x  x  x  x  x  x
      1  2  3  4  5  6  7  8  9 10 11 12 13 14 15