数独游戏和Python实现

数独游戏和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()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学习&实践爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值