You don't need to create all those dictionaries. You already have your coordinates, just don't lock them up in strings:
boxes = []
for i in range(1, 10):
for x in range(1,10):
boxes.append((i, x))
would create a list of (row, column) tuples instead, and you then wouldn't have to map them back.
Even if you needed to associate strings with data, you could do so in a nested dictionary:
coordinates = {
'r1c1': {'row': 1, 'column': 1},
# ...
}
but you could also parse that string and extract the numbers after r and c to produce the row and column numbers again.
In fact, I wrote a Sudoku checker on the same principles once; in the following code block_indices, per9() and zip(*per9(s)) produce indices for each block, row or column of a puzzle, letting you verify that you have 9 unique values in each. The only difference is that instead of a matrix, I used one long list to represent a puzzle, all elements from row to row included in sequence:
from itertools import product
block_indices = [[x + y + s for s in (0, 1, 2, 9, 10, 11, 18, 19, 20)]
for x, y in product(range(0, 81, 27), range(0, 9, 3))]
def per9(iterable):
# group iterable in chunks of 9
return zip(*([iter(iterable)] * 9))
def is_valid_sudoku(s):
return (
# rows
all(len(set(r)) == 9 for r in per9(s)) and
# columns
all(len(set(c)) == 9 for c in zip(*per9(s))) and
# blocks
all(len(set(s[i] for i in ix)) == 9 for ix in block_indices)
)
So row 1, column 4 is 1 * 9 + 4 = index 13 in a flat list.
boxes = boxes + ['r'+str(i)+'c'+str(x)]is a somewhat expensive way of spellingboxes.append('r'+str(i)+'c'+str(x))or evenboxes.append('r{}c{}'.fromat(i, c)).(i, c)(a tuple) in your list. That way you don't have to map back to the row, column format at all, you'd already have the coordinates. Don't use strings as intermediaries.