I am trying to create a simple Sudoku game using tkinter
I am using the sudoku library, it generates the puzzle in the form
puzzle = [[3, None, None, None, 2, None, None, None, None], [None, None, None, None, None, None, 7, None, 6], [2, 6, 7, None, None, None, None, None, None], [None, 9, None, None, None, None, None, 1, None], [None, None, None, None, None, None, None, None, None], [None, None, None, None, 4, None, None, None, None], [1, None, None, None, None, None, None, 9, 2], [None, None, None, None, 6, None, None,
None, 1], [4, None, None, None, None, 1, None, None, None]]
for example
I am trying to make my code edit the cells that had an initial value of None only, and prevent their editting otherwise.
You can try the full code to grasp what do I mean:
from sudoku import Sudoku
from tkinter import *
root = Tk()
root.title('Sudoku')
root.geometry('378x378')
root.resizable(0, 0)
canvas = Canvas(root)
canvas.pack(fill=BOTH, expand=True)
def draw_board():
canvas.create_line(42, 0, 42, 378, width=2, fill='lightgray')
canvas.create_line(84, 0, 84, 378, width=2, fill='lightgray')
canvas.create_line(168, 0, 168, 378, width=2, fill='lightgray')
canvas.create_line(210, 0, 210, 378, width=2, fill='lightgray')
canvas.create_line(294, 0, 294, 378, width=2, fill='lightgray')
canvas.create_line(336, 0, 336, 378, width=2, fill='lightgray')
canvas.create_line(0, 42, 378, 42, width=2, fill='lightgray')
canvas.create_line(0, 84, 378, 84, width=2, fill='lightgray')
canvas.create_line(0, 168, 378, 168, width=2, fill='lightgray')
canvas.create_line(0, 210, 378, 210, width=2, fill='lightgray')
canvas.create_line(0, 294, 378, 294, width=2, fill='lightgray')
canvas.create_line(0, 336, 378, 336, width=2, fill='lightgray')
canvas.create_line(126, 0, 126, 378, width=4, fill='black')
canvas.create_line(252, 0, 252, 378, width=4, fill='black')
canvas.create_line(0, 126, 378, 126, width=4, fill='black')
canvas.create_line(0, 252, 378, 252, width=4, fill='black')
puzzle_cells = []
def draw_nums():
global x_pos, y_pos, puzzle, cell_values
for y_cord, row in enumerate(puzzle):
for x_cord, num in enumerate(row):
x_pos = 20 + x_cord * 42
y_pos = 20 + y_cord * 42
cell = chr(ord('a') + x_cord) + str(y_cord + 1)
if num is None:
# Only draw numbers for empty cells
cell_id = canvas.create_text(x_pos, y_pos, text="", fill='gray', font=('Arial', 35, 'bold'), tags=(f"text_{cell}"))
puzzle_cells.append(cell_id)
else:
# Don't allow editing filled cells
cell_id = canvas.create_text(x_pos, y_pos, text=str(num), fill='gray', font=('Arial', 35, 'bold'), tags=(f"text_{cell}", "uneditable"))
cell_values[cell] = num
puzzle = Sudoku(3).difficulty(0.8)
puzzle = puzzle.board
x_pos = 20
y_pos = 20
cell_values = {}
def draw_ans(event):
global x_pos, y_pos, puzzle, cell_values, puzzle_cells
cell = detect_pos(event)
if cell:
cell_id = canvas.find_withtag(f"text_{cell}")
# Only allow adding numbers to empty cells in the puzzle and not in puzzle_cells
if cell_values[cell] is None and not cell_id in puzzle_cells:
new_value = 1 # Start with 1 for empty cells
else:
if cell_id not in puzzle_cells:
new_value = (cell_values[cell] % 9) + 1
update_cell(cell, new_value)
def update_cell(cell, value):
global x_pos, y_pos, puzzle, cell_values
x_cord, y_cord = ord(cell[0]) - ord('a'), int(cell[1]) - 1
puzzle[y_cord][x_cord] = value
x_pos = 20 + x_cord * 42
y_pos = 20 + y_cord * 42
canvas.delete(f"text_{cell}") # Delete the old text
canvas.create_text(x_pos, y_pos, text=str(value), fill='gray', font=('Arial', 35, 'bold'), tags=f"text_{cell}")
cell_values[cell] = value
def detect_pos(event):
x_cord, y_cord = event.x // 42, event.y // 42
if 0 <= x_cord <= 8 and 0 <= y_cord <= 8:
return chr(ord('a') + x_cord) + str(y_cord + 1)
canvas.bind("<Motion>", detect_pos)
canvas.bind("<Button-1>", draw_ans)
draw_board()
draw_nums()
root.mainloop()
This edits all the cells, I want the cells that provided by puzzle itself to be uneditable
You should keep the original generated board intact and use it to determine whether the cell is editable or not.
import copy
...
_puzzle = Sudoku(3).difficulty(0.8)
# duplicate the board to puzzle
puzzle = copy.deepcopy(_puzzle.board)
...
# function to get the value of a cell in original board
def get_puzzle_initial_value(cell):
col = ord(cell[0]) - ord('a')
row = int(cell[1]) - 1
return _puzzle.board[row][col]
def draw_ans(event):
cell = detect_pos(event)
num = get_puzzle_initial_value(cell)
if num is None:
global x_pos, y_pos, puzzle, cell_values, puzzle_cells
if cell:
cell_id = canvas.find_withtag(f"text_{cell}")
# Only allow adding numbers to empty cells in the puzzle and not in puzzle_cells
if cell_values[cell] is None and not cell_id in puzzle_cells:
new_value = 1 # Start with 1 for empty cells
else:
if cell_id not in puzzle_cells:
new_value = (cell_values[cell] % 9) + 1
update_cell(cell, new_value)
Note also that you don't need to delete the current number and create new number inside update_cell()
. Use canvas.itemconfigure()
to update the cell instead:
def update_cell(cell, value):
global x_pos, y_pos, puzzle, cell_values
x_cord, y_cord = ord(cell[0]) - ord('a'), int(cell[1]) - 1
puzzle[y_cord][x_cord] = value
x_pos = 20 + x_cord * 42
y_pos = 20 + y_cord * 42
canvas.itemconfigure(f"text_{cell}", text=str(value))
cell_values[cell] = value