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/

相关文章
相关标签/搜索