1

Related to this question, I'd like to convert between two systems of referencing a cell in a Sudoku grid.

Often, the 3x3 'blocks' in the grid are numbered 1–9 (usually in reading order), with the cells in each block numbered 1–9 similarly. This is in contrast to the more common method of row (usually top-to-bottom) and column (left-to-right). This allows for a cell to be referenced either by (row,column) or (block,cell).

With 0-indexing of the aforementioned elements for simplicity, this conversion can be computed as follows:

def rc2bc(r,c):
    return (3*(r//3)+c//3,3*(r%3)+c%3)

def bc2rc(b,c):
    return (3*(b//3)+c//3,3*(b%3)+c%3)

Thus, we have

>>> rc2bc(3,5)
(4,2)
>>> bc2rc(4,2)
(3,5)

Note that this transformation is symmetric: the same computation is performed on the inputs in both functions.

I have various (NumPy) arrays corresponding to the state data of a given Sudoku grid, typically of the form:

states_rc[row,col,...]

but sometimes of the form:

states_bc[block,cell,...]

I would like to be able to transform the first two dimensions of these arrays, potentially obviating the latter in favour of the former, using said transformation where needed.

For example, one such array, cell_digits, contains boolean data encoding possible digits in a cell, where cell_digits[row,col,digit] is True if (also-0-indexed) digit 'might' be in cell (row,col), and False if it has been ruled out. In order to perform an update of this state, one might do the following:

cell_digits[row,col,digit] = False # digit cannot be placed in cell (row,col)

However, it is more likely that one would perform the operation on a whole column or row at once, as per the rules of Sudoku. Ignoring the exclusion of the cell responsible for the change, again for simplicity, this would then be:

cell_digits[:,col,digit] = False # digit can no longer be placed in this row
cell_digits[row,:,digit] = False # Likewise for column

Where grid[row,col] = digit in the solution so far. However, should one wish to update cells in the same block accordingly, the operation becomes more complicated. Possible solutions include using a boolean mask and performing a slice to select cells of the relevant block. However, neither of these translate to vectorised operations attempting to apply many such updates, with multiple cell-digit pairs at once (nor do they play well with attempts to exclude the 'origin' cell from the update). Much preferred would be the ability to do the following:

cell_digits[block,:,digit] = False # digit can no longer be placed in this block

using (block,cell) coordinates instead. However, this obviously requires converting cell_digits from (row,column) to (block,cell) coordinates in some fashion. Creating a copy wouldn't be ideal, since the changes would need to persist to the next update (which would include ideally (row,column)-style operations), likely leading to another copy upon reversing the transformation, so it would be preferable to transform the coordinates (i.e. indices) instead, so that the operation can be performed on the same array in both cases. For one digit, using the conversion function defined previously, this is as simple as:

cell_digits[*bc2rc(block,cell),digit] = False # digit can no longer be placed in cell (block,cell)

This even works with attempts to select a whole block (with cell = np.arange(9)), but this method doesn't work well with advanced indexing (such as attempting to select multiple blocks at once, or perform the operation on multiple digits at once, each in different blocks).

Can something like the above be done using advanced indexing, preferably in such a way that it allows normal indexing of the remaining dimensions?

Alternatively, can an array in the former format ([row,col,...]) be reshaped, reordered, or otherwise manipulated into the latter ([block,cell,...]) without producing a copy?

Fie
  • 121
  • 7
  • One possible solution I've found—I'm not sure yet if this fills all my use cases—is to use `rc2bc(*np.where(mask))`. Multiple instances can't be concatenated easily, but multiple input boolean masks can. – Fie Oct 26 '22 at 00:28

0 Answers0