150+行Python程式碼實現帶介面的數獨遊戲
阿新 • • 發佈:2020-04-03
# 150行程式碼實現圖形化數獨遊戲
[Github地址](https://github.com/NemoHoHaloAi/Sudoku--),歡迎各位大佬們fork、star啥的,感謝;
今天閒著沒事幹,以前做過html+js版的數獨,這次做個python版本的,介面由pygame完成,數獨生成由遞迴演算法實現,由shuffle保證每次遊戲都是不一樣的情況,have fun;
功能列表:
- 圖形化的數獨遊戲;
- python實現,依賴pygame庫;
- 隨機生成遊戲,每次執行都不一樣;
- 數字填入後的正確性判斷以及顏色提示;
- 顯示剩餘需填入的空格,已經操作的次數;
- 難度可選,通過修改需要填入的空的數量;
## 遊戲介面
### 初始介面
![](https://img2020.cnblogs.com/blog/666842/202004/666842-20200403171058376-562210167.png)
### 過程中介面
![](https://img2020.cnblogs.com/blog/666842/202004/666842-20200403171157975-1073503274.png)
## 執行方式
```python
python main.py 15
```
這裡的15表示需要填入的空格數量為15,理論上這個值越大,難度就越高,大家可以隨機調整,或者設定容易、簡單、困難、地獄等對應不同的值即可,很方便修改;
## 程式分析
### 介面部分
這部分很簡單的通過pygame來實現,主要使用了其中的主迴圈、滑鼠鍵盤監聽、畫矩形線條、字型、顏色控制等,理解起來很容易,對於這部分不太熟悉的同學,這樣理解就好:**pygame的主迴圈中一方面負責接收使用者輸入,一般就是滑鼠和鍵盤,另一方面負責實時更新介面顯示內容**;
#### 對於介面上各部分內容的繪製的函式封裝
```python
# 繪製背景部分,這裡就是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)
# 將使用者選中的各自背景改為藍色塊表示選中
def draw_choose():
pygame.draw.rect(screen,COLORS['blue'],(cur_j*100+5,cur_i*100+5,100-10,100-10),0)
# 繪製九宮格中的數字,包括本來就有的,以及使用者填入的,本來就在的用灰色,使用者填入的如何合法則為綠色,否則為紅色,是一種提示
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))
```
#### 主迴圈中對上述函式的呼叫以及滑鼠鍵盤事件處理
```python
# 主迴圈,負責監聽滑鼠鍵盤時間,以及重新整理介面內容,以及檢查是否贏得了遊戲
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()
```
### 生成表示數獨的二維陣列
相對於介面部分,這部分在邏輯上要難一些,思路以遞迴為核心,輔以隨機性,得到一個每次生成都不一致的數獨遊戲,生成思路簡單描述如下:
1. 遍歷每個空格,填入目前為止合法的數字;
2. 如果有數字可以填入,則繼續向下一個空格;
3. 如果沒有數字可以填入,表示之前的數字有問題,則結束遞迴;
4. 當遞迴到最後一個格子的下一個時,表示已經生成完畢,返回即可;
5. 這個過程中對1~9這九個數字的遍歷數字會經過shuffle處理,保證隨機性而不是每次都得到同一個合法的數獨陣列;
#### 生成過程程式碼
遞迴的一個優勢是通常程式碼都很短,當然閱讀性不強,歡迎大佬們改為迴圈;
```python
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
```
#### 隨機覆蓋數獨陣列中的N個位置
- matrix_all表示整個數獨陣列
- matrix_blank表示部分被替換為0的用於顯示的陣列
- blank_ij表示被覆蓋位置的i和j
```python
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)) 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))