150+行Python程式碼實現帶介面的數獨遊戲

150行程式碼實現圖形化數獨遊戲

Github地址,歡迎各位大佬們fork、star啥的,感謝;

今天閑著沒事幹,以前做過html+js版的數獨,這次做個python版本的,介面由pygame完成,數獨生成由遞歸演算法實現,由shuffle保證每次遊戲都是不一樣的情況,have fun;

功能列表:

  • 圖形化的數獨遊戲;
  • python實現,依賴pygame庫;
  • 隨機生成遊戲,每次運行都不一樣;
  • 數字填入後的正確性判斷以及顏色提示;
  • 顯示剩餘需填入的空格,已經操作的次數;
  • 難度可選,通過修改需要填入的空的數量;

遊戲介面

初始介面

過程中介面

運行方式

python main.py 15  

這裡的15表示需要填入的空格數量為15,理論上這個值越大,難度就越高,大家可以隨機調整,或者設置容易、簡單、困難、地獄等對應不同的值即可,很方便修改;

程式分析

介面部分

這部分很簡單的通過pygame來實現,主要使用了其中的主循環、滑鼠鍵盤監聽、畫矩形線條、字體、顏色控制等,理解起來很容易,對於這部分不太熟悉的同學,這樣理解就好:pygame的主循環中一方面負責接收用戶輸入,一般就是滑鼠和鍵盤,另一方面負責實時更新介面顯示內容

對於介面上各部分內容的繪製的函數封裝

# 繪製背景部分,這裡就是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))  

主循環中對上述函數的調用以及滑鼠鍵盤事件處理

# 主循環,負責監聽滑鼠鍵盤時間,以及刷新介面內容,以及檢查是否贏得了遊戲  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處理,保證隨機性而不是每次都得到同一個合法的數獨數組;

生成過程程式碼

遞歸的一個優勢是通常程式碼都很短,當然閱讀性不強,歡迎大佬們改為循環;

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
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倉庫fork下來直接運行;

main.py:主流程+介面+執行

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:生成數獨數組部分

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)))  

總結

如果刻意減少程式碼的話,實際應該控制在100行以內,這也充分表達了python的強大,確實可以在很短的時間內完成一些看似複雜的工作,這個例子供一些同學上手python個人覺得還是不錯的,沒有太複雜的用法,對介面開發有一點點了解,對遞歸有一些理解基本就能完全掌握這份程式碼,希望大家玩的開心,挑戰一下50個空格唄,哈哈,反正我沒通過,太難了。。。。

最後

大家可以到我的Github上看看有沒有其他需要的東西,目前主要是自己做的機器學習項目、Python各種腳本工具、有意思的小項目以及Follow的大佬、Fork的項目等:
https://github.com/NemoHoHaloAi