数独游戏和Python实现
数独是一种经典的逻辑填数游戏,其规则相对简单,但需要玩家运用逻辑推理能力逐步解题。以下是数独的基本规则:
1. 棋盘结构
数独的棋盘是一个9×9的网格,由81个格子组成。
整个棋盘被划分为9个3×3的子网格(宫),每个子网格(宫)包含9个格子。
2.游戏规则
玩家需要在空格中填入数字1到9,使得每一行、每一列以及每一个3×3的小宫格中都恰好包含数字1到9,且每个数字不重复。 具体规则
行规则:每一行的9个格子中,数字1到9只能出现一次,不能重复。
列规则:每一列的9个格子中,数字1到9只能出现一次,不能重复。
宫规则:每一个3×3的小宫格中,数字1到9只能出现一次,不能重复。
3.数独解题思路
1)排除法(基础策略)
• 行/列排除:
若某行或列已有数字 N,则该行或列的其他单元格不能填 N。
示例:若第一行已有数字 5,则该行其他单元格排除 5。
• 宫排除:
若某个子网格(宫)已有数字 N,则该宫的其他单元格不能填 N。
示例:若左上角 3×3 宫已有数字 3,则该宫内其他空格不能填 3。
2)唯一候选数法
• 当某个单元格在行、列、宫中排除其他数字后,只剩一个可能的数字,即为唯一解。
示例:某单元格所在行、列、宫已包含 1-8,则该单元格必须填 9。
3)区块摒除法(中级策略)
• 利用子网格(宫)的分布限制某数字在行或列的位置。
示例:若数字 7 在中间宫只能出现在某一行,则该行其他宫的区域不能填 7。
4)隐性唯一数(高级策略)
• 当某行、列或宫中,某个数字 N 只能出现在一个单元格时,该单元格必填 N。
示例:某列中,数字 4 只能出现在一个单元格(其他位置均被排除),则该单元格填 4。
5)数对/数链法(进阶策略)
• 数对:若两个单元格在某行/列/宫中只能填相同的两个数字,则可排除其他单元格的这两个数字。
• 数链:通过多个单元格的关联关系,推导出唯一解。
解题技巧提示
1)从已知数最多的区域入手,优先填写容易确定的数字。
2)逐步缩小可能性,用铅笔标注候选数,避免遗漏。
3)反复检查冲突,确保每一步都符合行、列、宫的规则。
4)保持耐心,复杂谜题可能需要多次尝试和回溯。
先看运行效果
源码如下:
import tkinter as tk
from tkinter import messagebox
import random
class SudokuSolver:
def __init__(self, root):
self.root = root
self.root.title("数独求解器")
self.entries = [[None for _ in range(9)] for _ in range(9)]
self.create_ui()
def create_ui(self):
# 创建Canvas并绘制虚线
canvas = tk.Canvas(self.root, width=300, height=300)
canvas.grid(row=0, column=0, rowspan=9, columnspan=9)
# 绘制横向虚线
for y in [3, 6]:
canvas.create_line(0, y * 33 + 3.5, 300, y * 33 + 3.5, fill="red", dash=(4, 4))
# 绘制纵向虚线
for x in [3, 6]:
canvas.create_line(x * 33 + 3.5, 0, x * 33 + 3.5, 300, fill="red", dash=(4, 4))
# 创建输入框
for i in range(9):
for j in range(9):
entry = tk.Entry(self.root, width=2, font=('Arial', 18), justify='center', fg="black")
entry.place(x=j * 33 + 5, y=i * 33 + 5, width=30, height=30)
self.entries[i][j] = entry
# 创建按钮
solve_button = tk.Button(self.root, text="解谜", command=self.solve)
solve_button.grid(row=9, column=0, columnspan=2, pady=10, sticky="ew")
clear_button = tk.Button(self.root, text="清除", command=self.clear)
clear_button.grid(row=9, column=2, columnspan=2, pady=10, sticky="ew")
generate_button = tk.Button(self.root, text="生成", command=self.generate)
generate_button.grid(row=9, column=4, columnspan=2, pady=10, sticky="ew")
check_button = tk.Button(self.root, text="检查", command=self.check)
check_button.grid(row=9, column=6, columnspan=2, pady=10, sticky="ew")
# 新增帮助按钮
help_button = tk.Button(self.root, text="帮助", command=self.show_help)
help_button.grid(row=9, column=8, columnspan=1, pady=10, sticky="ew")
def show_help(self):
help_text = """
数独规则及玩法说明:
1. 数独是一个 9x9 的网格,分为 9 个 3x3 的子网格。
2. 目标是用数字 1-9 填满网格,满足以下条件:
- 每一行必须包含 1-9 且不重复
- 每一列必须包含 1-9 且不重复
- 每个 3x3 子网格必须包含 1-9 且不重复
3. 初始谜题会给出部分数字(红色),需推理填写剩余空格。
4. 按钮功能:
- 解谜:自动求解当前题目
- 清除:重置所有输入
- 生成:随机生成新题目
- 检查:验证答案是否正确
"""
messagebox.showinfo("数独帮助", help_text.strip())
def get_puzzle(self):
board = []
for i in range(9):
row = []
for j in range(9):
value = self.entries[i][j].get()
if value == "":
row.append(0)
else:
row.append(int(value))
board.append(row)
return board
def display_solution(self, board, original_board):
for i in range(9):
for j in range(9):
self.entries[i][j].delete(0, tk.END)
self.entries[i][j].insert(0, str(board[i][j]))
if original_board[i][j] != 0:
self.entries[i][j].config(fg="red")
else:
self.entries[i][j].config(fg="black")
def solve(self):
original_board = self.get_puzzle()
board = [row[:] for row in original_board]
if self.solve_sudoku(board):
self.display_solution(board, original_board)
else:
messagebox.showwarning("无解", "该数独无解!")
def solve_sudoku(self, board):
empty = self.find_empty(board)
if not empty:
return True
row, col = empty
for num in range(1, 10):
if self.is_valid(board, num, (row, col)):
board[row][col] = num
if self.solve_sudoku(board):
return True
board[row][col] = 0
return False
def is_valid(self, board, num, pos):
for i in range(9):
if board[pos[0]][i] == num and pos[1] != i:
return False
for i in range(9):
if board[i][pos[1]] == num and pos[0] != i:
return False
box_x = pos[1] // 3
box_y = pos[0] // 3
for i in range(box_y * 3, box_y * 3 + 3):
for j in range(box_x * 3, box_x * 3 + 3):
if board[i][j] == num and (i, j) != pos:
return False
return True
def find_empty(self, board):
for i in range(9):
for j in range(9):
if board[i][j] == 0:
return (i, j)
return None
def clear(self):
for i in range(9):
for j in range(9):
self.entries[i][j].delete(0, tk.END)
self.entries[i][j].config(fg="black")
def generate(self):
self.clear()
board = [[0 for _ in range(9)] for _ in range(9)]
self.fill_board(board)
self.remove_numbers(board)
self.display_solution(board, board)
def fill_board(self, board):
self.solve_sudoku_random(board)
def solve_sudoku_random(self, board):
empty = self.find_empty(board)
if not empty:
return True
row, col = empty
numbers = list(range(1, 10))
random.shuffle(numbers)
for num in numbers:
if self.is_valid(board, num, (row, col)):
board[row][col] = num
if self.solve_sudoku_random(board):
return True
board[row][col] = 0
return False
def remove_numbers(self, board):
cells = [(i, j) for i in range(9) for j in range(9)]
random.shuffle(cells)
for cell in cells[:random.randint(40, 50)]: # 随机移除40到50个数字
board[cell[0]][cell[1]] = 0
def check(self):
board = self.get_puzzle()
if self.is_valid_solution(board):
messagebox.showinfo("检查结果", "恭喜!您的解答正确。")
else:
messagebox.showwarning("检查结果", "抱歉,您的解答不正确。")
def is_valid_solution(self, board):
for i in range(9):
if not self.is_valid_group(board[i]): # 检查行
return False
if not self.is_valid_group([board[j][i] for j in range(9)]): # 检查列
return False
for i in range(0, 9, 3):
for j in range(0, 9, 3):
if not self.is_valid_group([board[x][y] for x in range(i, i+3) for y in range(j, j+3)]): # 检查3x3宫格
return False
return True
def is_valid_group(self, group):
return set(group) == set(range(1, 10))
if __name__ == "__main__":
root = tk.Tk()
app = SudokuSolver(root)
root.mainloop()