增加选择4x4、6x6模式,以下是三种模式的不同解析:
-
4x4模式:
- 数独大小:4x4
- 每个宫格大小:2x2
- 数字范围:1-4
-
6x6模式:
- 数独大小:6x6
- 每个宫格大小:2x3
- 数字范围:1-6
-
9x9模式:
- 数独大小:9x9
- 每个宫格大小:3x3
- 数字范围:1-9
主要优化点:
4. 添加了模式选择下拉框,可以选择4x4、6x6、9x9模式
5. 根据选择的模式动态创建不同大小的棋盘
6. 生成不同大小的数独题目
7. 验证输入的合法性
8. 检查是否完成数独
以下为非完整代码,完整代码资源有下载
class SudokuGame:
# 常量定义
CELL_WIDTH = 3
CELL_FONT = ('Arial', 18)
ORIGINAL_COLOR = 'black'
PLAYER_COLOR = 'blue'
BG_COLOR = 'white'
READONLY_BG_COLOR = 'lightgray'
MIN_REMOVE_COUNT = 1 # 至少保留一个数字
MODE_CONFIG = {
"4x4": {"base": 2, "remove_count": 8, "size": 4}, # 4x4棋盘,每个宫格为2x2,数字范围1-4,移除数字数量为8个
"6x6": {"base": 2, "remove_count": 20, "size": 6}, # 6x6棋盘,数字范围1-6,移除数字数量为20个
"9x9": {"base": 3, "remove_count": 40, "size": 9} # 9x9棋盘,数字范围1-9,移除数字数量为40个
}
def __init__(self, root):
self.root = root
self.root.title("数独游戏")
self.board = []
self.original = []
self.entries = [[None for _ in range(9)] for _ in range(9)]
self.mode = "9x9" # 默认模式
self.create_mode_selection()
self.create_widgets()
def create_mode_selection(self):
# 创建模式选择区域
self.mode_frame = tk.Frame(self.root)
self.mode_frame.grid(row=0, column=0, pady=10)
tk.Label(self.mode_frame, text="选择模式:").pack(side="left", padx=5)
self.mode_var = tk.StringVar(value="9x9")
modes = ["4x4", "6x6", "9x9"]
for mode in modes:
tk.Radiobutton(self.mode_frame, text=mode, variable=self.mode_var,
value=mode, command=self.update_mode).pack(side="left", padx=5)
def update_mode(self):
# 更新模式并生成新棋局
self.mode = self.mode_var.get()
self.clear_board()
self.generate_sudoku()
def clear_board(self):
# 清理现有的输入框
for row in range(9):
for col in range(9):
if self.entries[row][col]:
self.entries[row][col].destroy()
self.entries[row][col] = None
def create_widgets(self):
# 创建数独棋盘
self.frame = tk.Frame(self.root)
self.frame.grid(row=1, column=0, padx=10, pady=10)
# 根据模式设置棋盘大小
config = self.MODE_CONFIG.get(self.mode, self.MODE_CONFIG["9x9"])
size = config["size"]
for row in range(size):
for col in range(size):
entry = tk.Entry(self.frame, width=self.CELL_WIDTH, font=self.CELL_FONT, justify='center')
entry.grid(row=row, column=col, padx=(0 if col % 3 != 2 else 5),
pady=(0 if row % 3 != 2 else 5))
self.entries[row][col] = entry
# 按钮
self.btn_frame = tk.Frame(self.root)
self.btn_frame.grid(row=2, column=0, pady=10)
self.reset_button = tk.Button(self.btn_frame, text="重新开始本局", command=self.reset_board)
self.reset_button.pack(side="left", padx=10)
self.new_button = tk.Button(self.btn_frame, text="生成新棋局", command=self.generate_sudoku)
self.new_button.pack(side="left", padx=10)
def generate_sudoku(self):
# 根据选择的模式生成数独
config = self.MODE_CONFIG.get(self.mode, self.MODE_CONFIG["9x9"])
base = config["base"]
remove_count = config["remove_count"]
size = config["size"]
side = size # 棋盘边长等于size
def pattern(r, c): return (base * (r % base) + r // base + c) % side
from random import sample
def shuffle(s): return sample(s, len(s))
rBase = range(base)
rows = [g * base + r for g in shuffle(rBase) for r in shuffle(rBase)]
cols = [g * base + c for g in shuffle(rBase) for c in shuffle(rBase)]
nums = shuffle(range(1, size + 1)) # 数字范围根据棋盘大小调整
# 创建棋盘时使用正确的尺寸
board = [[0 for _ in range(side)] for _ in range(side)]
# 填充棋盘
for r in range(side):
for c in range(side):
board[r][c] = nums[pattern(r, c)]
# 移除部分数字,生成题目
squares = side * side
# 确保不会尝试移除超过可用数量的数字
remove_count = min(remove_count, squares - self.MIN_REMOVE_COUNT) # 至少保留一个数字
# 安全地随机移除数字
available_positions = [(r, c) for r in range(side) for c in range(side) if r < side and c < side]
if available_positions:
# 确保至少保留一个数字
safe_remove_count = min(remove_count, len(available_positions))
for r, c in sample(available_positions, safe_remove_count):
if r < side and c < side: # 再次检查边界
board[r][c] = 0
# 清空未使用区域
self.clear_board()
# 重新创建对应当前模式的输入框
# 使用安全的size值
current_size = config["size"]
for row in range(current_size):
for col in range(current_size):
# 确保不会越界访问
if row < len(self.entries) and col < len(self.entries[row]):
entry = tk.Entry(self.frame, width=self.CELL_WIDTH, font=self.CELL_FONT, justify='center')
entry.grid(row=row, column=col, padx=(0 if col % 3 != 2 else 5),
pady=(0 if row % 3 != 2 else 5))
entry.bind("<Key>", self.on_key)
self.entries[row][col] = entry
self.board = board
self.original = copy.deepcopy(board)
self.update_gui()
def update_gui(self):
# 根据模式更新GUI
config = self.MODE_CONFIG.get(self.mode, self.MODE_CONFIG["9x9"])
size = config["base"] ** 2
# 确保board已初始化
if not self.board:
return
for row in range(size):
for col in range(size):
val = 0
# 安全访问board元素
if row < len(self.board) and col < len(self.board[row]):
val = self.board[row][col]
entry = self.entries[row][col]
if entry:
entry.delete(0, tk.END)
# 安全访问board元素
if row < len(self.board) and col < len(self.board[row]):
val = self.board[row][col]
if val != 0:
entry.insert(0, str(val))
entry.config(fg=self.ORIGINAL_COLOR, readonlybackground=self.READONLY_BG_COLOR)
entry.bind("<Key>", lambda e: "break") # 不可编辑原始数字
else:
entry.config(fg=self.PLAYER_COLOR, bg=self.BG_COLOR)
entry.bind("<Key>", self.on_key)
def on_key(self, event):
widget = event.widget
if event.char.isdigit() or event.keysym in ('BackSpace', 'Delete'):
# 允许输入数字或删除
pass
else:
return "break"
def reset_board(self):
self.board = copy.deepcopy(self.original)
self.update_gui()