python带图形化界面的数独游戏

实现一个带有图形用户界面(GUI)的数独游戏应用程序。具体功能和使用的技术如下:

主要功能

  1. 生成数独谜题:程序可以生成一个有效的数独谜题,确保该谜题只有一个解。
  2. 求解数独谜题:用户可以在界面上手动输入数独谜题,然后点击“求解”按钮来获取答案。
  3. 显示结果:如果数独谜题有解,则在界面上显示解;如果没有解,则弹出消息框提示“无解”。

使用的技术

  1. Tkinter

    • Tkinter 是 Python 的标准 GUI 库,用于创建图形用户界面。
    • 在这个项目中,Tkinter 被用来创建窗口、框架、输入框和按钮等组件。
  2. 回溯算法

    • 回溯算法是一种递归算法,适用于解决约束满足问题,如数独。
    • 通过尝试每个可能的数字并在必要时回溯,找到数独谜题的解决方案。
  3. 随机数生成

    • 使用 random 模块来生成随机数,确保生成的数独谜题具有一定的随机性和挑战性。
  4. 异常处理

    • 使用 try-except 块来捕获并处理用户输入中的非整数值,确保程序的健壮性。

详细说明

初始化 (__init__)
  • 创建主窗口并设置标题。
  • 初始化一个 9x9 的二维数组 board 来表示数独棋盘。
  • 初始化一个列表 entries 来存储所有输入框控件。
  • 调用 create_widgets 方法创建界面组件。
  • 调用 generate_puzzle 方法生成初始数独谜题。
创建界面组件 (create_widgets)
  • 创建一个框架 frame 来容纳数独棋盘。
  • 使用嵌套循环创建 9x9 的输入框网格,并将它们添加到 entries 列表中。
  • 设置交替背景色以区分不同的 3x3 区域。
  • 创建“求解”按钮和“生成”按钮,并将它们放置在窗口中。
检查有效性 (is_valid)
  • 检查在指定位置放置数字是否违反数独规则(即在同一行、列或 3x3 区域内没有重复数字)。
求解数独 (solve_sudoku_helper)
  • 使用回溯算法递归地尝试填充棋盘上的空白位置。
  • 如果成功找到一个解决方案,则返回 True;否则继续尝试其他可能性。
解析用户输入并求解 (solve_sudoku)
  • 从输入框中读取用户输入的值,并将其转换为整数填入 board 数组。
  • 调用 solve_sudoku_helper 方法求解数独。
  • 如果找到解决方案,则调用 update_board 更新界面;否则弹出消息框提示“无解”。
查找空位置 (find_empty_location)
  • 遍历棋盘,查找第一个空白位置(值为 0 的位置)。
更新界面 (update_board)
  • 清空所有输入框,并根据 board 数组的内容更新输入框的值。
生成数独谜题 (generate_puzzle)
  • 生成一个完整的数独解决方案。
  • 通过随机移除一些数字来生成谜题,同时确保生成的谜题只有一个解。
  • 使用 find_all_solutions 方法验证生成的谜题是否有唯一解。
查找所有解决方案 (find_all_solutions)
  • 使用回溯算法查找所有可能的解决方案。
  • 返回所有解决方案的列表。

代码展示:

import tkinter as tk
from tkinter import messagebox
import random

