150行Python代碼實(shí)現(xiàn)帶界面的數(shù)獨(dú)游戲
今天閑著沒(méi)事干,以前做過(guò)html+js版的數(shù)獨(dú),這次做個(gè)python版本的,界面由pygame完成,數(shù)獨(dú)生成由遞歸算法實(shí)現(xiàn),由shuffle保證每次游戲都是不一樣的情況,have fun;
功能列表:
- 圖形化的數(shù)獨(dú)游戲;
- python實(shí)現(xiàn),依賴pygame庫(kù);
- 隨機(jī)生成游戲,每次運(yùn)行都不一樣;
- 數(shù)字填入后的正確性判斷以及顏色提示;
- 顯示剩余需填入的空格,已經(jīng)操作的次數(shù);
- 難度可選,通過(guò)修改需要填入的空的數(shù)量;
游戲界面
初始界面

過(guò)程中界面

運(yùn)行方式
python main.py 15
這里的15表示需要填入的空格數(shù)量為15,理論上這個(gè)值越大,難度就越高,大家可以隨機(jī)調(diào)整,或者設(shè)置容易、簡(jiǎn)單、困難、地獄等對(duì)應(yīng)不同的值即可,很方便修改;
程序分析
界面部分
這部分很簡(jiǎn)單的通過(guò)pygame來(lái)實(shí)現(xiàn),主要使用了其中的主循環(huán)、鼠標(biāo)鍵盤(pán)監(jiān)聽(tīng)、畫(huà)矩形線條、字體、顏色控制等,理解起來(lái)很容易,對(duì)于這部分不太熟悉的同學(xué),這樣理解就好: pygame的主循環(huán)中一方面負(fù)責(zé)接收用戶輸入,一般就是鼠標(biāo)和鍵盤(pán),另一方面負(fù)責(zé)實(shí)時(shí)更新界面顯示內(nèi)容 ;
對(duì)于界面上各部分內(nèi)容的繪制的函數(shù)封裝
# 繪制背景部分,這里就是9*9的九宮格
def draw_background():
# white background
screen.fill(COLORS['white'])
# draw game board
pygame.draw.rect(screen,COLORS['black'],(0,0,300,900),5)
pygame.draw.rect(screen,COLORS['black'],(300,0,300,900),5)
pygame.draw.rect(screen,COLORS['black'],(600,0,300,900),5)
pygame.draw.rect(screen,COLORS['black'],(0,0,900,300),5)
pygame.draw.rect(screen,COLORS['black'],(0,300,900,300),5)
pygame.draw.rect(screen,COLORS['black'],(0,600,900,300),5)
# 將用戶選中的各自背景改為藍(lán)色塊表示選中
def draw_choose():
pygame.draw.rect(screen,COLORS['blue'],(cur_j*100+5,cur_i*100+5,100-10,100-10),0)
# 繪制九宮格中的數(shù)字,包括本來(lái)就有的,以及用戶填入的,本來(lái)就在的用灰色,用戶填入的如何合法則為綠色,否則為紅色,是一種提示
def draw_number():
for i in range(len(MATRIX)):
for j in range(len(MATRIX[0])):
_color = check_color(MATRIX,i,j) if (i,j) in BLANK_IJ else COLORS['gray']
txt = font80.render(str(MATRIX[i][j] if MATRIX[i][j] not in [0,'0'] else ''),True,_color)
x,y = j*100+30,i*100+10
screen.blit(txt,(x,y))
# 繪制最下方的當(dāng)前空格子數(shù)量以及用戶的操作數(shù)量
def draw_context():
txt = font100.render('Blank:'+str(cur_blank_size)+' Change:'+str(cur_change_size),True,COLORS['black'])
x,y = 10,900
screen.blit(txt,(x,y))
主循環(huán)中對(duì)上述函數(shù)的調(diào)用以及鼠標(biāo)鍵盤(pán)事件處理
# 主循環(huán),負(fù)責(zé)監(jiān)聽(tīng)鼠標(biāo)鍵盤(pán)時(shí)間,以及刷新界面內(nèi)容,以及檢查是否贏得了游戲
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
break
elif event.type == pygame.MOUSEBUTTONDOWN:
cur_j,cur_i = int(event.pos[0]/100),int(event.pos[1]/100)
elif event.type == event.type == pygame.KEYUP:
if chr(event.key) in ['1','2','3','4','5','6','7','8','9'] and (cur_i,cur_j) in BLANK_IJ:
MATRIX[cur_i][cur_j] = int(chr(event.key))
cur_blank_size = sum([1 if col==0 or col=='0' else 0 for row in MATRIX for col in row])
cur_change_size +=1
# background
draw_background()
# choose item
draw_choose()
# numbers
draw_number()
# point
draw_context()
# flip
pygame.display.flip()
# check win or not
if check_win(MATRIX_ANSWER,MATRIX):
print('You win, smarty ass!!!')
break
pygame.quit()
生成表示數(shù)獨(dú)的二維數(shù)組
相對(duì)于界面部分,這部分在邏輯上要難一些,思路以遞歸為核心,輔以隨機(jī)性,得到一個(gè)每次生成都不一致的數(shù)獨(dú)游戲,生成思路簡(jiǎn)單描述如下:
- 遍歷每個(gè)空格,填入目前為止合法的數(shù)字;
- 如果有數(shù)字可以填入,則繼續(xù)向下一個(gè)空格;
- 如果沒(méi)有數(shù)字可以填入,表示之前的數(shù)字有問(wèn)題,則結(jié)束遞歸;
- 當(dāng)遞歸到最后一個(gè)格子的下一個(gè)時(shí),表示已經(jīng)生成完畢,返回即可;
- 這個(gè)過(guò)程中對(duì)1~9這九個(gè)數(shù)字的遍歷數(shù)字會(huì)經(jīng)過(guò)shuffle處理,保證隨機(jī)性而不是每次都得到同一個(gè)合法的數(shù)獨(dú)數(shù)組;
生成過(guò)程代碼
遞歸的一個(gè)優(yōu)勢(shì)是通常代碼都很短,當(dāng)然閱讀性不強(qiáng),歡迎大佬們改為循環(huán);
def shuffle_number(_list):
random.shuffle(_list)
return _list
def check(matrix,i,j,number):
if number in matrix[i]:
return False
if number in [row[j] for row in matrix]:
return False
group_i,group_j = int(i/3),int(j/3)
if number in [matrix[i][j] for i in range(group_i*3,(group_i+1)*3) for j in range(group_j*3,(group_j+1)*3)]:
return False
return True
def build_game(matrix,i,j,number):
if i>8 or j>8:
return matrix
if check(matrix,i,j,number):
_matrix = [[col for col in row] for row in matrix]
_matrix[i][j] = number
next_i,next_j = (i+1,0) if j==8 else (i,j+1)
for _number in shuffle_number(number_list):
__matrix = build_game(_matrix,next_i,next_j,_number)
if __matrix and sum([sum(row) for row in __matrix])==(sum(range(1,10))*9):
return __matrix
return None
隨機(jī)覆蓋數(shù)獨(dú)數(shù)組中的N個(gè)位置
- matrix_all表示整個(gè)數(shù)獨(dú)數(shù)組
- matrix_blank表示部分被替換為0的用于顯示的數(shù)組
- blank_ij表示被覆蓋位置的i和j
def give_me_a_game(blank_size=9):
matrix_all = build_game(matrix,0,0,random.choice(number_list))
set_ij = set()
while len(list(set_ij))<blank_size:
set_ij.add(str(random.choice([0,1,2,3,4,5,6,7,8]))+','+str(random.choice([0,1,2,3,4,5,6,7,8])))
matrix_blank = [[col for col in row] for row in matrix_all]
blank_ij = []
for ij in list(set_ij):
i,j = int(ij.split(',')[0]),int(ij.split(',')[1])
blank_ij.append((i,j))
matrix_blank[i][j] = 0
return matrix_all,matrix_blank,blank_ij
最后附上全部代碼
大家也可以直接從我的 Github倉(cāng)庫(kù) fork下來(lái)直接運(yùn)行;
main.py:主流程+界面+執(zhí)行
import sys
import pygame
from pygame.color import THECOLORS as COLORS
from build import print_matrix,give_me_a_game,check
def draw_background():
# white background
screen.fill(COLORS['white'])
# draw game board
pygame.draw.rect(screen,COLORS['black'],(0,0,300,900),5)
pygame.draw.rect(screen,COLORS['black'],(300,0,300,900),5)
pygame.draw.rect(screen,COLORS['black'],(600,0,300,900),5)
pygame.draw.rect(screen,COLORS['black'],(0,0,900,300),5)
pygame.draw.rect(screen,COLORS['black'],(0,300,900,300),5)
pygame.draw.rect(screen,COLORS['black'],(0,600,900,300),5)
def draw_choose():
pygame.draw.rect(screen,COLORS['blue'],(cur_j*100+5,cur_i*100+5,100-10,100-10),0)
def check_win(matrix_all,matrix):
if matrix_all == matrix:
return True
return False
def check_color(matrix,i,j):
_matrix = [[col for col in row]for row in matrix]
_matrix[i][j] = 0
if check(_matrix,i,j,matrix[i][j]):
return COLORS['green']
return COLORS['red']
def draw_number():
for i in range(len(MATRIX)):
for j in range(len(MATRIX[0])):
_color = check_color(MATRIX,i,j) if (i,j) in BLANK_IJ else COLORS['gray']
txt = font80.render(str(MATRIX[i][j] if MATRIX[i][j] not in [0,'0'] else ''),True,_color)
x,y = j*100+30,i*100+10
screen.blit(txt,(x,y))
def draw_context():
txt = font100.render('Blank:'+str(cur_blank_size)+' Change:'+str(cur_change_size),True,COLORS['black'])
x,y = 10,900
screen.blit(txt,(x,y))
if __name__ == "__main__":
# init pygame
pygame.init()
# contant
SIZE = [900,1000]
font80 = pygame.font.SysFont('Times', 80)
font100 = pygame.font.SysFont('Times', 90)
# create screen 500*500
screen = pygame.display.set_mode(SIZE)
# variable parameter
cur_i, cur_j = 0,0
cur_blank_size = int(sys.argv[1])
cur_change_size = 0
# matrix abount
MATRIX_ANSWER,MATRIX,BLANK_IJ = give_me_a_game(blank_size=cur_blank_size)
print(BLANK_IJ)
print_matrix(MATRIX)
# main loop
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
break
elif event.type == pygame.MOUSEBUTTONDOWN:
cur_j,cur_i = int(event.pos[0]/100),int(event.pos[1]/100)
elif event.type == event.type == pygame.KEYUP:
if chr(event.key) in ['1','2','3','4','5','6','7','8','9'] and (cur_i,cur_j) in BLANK_IJ:
MATRIX[cur_i][cur_j] = int(chr(event.key))
cur_blank_size = sum([1 if col==0 or col=='0' else 0 for row in MATRIX for col in row])
cur_change_size +=1
# background
draw_background()
# choose item
draw_choose()
# numbers
draw_number()
# point
draw_context()
# flip
pygame.display.flip()
# check win or not
if check_win(MATRIX_ANSWER,MATRIX):
print('You win, smarty ass!!!')
break
pygame.quit()
build.py:生成數(shù)獨(dú)數(shù)組部分
import random
def print_matrix(matrix):
print('—'*19)
for row in matrix:
print('|'+' '.join([str(col) for col in row])+'|')
print('—'*19)
def shuffle_number(_list):
random.shuffle(_list)
return _list
def check(matrix,i,j,number):
if number in matrix[i]:
return False
if number in [row[j] for row in matrix]:
return False
group_i,group_j = int(i/3),int(j/3)
if number in [matrix[i][j] for i in range(group_i*3,(group_i+1)*3) for j in range(group_j*3,(group_j+1)*3)]:
return False
return True
def build_game(matrix,i,j,number):
if i>8 or j>8:
return matrix
if check(matrix,i,j,number):
_matrix = [[col for col in row] for row in matrix]
_matrix[i][j] = number
next_i,next_j = (i+1,0) if j==8 else (i,j+1)
for _number in shuffle_number(number_list):
#_matrixs.append(build_game(_matrix,next_i,next_j,_number))
__matrix = build_game(_matrix,next_i,next_j,_number)
if __matrix and sum([sum(row) for row in __matrix])==(sum(range(1,10))*9):
return __matrix
#return _matrixs
return None
def give_me_a_game(blank_size=9):
matrix_all = build_game(matrix,0,0,random.choice(number_list))
set_ij = set()
while len(list(set_ij))<blank_size:
set_ij.add(str(random.choice([0,1,2,3,4,5,6,7,8]))+','+str(random.choice([0,1,2,3,4,5,6,7,8])))
matrix_blank = [[col for col in row] for row in matrix_all]
blank_ij = []
for ij in list(set_ij):
i,j = int(ij.split(',')[0]),int(ij.split(',')[1])
blank_ij.append((i,j))
matrix_blank[i][j] = 0
return matrix_all,matrix_blank,blank_ij
number_list = [1,2,3,4,5,6,7,8,9]
matrix = [([0]*9) for i in range(9)]
if __name__ == "__main__":
print_matrix(build_game(matrix,0,0,random.choice(number_list)))
總結(jié)
如果刻意減少代碼的話,實(shí)際應(yīng)該控制在100行以內(nèi),這也充分表達(dá)了python的強(qiáng)大,確實(shí)可以在很短的時(shí)間內(nèi)完成一些看似復(fù)雜的工作,這個(gè)例子供一些同學(xué)上手python個(gè)人覺(jué)得還是不錯(cuò)的,沒(méi)有太復(fù)雜的用法,對(duì)界面開(kāi)發(fā)有一點(diǎn)點(diǎn)了解,對(duì)遞歸有一些理解基本就能完全掌握這份代碼,希望大家玩的開(kāi)心,挑戰(zhàn)一下50個(gè)空格唄,哈哈,反正我沒(méi)通過(guò),太難了。。。。
最后
大家可以到我的Github上看看有沒(méi)有其他需要的東西,目前主要是自己做的機(jī)器學(xué)習(xí)項(xiàng)目、Python各種腳本工具、有意思的小項(xiàng)目以及Follow的大佬、Fork的項(xiàng)目等:
https://github.com/NemoHoHaloAi
到此這篇關(guān)于150行Python代碼實(shí)現(xiàn)帶界面的數(shù)獨(dú)游戲的文章就介紹到這了,更多相關(guān)Python 數(shù)獨(dú)游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
作者:Ho Loong
Github:https://github.com/NemoHoHaloAi
Kaggle:https://www.kaggle.com/holoong9291
相關(guān)文章
使用Python中的Argparse實(shí)現(xiàn)將列表作為命令行參數(shù)傳遞
Argparse?是一個(gè)?Python?庫(kù),用于以用戶友好的方式解析命令行參數(shù),本文我們將討論如何使用?Python?中的?Argparse?庫(kù)將列表作為命令行參數(shù)傳遞,感興趣的可以了解下2023-08-08
Python+Opencv實(shí)現(xiàn)數(shù)字識(shí)別的示例代碼
這篇文章主要介紹了Python+Opencv實(shí)現(xiàn)數(shù)字識(shí)別的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
pytorch中torch.stack()函數(shù)用法解讀
這篇文章主要介紹了pytorch中torch.stack()函數(shù)用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04
Python使用re模塊正則提取字符串中括號(hào)內(nèi)的內(nèi)容示例
這篇文章主要介紹了Python使用re模塊正則提取字符串中括號(hào)內(nèi)的內(nèi)容,結(jié)合實(shí)例形式分析了Python使用re模塊進(jìn)行針對(duì)括號(hào)內(nèi)容的正則匹配操作,并簡(jiǎn)單解釋了相關(guān)修正符與正則語(yǔ)句的用法,需要的朋友可以參考下2018-06-06
Python實(shí)現(xiàn)日志實(shí)時(shí)監(jiān)測(cè)的示例詳解
觀察者模式:是一種行為型設(shè)計(jì)模式。主要關(guān)注的是對(duì)象的責(zé)任,允許你定義一種訂閱機(jī)制,可在對(duì)象事件發(fā)生時(shí)通知多個(gè)"觀察"該對(duì)象的其他對(duì)象。本文將利用觀察者模式實(shí)現(xiàn)日志實(shí)時(shí)監(jiān)測(cè),需要的可以參考一下2022-04-04
VS Code配置Anaconda Python環(huán)境的詳細(xì)教程
在 Visual Studio Code (VS Code) 中可以使用 Anaconda 環(huán)境進(jìn)行 Python 開(kāi)發(fā),可以充分利用 Anaconda 提供的包管理和虛擬環(huán)境功能,同時(shí)享受 VS Code 提供的強(qiáng)大開(kāi)發(fā)工具和調(diào)試功能,本文主要介紹了VS Code配置Anaconda Python環(huán)境的詳細(xì)教程,需要的朋友可以參考下2024-09-09
手把手教你jupyter?notebook更換環(huán)境的方法
在日常使用jupyter-notebook時(shí),可能會(huì)碰到需要切換不同虛擬環(huán)境的場(chǎng)景,下面這篇文章主要給大家介紹了關(guān)于jupyter?notebook更換環(huán)境的方法,需要的朋友可以參考下2023-05-05

