If you're OK with making it a text based game, you can loop until the sudoku is complete and valid. Print the current board state and ask for a coordinate assignment.
Make sure that the initial numbers cannot be overridden and allow erasing misplaced numbers (e.g. by assigning them a zero)
Here is a nice printing function for the sudoku board that highlights the initial numbers by placing them between parentheses:
def niceSudo(board):
side = len(board)
base = int(side**0.5)
def expandLine(line):
return line[0]+line[5:9].join([line[1:5]*(base-1)]*base)+line[9:]
line0 = " "+expandLine("╔═══╤═══╦═══╗ ")
line1 = "# "+expandLine("║ . │ . ║ . ║ # ")
line2 = " "+expandLine("╟───┼───╫───╢ ")
line3 = " "+expandLine("╠═══╪═══╬═══╣ ")
line4 = " "+expandLine("╚═══╧═══╩═══╝ ")
symbol = " 123456789" if base <= 3 else " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
nums = [ [""]+[f"({symbol[-n]})" if n<0 else f" {symbol[n]} " for n in row]
for row in board ]
coord = " "+"".join(f" {s} " for s in symbol[1:side+1])+" "
lines = []
lines.append(coord)
lines.append(line0)
for r in range(1,side+1):
line1n = line1.replace("#",str(symbol[r]))
lines.append( "".join(n+s for n,s in zip(nums[r-1],line1n.split(" . "))) )
lines.append([line2,line3,line4][(r%side==0)+(r%base==0)])
lines.append(coord)
return lines
def printSudoku(*boards):
print(*(" ".join(ss) for ss in zip(*(niceSudo(b) for b in boards))),sep="\n")
You will also need a function to check if the board is complete and valid:
def sudokuDone(board):
side = len(board)
base = int(len(board)**0.5)
if any( 0 in row for row in board) : return False
filled = set(range(1,10))
board = [ [abs(n) for n in row] for row in board]
if any(set(row) != filled for row in board): return False
if any(set(col) != filled for col in zip(*board)): return False
if any(set(v for row in board[r:r+base] for v in row[c:c+base]) != filled
for r in range(0,side,base) for c in range(0,side,base)):
return False
return True
The whole game would be a single loop that prints, asks for coordinate assignments and checks for completion:
def playSudoku(board):
board = [ [-n for n in row] for row in board ]
while not sudokuDone(board):
printSudoku(board)
command = input("Enter Row,Column=Number: ")
try:
r,c,n = map(int,command.replace("=",",").split(","))
except:
print("Invalid input")
continue
if n not in range(0,10): print("Number must be 0...9");continue
if r not in range(1,10): print("Row must be 1...9");continue
if c not in range(1,10): print("col must be 1...9");continue
if board[r-1][c-1]<0: print("Cannot change initial numbers");continue
board[r-1][c-1] = n
printSudoku(board)
print("Success!!")
Note that, although the printing and validation functions can handle sudokus up to 36x36, I only implemented the 9x9 in this playSudoku function
Test run:
test = [ [8,0,0, 0,0,0, 0,0,0],
[0,0,3, 6,0,0, 0,0,0],
[0,7,0, 0,9,0, 2,0,0],
[0,5,0, 0,0,7, 0,0,0],
[0,0,0, 0,4,5, 6,0,0],
[0,0,0, 1,0,0, 0,3,0],
[0,0,1, 0,0,0, 0,6,8],
[0,0,8, 5,0,0, 0,1,0],
[0,9,0, 0,0,0, 4,0,0]
]
playSudoku(test)
...
1 2 3 4 5 6 7 8 9
╔═══╤═══╤═══╦═══╤═══╤═══╦═══╤═══╤═══╗
1 ║(8)│ │ ║ │ │ ║ │ │ ║ 1
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
2 ║ │ │(3)║(6)│ │ ║ │ │ ║ 2
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
3 ║ │(7)│ ║ │(9)│ ║(2)│ │ ║ 3
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
4 ║ │(5)│ ║ │ │(7)║ │ │ ║ 4
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
5 ║ │ │ ║ │(4)│(5)║(6)│ │ ║ 5
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
6 ║ │ │ ║(1)│ │ ║ │(3)│ ║ 6
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
7 ║ │ │(1)║ │ │ ║ │(6)│(8)║ 7
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
8 ║ │ │(8)║(5)│ │ ║ │(1)│ ║ 8
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
9 ║ │(9)│ ║ │ │ ║(4)│ │ ║ 9
╚═══╧═══╧═══╩═══╧═══╧═══╩═══╧═══╧═══╝
1 2 3 4 5 6 7 8 9
Enter Row,Column=Number: 5,4=2
1 2 3 4 5 6 7 8 9
╔═══╤═══╤═══╦═══╤═══╤═══╦═══╤═══╤═══╗
1 ║(8)│ │ ║ │ │ ║ │ │ ║ 1
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
2 ║ │ │(3)║(6)│ │ ║ │ │ ║ 2
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
3 ║ │(7)│ ║ │(9)│ ║(2)│ │ ║ 3
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
4 ║ │(5)│ ║ │ │(7)║ │ │ ║ 4
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
5 ║ │ │ ║ 2 │(4)│(5)║(6)│ │ ║ 5
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
6 ║ │ │ ║(1)│ │ ║ │(3)│ ║ 6
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
7 ║ │ │(1)║ │ │ ║ │(6)│(8)║ 7
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
8 ║ │ │(8)║(5)│ │ ║ │(1)│ ║ 8
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
9 ║ │(9)│ ║ │ │ ║(4)│ │ ║ 9
╚═══╧═══╧═══╩═══╧═══╧═══╩═══╧═══╧═══╝
1 2 3 4 5 6 7 8 9
Enter Row,Column=Number:1,2=1
...
1 2 3 4 5 6 7 8 9
╔═══╤═══╤═══╦═══╤═══╤═══╦═══╤═══╤═══╗
1 ║(8)│ 1 │ 2 ║ 7 │ 5 │ 4 ║ 3 │ 9 │ 6 ║ 1
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
2 ║ 9 │ 4 │(3)║(6)│ 8 │ 2 ║ 1 │ 5 │ 7 ║ 2
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
3 ║ 6 │(7)│ 5 ║ 3 │(9)│ 1 ║(2)│ 8 │ 4 ║ 3
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
4 ║ 1 │(5)│ 6 ║ 9 │ 3 │(7)║ 8 │ 4 │ 2 ║ 4
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
5 ║ 3 │ 8 │ 9 ║ 2 │(4)│(5)║(6)│ 7 │ 1 ║ 5
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
6 ║ 7 │ 2 │ 4 ║(1)│ 6 │ 8 ║ 9 │(3)│ 5 ║ 6
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
7 ║ 2 │ 3 │(1)║ 4 │ 7 │ 9 ║ 5 │(6)│(8)║ 7
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
8 ║ 4 │ 6 │(8)║(5)│ 2 │ 3 ║ 7 │(1)│ 9 ║ 8
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
9 ║ 5 │(9)│ 7 ║ 8 │ 1 │ 6 ║(4)│ 2 │ ║ 9
╚═══╧═══╧═══╩═══╧═══╧═══╩═══╧═══╧═══╝
1 2 3 4 5 6 7 8 9
Enter Row,Column=Number: 9,9=3
1 2 3 4 5 6 7 8 9
╔═══╤═══╤═══╦═══╤═══╤═══╦═══╤═══╤═══╗
1 ║(8)│ 1 │ 2 ║ 7 │ 5 │ 4 ║ 3 │ 9 │ 6 ║ 1
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
2 ║ 9 │ 4 │(3)║(6)│ 8 │ 2 ║ 1 │ 5 │ 7 ║ 2
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
3 ║ 6 │(7)│ 5 ║ 3 │(9)│ 1 ║(2)│ 8 │ 4 ║ 3
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
4 ║ 1 │(5)│ 6 ║ 9 │ 3 │(7)║ 8 │ 4 │ 2 ║ 4
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
5 ║ 3 │ 8 │ 9 ║ 2 │(4)│(5)║(6)│ 7 │ 1 ║ 5
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
6 ║ 7 │ 2 │ 4 ║(1)│ 6 │ 8 ║ 9 │(3)│ 5 ║ 6
╠═══╪═══╪═══╬═══╪═══╪═══╬═══╪═══╪═══╣
7 ║ 2 │ 3 │(1)║ 4 │ 7 │ 9 ║ 5 │(6)│(8)║ 7
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
8 ║ 4 │ 6 │(8)║(5)│ 2 │ 3 ║ 7 │(1)│ 9 ║ 8
╟───┼───┼───╫───┼───┼───╫───┼───┼───╢
9 ║ 5 │(9)│ 7 ║ 8 │ 1 │ 6 ║(4)│ 2 │ 3 ║ 9
╚═══╧═══╧═══╩═══╧═══╧═══╩═══╧═══╧═══╝
1 2 3 4 5 6 7 8 9
Success!!