PyQt寫的五子棋

技術路線

GUI的實現

  1. 使用PyQt技術做爲基礎。PyQt是一個支持多平臺的客戶端開發SDK,使用它實現的客戶端能夠運行在目前幾乎全部主流平臺之上。
  2. 使用PyQt,Qt設計器實現UI,經過pyuic4 -x -o main_page.py untitled.ui命令將設計好的xml文件轉換爲python程序。
  3. 繼承純UI的mainWindow類,重寫setupUI方法,在UI創建完成以後進行控件的信號-槽的綁定。
  4. 在子類中完成各種事件的響應。

棋盤的繪製

  1. PyQt提供了豐富的控件,如TableView,雖然和棋盤有類似之處,但提供的API過於死板。因此我決定用Widget繪製一個棋盤。
  2. 創建一個QiPan類,繼承於QtGui.QWidget 實現它的paintEvent接口,在此接口中可進行Widget的繪製。
  3. 將QiPan的長寬和棋盤格子數進行計算,繪製橫縱的直線,完成棋盤的繪製。
  4. 將QiPan類實例化,獲得的對象在主窗口setupUI的時候添加到上一層的容器內。

落子位置的斷定

  1. 由於用戶不可能每次剛好點中橫縱線的交點處,因此要對用戶點擊的位置的座標進行斷定,判斷用戶想要落子到哪一個點
  2. 如圖,我將每一個點的有效範圍擴充至它到鄰點的1/2組成的矩形。如圖陰影區域: 
    s
  3. 如此,棋盤中每一個點都能映射到對應的交點上。

勝利條件斷定算法

  1. 由於每次落棋都須要斷定當前狀態是否有一方勝利,因此勝利條件的斷定應當在每次落棋以後進行。
  2. 又由於每次落棋時,落子的一方只有(贏/還沒贏)兩種狀態,因此只需判斷落子的一方是否贏得比賽,另外一方的棋子無需判斷。因此只需從當前落子的棋的4條線上進行斷定是否連珠便可。
  3. 4條線分爲8個方向,兩兩對稱。由於狀況不少,因此我構造了兩個數組,dx和dy,如圖:python


  4. 將數組從[0,3][4,7]分爲兩部分,分別對應四條線每條線的兩個方向。算法

  5. 如此只需循環遍歷8個方向,每一個方向出發的4個棋子子,統計這些棋子有多少和中心棋子顏色相同,每一個方向的除以4取餘數的存儲計算結果。設計模式

  6. 若是某個方向上第x個棋子已經與中心棋子不一樣,當即跳出循環,再也不繼續遍歷該方向。數組

  7. 核心判斷代碼:app

def check_line(self, x, y, map, width, height): tag = map[x][y] dx = [+1, -1, 0, -1, -1, + 1, 0, +1] dy = [-1, 0, -1, -1, +1, 0, +1, +1] count = [0] * 4 for k in xrange(0, 8): for i in xrange(1, 5): if 0 < x + dx[k] * i < width and 0 < y + dy[k] * i < height and map[x + dx[k] * i][y + dy[k] * i] == tag: count[k % 4] += 1 else: break for k in count: if k == 4: return True return False 

悔棋功能的實現

  1. 創建兩個棧,分別用做黑棋和白棋的棋子狀態,在每次落子時push進該棋子的狀態。
  2. 當用戶點擊悔棋按鈕時,從相應的棧中pop出來,恢復上一狀態。

工程結構


採用MVC的模式函數

  • build: 生成可執行文件目錄
  • controller:UI的相關控制,至關於C的做用工具

    1. MyMainWindow.py:繼承Ui_MainWindow類,主要控制主窗口
    2. QiPan.py:棋盤類,繼承於QWidget,用於棋盤的相關控制
  • libs: 各項斷定條件的算法,充當了一部分Model的做用ui

    1. AI.py:AI類,用來斷定輸贏條件
    2. BitmapTools.py:封裝了對落子在棋盤map中的位置的斷定方法
  • resource:圖片資源文件夾spa

  • ui:UI相關代碼,View的做用設計

    1. main_page.py:UI界面的python代碼
    2. untitled.ui:UI界面的xml代碼

阿薩德 - app.py:主函數入口,用於啓動MainWindow

設計模式

單例模式

AI類和BitMapTools都採用單例模式: 好處在於能夠只用實例化一次,佔用資源少。在棋盤初始化的時候就加載這兩個工具類,將它們的實例保存爲成員,之後能夠頻繁地使用這個實例。

class BitMapTools: tools = None @staticmethod def getinstance(): if BitMapTools.tools is None: BitMapTools.tools = BitMapTools() return BitMapTools.tools 

觀察者模式

繼承UI相關的類,並重寫setupUI,在其中訂閱各種點擊事件,在相關的函數中處理這些事件發生以後的工做。

self.father.restart_btn.clicked.connect(self.restart) 

這樣每個事件對應一個函數,使得結構更加清晰。

 

from: PyQt寫的五子棋

blog: http://cyhhao.zhusun.in/

相關文章
相關標籤/搜索