利用Python函数生成一个九宫格数独游戏
数独游戏是一种热门的思维训练游戏,也称为“九宫格”。这个游戏由一个$9\times 9$的方阵组成,每个方格中填写一个数字,一般是$1$到$9$之间的整数,使得每行、每列和每个$3\times3$的小方格内数字都不能重复。本文将介绍如何用Python函数生成一个九宫格数独游戏。
1. 导入模块
在编写程序之前,我们需要导入两个Python模块,分别为random和numpy。random模块用于随机生成数独游戏的初始状态,numpy模块用于创建九宫格数组和打印游戏界面。
import random import numpy as np
2. 生成初始状态的数独游戏
我们可以通过random模块生成一个$9\times 9$的二维数组,其中每个元素随机填写一个$1$到$9$之间的整数,并且需要满足数独游戏的规则,即每行、每列和每个$3\times3$的小方格内数字都不能重复。
为了方便判断行、列和小方格内是否已经存在某个数字,我们可以创建三个空的列表rows、cols和boxes。分别表示每行、每列和每个小方格,元素为一个集合,存储该行、列或小方格内已经存在的数字。
def generate_sudoku():
sudoku = [[0 for x in range(9)] for y in range(9)]
rows = [set() for x in range(9)]
cols = [set() for x in range(9)]
boxes = [set() for x in range(9)]
# 随机填写每个方格,并满足数独游戏的规则
for i in range(9):
for j in range(9):
# 计算当前方格所在的小方格的编号
box_index = (i // 3) * 3 + j // 3
# 生成一个随机数字
num = random.randint(1, 9)
# 如果该数字已经存在于该行、该列或该小方格中,则重新生成一个数字
while num in rows[i] or num in cols[j] or num in boxes[box_index]:
num = random.randint(1, 9)
# 将该数字填写到当前方格中,并记录到对应的行、列和小方格中
sudoku[i][j] = num
rows[i].add(num)
cols[j].add(num)
boxes[box_index].add(num)
return sudoku
在以上代码中,我们使用双重循环遍历每个方格,计算当前方格所在的小方格的编号,然后生成一个随机数字,如果该数字已经存在于该行、该列或该小方格中,则重新生成一个数字,最后将该数字填写到当前方格中,并记录到对应的行、列和小方格中。最终返回一个初始状态的数独游戏。
3. 检查数独游戏的解
我们需要编写一个函数来检查数独游戏的解是否正确。函数的输入为一个数独游戏的二维数组,输出为一个布尔值,表示该游戏是否有唯一解。
def check_solution(sudoku):
# 检查行
for i in range(9):
used = set()
for j in range(9):
if sudoku[i][j] in used:
return False
used.add(sudoku[i][j])
# 检查列
for j in range(9):
used = set()
for i in range(9):
if sudoku[i][j] in used:
return False
used.add(sudoku[i][j])
# 检查小方格
for box_i in range(3):
for box_j in range(3):
used = set()
for i in range(box_i*3, (box_i+1)*3):
for j in range(box_j*3, (box_j+1)*3):
if sudoku[i][j] in used:
return False
used.add(sudoku[i][j])
return True
在以上代码中,我们使用三个循环分别检查每一行、每一列和每一个小方格。对于每一行、每一列和每一个小方格,使用一个集合used来存储已经使用的数字,如果当前方格的数字已经存在于used集合中,则返回False表示该游戏无解,否则将该数字添加到used集合中,继续检查下一个方格。最终返回True表示该游戏有唯一解。
4. 生成数独游戏的解
为了生成数独游戏的解,我们可以使用递归 Backtracking 算法来求解数独游戏。
def solve_sudoku(sudoku):
# 查找未填写的方格
for i in range(9):
for j in range(9):
if sudoku[i][j] == 0:
# 枚举每个可以填写的数字
for num in range(1, 10):
# 判断该数字是否可以填写到当前方格中
if num not in get_used_nums(sudoku, i, j):
sudoku[i][j] = num
# 递归求解数独游戏
if solve_sudoku(sudoku):
return True
# 回溯
sudoku[i][j] = 0
return False
return True
在以上代码中,我们首先查找未填写的方格,如果当前方格的数值为$0$,则说明该方格未填写。我们枚举该方格可以填写的数字,判断该数字是否可以填写到当前方格中,注意要使用get_used_nums函数来获取当前行、当前列和当前小方格中已经存在的数字,如果可以填写,则将该数字填写到当前方格中,并递归求解数独游戏。如果递归求解成功,则返回True表示该游戏有解,如果递归求解失败,则回溯,将当前方格的数值重新设置为$0$,并继续枚举下一个数字。如果枚举完所有可能的数字后,仍然没有找到解,则返回False表示该游戏无解,继续回溯。
我们还需要编写一个函数get_used_nums来获取当前行、当前列和当前小方格中已经存在的数字。
def get_used_nums(sudoku, i, j):
used = set()
# 获取当前行中已经存在的数字
used |= set(sudoku[i])
# 获取当前列中已经存在的数字
used |= set(sudoku[x][j] for x in range(9))
# 获取当前小方格中已经存在的数字
box_i = i // 3
box_j = j // 3
used |= set(sudoku[x][y] for x in range(box_i*3, (box_i+1)*3) for y in range(box_j*3, (box_j+1)*3))
return used
在以上代码中,我们首先创建一个空的集合used,然后获取当前行中已经存在的数字并添加到used集合中,获取当前列中已经存在的数字并添加到used集合中,获取当前小方格中已经存在的数字并添加到used集合中,最终返回used集合。
5. 生成数独游戏
在以上步骤中,我们已经能够生成一个
