用python完成一个数独小游戏
时隔一年我回来完善我的数独程序了,在原来使用matlab产生数独的基础上,改为python语法,并对其进行扩展功能,改为一个小游戏。
matlab产生数独的原理在我第一篇博客里有比较详细的介绍,matlab产生数独原理,这里不多说
改为python以后代码如下
def daan(): #用于产生数独答案
shudu = np.zeros((9,9),dtype=int) #产生全0数组备用
while (sum(sum(shudu[:,:]))!=405): #判断数独答案是否正确,简单了点,但很实用
n = 1
A = np.zeros((9,9), dtype=int)
a = [x for x in range(1, 10)] #产生1-9数组备用
b = [x for x in range(1, 10)]
random.shuffle(b) #产生随机1-9数组,作为数独第一行
A[0,:] = b #赋值
for i in range(1,9): #开始填数
for j in range(0,9):
x = A[i,:] #取出所在行
y = A[:,j] #取出所在列
if 0<=j and j<3:
z = A[:,0:3]
elif 3<=j and j<6:
z = A[:,3:6]
else :
z = A[:,6:9]
if 0 <= i and i < 3:
z = z[0:3,:]
elif 3 <= i and i < 6:
z = z[3:6,:]
else:
z = z[6:9,:] #取出所在宫
x_pos = np.nonzero(x)[0] #取出行,列,宫的所有非零值
X = np.zeros((len(x_pos)), dtype=int)
for p in range(0, len(x_pos)):
X[p] = x[x_pos[p]]
y_pos = np.nonzero(y)[0]
Y = np.zeros((len(y_pos)), dtype=int)
for p in range(0, len(y_pos)):
Y[p] = y[y_pos[p]]
z_pos = np.transpose(np.nonzero(z))
Z = np.zeros((len(z_pos)), dtype=int)
for p in range(0,len(z_pos)):
m = z_pos[p,0]
n = z_pos[p,1]
Z[p] = z[m,n]
t = list(set(X).union(set(Y)))
t = list(set(t).union(set(Z))) #所有非零值取并集,
n = list(set(a).difference(set(t))) # 并集和a取差集,判断哪些数还可以填
try:
L = len(n) #判断是否还有备选数
except BaseException as e:
L = 0
r = random.random()
h = math.ceil(r*L) #备选数中随机选择一个
try:
A[i,j] = n[h-1] #进行赋值
except BaseException as e:
n = 2 #报错则说明之前的填数有误
break
if n == 2:
break
if n == 2:
continue #重新开始循环
shudu = A #81个数全部正确,则赋值
return shudu #返回
这样可以产生一个标准数独作为答案
下面可以随机挖空产生一个数独作为题面
def chansheng(shudu,grade): #随机挖空,产生数独题面
for i in range(0,9):
b = [x for x in range(0, 9)]
random.shuffle(b)
for j in range(0,grade):
shudu[i,b[j]] = 0
return shudu
grade作为输入控制数独难度,每一行挖掉几个数字
然后是修改数独函数,用来让用户填数
def xiugai(shudu,pos,pos_nonling): #输入参数,对数独进行修改
x = int(pos/100) #得到行值
y = int((pos%100)/10) #得到列值
for i in range(0,len(pos_nonling)): #pos_nonling代表题面非零值的位置
a = int(pos_nonling[i,0])
b = int(pos_nonling[i,1])
if x-1 == a and y-1 == b:
print('此数不可修改,请重新输入') #这些位置的数字不可修改
n = 2
return shudu,n
num = int((pos%100)%10) #若不是这些位置,则可进行修改
shudu[x-1,y-1] = num
n = 1 #是否正确修改的标志位
return shudu,n #返回修改后的结果和标志位
检查填入的数字是否正确
def jiancha(shudu,pos,shibai): #检查填入数字是否正确
a = int(pos/100) #得到行值
b = int((pos%100)/10) #得到列值
c = int((pos%100)%10) #得到所填数字
shudu[a-1,b-1] = 0
x = shudu[a-1, :]
y = shudu[:, b-1]
if 0 <= b-1 and b-1 < 3:
z = shudu[:, 0:3]
elif 3 <= b-1 and b-1 < 6:
z = shudu[:, 3:6]
else:
z = shudu[:, 6:9]
if 0 <= a-1 and a-1 < 3:
z = z[0:3, :]
elif 3 <= a-1 and a-1 < 6:
z = z[3:6, :]
else:
z = z[6:9, :]
x_pos = np.nonzero(x)[0]
X = np.zeros((len(x_pos)), dtype=int)
for p in range(0, len(x_pos)):
X[p] = x[x_pos[p]]
y_pos = np.nonzero(y)[0]
Y = np.zeros((len(y_pos)), dtype=int)
for p in range(0, len(y_pos)):
Y[p] = y[y_pos[p]]
z_pos = np.transpose(np.nonzero(z))
Z = np.zeros((len(z_pos)), dtype=int)
for p in range(0, len(z_pos)):
m = z_pos[p, 0]
n = z_pos[p, 1]
Z[p] = z[m, n]
t = list(set(X).union(set(Y)))
t = list(set(t).union(set(Z))) #以上和产生答案逻辑相同,获取所有不可填数字
if c in t: #与不可填数字相同
shibai = shibai+1
print('此处已经不能填', c)
print("您已失败%d次" % (shibai))
print("剩余失败次数:", shibaicishu - shibai)
shudu[a-1, b-1] = 0 #抹除这次填数
return shibai
else:
shudu[a-1, b-1] = c #不同则进行赋值
return shibai
判断游戏是否结束,是否全部填完
def jieshu(shudu): #检查游戏是否结束
if sum(sum(shudu[:, :])) == 405:
return 1
else:
return 2
对每一次的数独进行展示
def show(shudu,pos_nonling): #对每一步的数独结果进行展示
for i in range(0,10):
for j in range(0,10):
if i == 0 and j == 0: #(1,1)位置空白,为了好看
print('\t',end='')
if i == 0 and j != 0:
print('\033[34m',j,'\t',end='')#紫色 #第一行打入用紫色写入横坐标
if j == 9:
print('\n') #结束后换行
if i != 0 and j == 0:
print('\033[34m',i,'\t',end='') #第一列用紫色打入列坐标
if i != 0 and j != 0:
if shudu[i-1,j-1] == 0:
print('\033[0m',' ', '\t', end='') #被挖掉得空不进行显示
else:
for m in range(0, len(pos_nonling)): #判断是否为题面固定数字
a = int(pos_nonling[m, 0])
b = int(pos_nonling[m, 1])
if i - 1 == a and j - 1 == b:
key = 1
break
else:
key = 2
if key == 1:
print('\033[0m', shudu[i-1,j-1], '\t', end='') #题面固定数字用白色显示
else:
print('\033[31m', shudu[i - 1, j - 1], '\t', end='') #可填数字用红色显示
if j == 9:
print('\n') #每行结尾换行
最后,主函数
if __name__ == '__main__':
print('游戏开始')
global shibai
shibai = 0 #用来统计失败次数
try:
grand = int(input('请选择难度(1——6):')) #每行挖掉几个空
while (grand < 1 or grand > 6): #最少挖一个,最多6个
try:
grand = int(input('您的输入有误,请输入1——6之间的任意一个整数:'))
except BaseException as e:
continue
except BaseException as e:
grand = 0
while(grand<1 or grand>6):
try:
grand = int(input('您的输入有误,请输入1——6之间的任意一个整数:'))
except BaseException as e:
continue
try:
shibaicishu = int(input('请选择可失败次数:')) #允许输入错误的次数
while (shibaicishu < 1):
try:
shibaicishu = int(input('您的输入有误,请输入一个正整数:'))
except BaseException as e:
continue
except BaseException as e:
shibaicishu = 0
while(shibaicishu<1):
try:
shibaicishu = int(input('您的输入有误,请输入一个正整数:'))
except BaseException as e:
continue
shudu_answer= daan() #产生数独
shudu_question = chansheng(shudu_answer,grand) #挖空
pos_nonling = np.transpose(np.nonzero(shudu_question)) #获取非零元素位置
show(shudu_question,pos_nonling) #展示
while(1):
n = 2 #标志位,输入是否正确
while(n == 2):
try:
pos = int(input('请输入要输入的数字:')) #输入一个三位数,百位代表行值,十位代表列值,个位代表要填的数,所以最大999,最小111
while (pos < 111 or pos > 999):
try:
pos = int(input('您的输入有误,请输入一个三位数,百位代表行值,十位代表列值,个位代表要填的数:'))
except BaseException as e:
continue
except BaseException as e:
pos = 0
while (pos < 111 or pos > 999):
try:
pos = int(input('您的输入有误,请输入一个三位数,百位代表行值,十位代表列值,个位代表要填的数:'))
except BaseException as e:
continue
[shudu_change,n] = xiugai(shudu_question,pos,pos_nonling) #修改数独
shibai = jiancha(shudu_change,pos,shibai) #判断是否错误
if shibai == shibaicishu: #失败次数和预先设定一样,则输
key_finish = 1
break
show(shudu_change,pos_nonling)
stop = jieshu(shudu_change) #判断是否全部填完
if stop == 1:
key_finish = 2
break
if key_finish == 1: #标志位
print('游戏结束,挑战失败')
else:
print('恭喜,挑战成功')
实测了一下,在pycharm里跑没有问题,运行如下,但是在cmd里运行出示问题,没有颜色显示,
但是换成Windows terminal以后就可以了,运行结果很漂亮而且我没有办法打包生成exe文件,让我非常苦恼
如果有人可以成功生成exe的话十分希望可以和我联系一下,并且发我一份,十分感谢。
可以发我邮箱:tai_ge@163.com