class SudokuSolver:
    def __init__(self, master):
        # 初始化主窗口
        self.master = master
        master.title("数独求解器")
        
        # 初始化数独棋盘和输入框列表
        self.board = [[0] * 9 for _ in range(9)]
        self.entries = []
        
        # 创建界面组件
        self.create_widgets()
        # 生成一个初始数独谜题
        self.generate_puzzle()
    
    def create_widgets(self):
        # 创建一个框架来容纳数独棋盘
        frame = tk.Frame(self.master)
        frame.pack(padx=10, pady=10)
        
        # 创建9x9的输入框网格
        for i in range(9):
            row_entries = []
            for j in range(9):
                entry = tk.Entry(frame, width=3, font=('Arial', 20), justify='center')
                entry.grid(row=i, column=j, padx=1, pady=1)
                # 设置交替背景色以区分不同的3x3区域
                if (i // 3 + j // 3) % 2 == 0:
                    entry.config(bg='#f0f0f0')
                row_entries.append(entry)
            self.entries.append(row_entries)
        
        # 创建“求解”按钮
        solve_button = tk.Button(self.master, text="求解", command=self.solve_sudoku)
        solve_button.pack(side=tk.LEFT, padx=10, pady=10)
        
        # 创建“生成”按钮
        generate_button = tk.Button(self.master, text="生成", command=self.generate_puzzle)
        generate_button.pack(side=tk.RIGHT, padx=10, pady=10)
    
    def is_valid(self, board, row, col, num):
        # 检查在指定位置放置数字是否有效
        if num in board[row]:
            return False
        
        if num in [board[i][col] for i in range(9)]:
            return False
        
        start_row, start_col = 3 * (row // 3), 3 * (col // 3)
        for i in range(start_row, start_row + 3):
            for j in range(start_col, start_col + 3):
                if board[i][j] == num:
                    return False
        
        return True
    
    def solve_sudoku_helper(self, board):
        # 使用回溯算法求解数独
        empty = self.find_empty_location(board)
        if not empty:
            return True
        
        row, col = empty
        
        for num in range(1, 10):
            if self.is_valid(board, row, col, num):
                board[row][col] = num
                
                if self.solve_sudoku_helper(board):
                    return True
                
                board[row][col] = 0
        
        return False
    
    def solve_sudoku(self):
        # 从输入框中读取值并填充到棋盘数组
        for i in range(9):
            for j in range(9):
                try:
                    value = int(self.entries[i][j].get())
                    if 1 <= value <= 9:
                        self.board[i][j] = value
                    else:
                        self.board[i][j] = 0
                except ValueError:
                    self.board[i][j] = 0
        
        # 尝试求解数独
        if self.solve_sudoku_helper(self.board):
            self.update_board()
        else:
            messagebox.showinfo("结果", "无解!")
    
    def find_empty_location(self, board):
        # 查找棋盘上的第一个空白位置
        for i in range(9):
            for j in range(9):
                if board[i][j] == 0:
                    return (i, j)
        return None
    
    def update_board(self):
        # 更新界面中的输入框以显示当前棋盘状态
        for i in range(9):
            for j in range(9):
                self.entries[i][j].delete(0, tk.END)
                if self.board[i][j] != 0:
                    self.entries[i][j].insert(tk.END, str(self.board[i][j]))
    
    def generate_puzzle(self):
        # 生成一个新的数独谜题
        while True:
            board = [[0] * 9 for _ in range(9)]
            
            for r in range(0, 9, 3):
                nums = list(range(1, 10))
                random.shuffle(nums)
                for i in range(3):
                    for j in range(3):
                        board[r+i][r+j] = nums.pop()
            
            if self.solve_sudoku_helper(board):
                attempts = 5
                counter = 81
                while attempts > 0 and counter >= 36:
                    row = random.randint(0, 8)
                    col = random.randint(0, 8)
                    while board[row][col] == 0:
                        row = random.randint(0, 8)
                        col = random.randint(0, 8)
                    
                    backup = board[row][col]
                    board[row][col] = 0
                    
                    copy_board = [row[:] for row in board]
                    if len(self.find_all_solutions(copy_board)) == 1:
                        counter -= 1
                    else:
                        board[row][col] = backup
                        attempts -= 1
            
                if counter <= 36:
                    break
        
        self.board = board
        self.update_board()
    
    def find_all_solutions(self, board):
        # 查找所有可能的解决方案,确保生成的谜题只有一个解
        solutions = []
        
        def backtrack():
            empty = self.find_empty_location(board)
            if not empty:
                solutions.append([row[:] for row in board])
                return
            
            row, col = empty
            
            for num in range(1, 10):
                if self.is_valid(board, row, col, num):
                    board[row][col] = num
                    
                    backtrack()
                    
                    board[row][col] = 0
        
        backtrack()
        return solutions

if __name__ == "__main__":
    root = tk.Tk()
    app = SudokuSolver(root)
    root.mainloop()




运行代码

你可以运行这段代码来启动一个简单的数独游戏应用程序。以下是运行步骤:

  1. 确保你已经安装了 Python。
  2. 将上述代码保存到一个 .py 文件中,例如 sudoku_solver.py
  3. 打开终端或命令提示符,导航到保存文件的目录。
  4. 运行脚本:python sudoku_solver.py

这将打开一个包含数独棋盘和控制按钮的窗口,你可以手动输入数独谜题或点击“生成”按钮来获得一个新的谜题,然后点击“求解”按钮来查看解答。

效果图

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

编织幻境的妖

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

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

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

打赏作者

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

抵扣说明:

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

余额充值