PyQt5实现五子棋游戏(人机对弈)

这篇博客主要是为了学习Python和PyQt,因为对棋类游戏比较热衷,所以从规则较简单的五子棋入手,利用PyQt5实现图形界面,做一个可以进行人机对弈的脚本,最后打包成应用程序。AI的算法打算用神经网络来完成,正在苦学TensorFlow中。

本来我以为五子棋规则很简单,不就像小学时候玩的那样,五个棋子连在一起就赢了嘛,但是后来发现事情并没有那么简单,现在的五子棋有禁手这个规则 ,“三三禁手” 、“四四禁手”、“长连禁手”等等,都是为了限制现行一方必胜。我也不是职业的棋手,对吧,所以禁手什么的就不考虑了,弄个简单的成品出来就很满足了。

代码全是边学习边写的,有瑕疵的地方欢迎提出。

第一步,收集素材

主要就是棋子、棋盘的图片,还有下棋的音效

音效与代码一起在最后给出

第二步,五子棋的逻辑类

收集完素材后,不着急界面的编写,先将五子棋的逻辑写好,界面和逻辑要分开,这很重要。

先想想在五子棋的逻辑类里要有哪些东西。

首先是棋盘,棋盘用15*15的数组表示
然后是棋子,黑棋用1表示,白棋用2表示,空白就用0表示
再然后还要获取指定点的坐标,获取指定点的方向等等。
最重要的也是稍微有点难度的部分就是判断输赢。结合网上的方法和我自己的理解,下面贴出我写的代码,仅供参考。

chessboard.py

# ----------------------------------------------------------------------
# 定义棋子类型,输赢情况
# ----------------------------------------------------------------------
EMPTY = 0
BLACK = 1
WHITE = 2

# ----------------------------------------------------------------------
# 定义棋盘类,绘制棋盘的形状,切换先后手,判断输赢等
# ----------------------------------------------------------------------
class ChessBoard(object):
  def __init__(self):
    self.__board = [[EMPTY for n in range(15)] for m in range(15)]
    self.__dir = [[(-1, 0), (1, 0)], [(0, -1), (0, 1)], [(-1, 1), (1, -1)], [(-1, -1), (1, 1)]]
    #        (左   右)   (上    下)   (左下   右上)   (左上   右下)

  def board(self): # 返回数组对象
    return self.__board

  def draw_xy(self, x, y, state): # 获取落子点坐标的状态
    self.__board[x][y] = state

  def get_xy_on_logic_state(self, x, y): # 获取指定点坐标的状态
    return self.__board[x][y]

  def get_next_xy(self, point, direction): # 获取指定点的指定方向的坐标
    x = point[0] + direction[0]
    y = point[1] + direction[1]
    if x < 0 or x >= 15 or y < 0 or y >= 15:
      return False
    else:
      return x, y

  def get_xy_on_direction_state(self, point, direction): # 获取指定点的指定方向的状态
    if point is not False:
      xy = self.get_next_xy(point, direction)
      if xy is not False:
        x, y = xy
        return self.__board[x][y]
    return False

  def anyone_win(self, x, y):
    state = self.get_xy_on_logic_state(x, y) # 当前落下的棋是黑棋还是白棋,它的状态存储在state中
    for directions in self.__dir: # 对米字的4个方向分别检测是否有5子相连的棋
      count = 1 # 初始记录为1,因为刚落下的棋也算
      for direction in directions: # 对落下的棋子的同一条线的两侧都要检测,结果累积
        point = (x, y) # 每次循环前都要刷新
        while True:
          if self.get_xy_on_direction_state(point, direction) == state:
            count += 1
            point = self.get_next_xy(point, direction)
          else:
            break
      if count >= 5:
        return state
    return EMPTY

  def reset(self): # 重置
    self.__board = [[EMPTY for n in range(15)] for m in range(15)]

将上面的代码放在chessboard.py里面就完成了最基本的操作了。

第三步,利用PyQt5实现图形界面

先想好思路。

1.目标是做一个简易的五子棋的界面,主窗口只需要一个Widget就可以了

2.Widget的背景设置为棋盘图片

3.鼠标每点击一次空白区域,该区域就添加一个标签,在标签中插入棋子图片

4.因为是人机对弈,玩家执黑棋,所以可以将鼠标变成黑棋图片(这一点比较复杂,需要重写标签类)

5.整体逻辑是:鼠标点击一次—->换算坐标(UI坐标到棋盘坐标)—->判断坐标是否合理—->黑棋落在棋盘上—->判断是否赢棋—->电脑思考—->电脑下白棋—->判断是否赢棋……

6.因为AI思考需要时间,所以还需要加一个线程,单独让它计算AI的走法

7.一些细节问题: 赢棋和输棋怎么处理(对话框)、和棋怎么办(这个先不考虑)、游戏后期棋子非常多的时候容易眼花,不知道AI走到哪怎么办(加一个指示箭头)、音效怎么插入(用QSound)等等

下面给出整体代码:

gobangGUI.py

from chessboard import ChessBoard
from ai import searcher

WIDTH = 540
HEIGHT = 540
MARGIN = 22
GRID = (WIDTH - 2 * MARGIN) / (15 - 1)
PIECE = 34
EMPTY = 0
BLACK = 1
WHITE = 2

import sys
from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QMessageBox
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap, QIcon, QPalette, QPainter
from PyQt5.QtMultimedia import QSound

# ----------------------------------------------------------------------
# 定义线程类执行AI的算法
# ----------------------------------------------------------------------
class AI(QtCore.QThread):
  finishSignal = QtCore.pyqtSignal(int, int)

  # 构造函数里增加形参
  def __init__(self, board, parent=None):
    super(AI, self).__init__(parent)
    self.board = board

  # 重写 run() 函数
  def run(self):
    self.ai = searcher()
    self.ai.board = self.board
    score, x, y = self.ai.search(2, 2)
    self.finishSignal.emit(x, y)

# ----------------------------------------------------------------------
# 重新定义Label类
# ----------------------------------------------------------------------
class LaBel(QLabel):
  def __init__(self, parent):
    super().__init__(parent)
    self.setMouseTracking(True)

  def enterEvent(self, e):
    e.ignore()

class GoBang(QWidget):
  def __init__(self):
    super().__init__()
    self.initUI()

  def initUI(self):

    self.chessboard = ChessBoard() # 棋盘类

    palette1 = QPalette() # 设置棋盘背景
    palette1.setBrush(self.backgroundRole(), QtGui.QBrush(QtGui.QPixmap('img/chessboard.jpg')))
    self.setPalette(palette1)
    # self.setStyleSheet("board-image:url(img/chessboard.jpg)") # 不知道这为什么不行
    self.setCursor(Qt.PointingHandCursor) # 鼠标变成手指形状
    self.sound_piece = QSound("sound/luozi.wav") # 加载落子音效
    self.sound_win = QSound("sound/win.wav") # 加载胜利音效
    self.sound_defeated = QSound("sound/defeated.wav") # 加载失败音效

    self.resize(WIDTH, HEIGHT) # 固定大小 540*540
    self.setMinimumSize(QtCore.QSize(WIDTH, HEIGHT))
    self.setMaximumSize(QtCore.QSize(WIDTH, HEIGHT))

    self.setWindowTitle("GoBang") # 窗口名称
    self.setWindowIcon(QIcon('img/black.png')) # 窗口图标

    # self.lb1 = QLabel('      ', self)
    # self.lb1.move(20, 10)

    self.black = QPixmap('img/black.png')
    self.white = QPixmap('img/white.png')

    self.piece_now = BLACK # 黑棋先行
    self.my_turn = True # 玩家先行
    self.step = 0 # 步数
    self.x, self.y = 1000, 1000

    self.mouse_point = LaBel(self) # 将鼠标图片改为棋子
    self.mouse_point.setScaledContents(True)
    self.mouse_point.setPixmap(self.black) #加载黑棋
    self.mouse_point.setGeometry(270, 270, PIECE, PIECE)
    self.pieces = [LaBel(self) for i in range(225)] # 新建棋子标签,准备在棋盘上绘制棋子
    for piece in self.pieces:
      piece.setVisible(True) # 图片可视
      piece.setScaledContents(True) #图片大小根据标签大小可变

    self.mouse_point.raise_() # 鼠标始终在最上层
    self.ai_down = True # AI已下棋,主要是为了加锁,当值是False的时候说明AI正在思考,这时候玩家鼠标点击失效,要忽略掉 mousePressEvent

    self.setMouseTracking(True)
    self.show()

  def paintEvent(self, event): # 画出指示箭头
    qp = QPainter()
    qp.begin(self)
    self.drawLines(qp)
    qp.end()

  def mouseMoveEvent(self, e): # 黑色棋子随鼠标移动
    # self.lb1.setText(str(e.x()) + ' ' + str(e.y()))
    self.mouse_point.move(e.x() - 16, e.y() - 16)

  def mousePressEvent(self, e): # 玩家下棋
    if e.button() == Qt.LeftButton and self.ai_down == True:
      x, y = e.x(), e.y() # 鼠标坐标
      i, j = self.coordinate_transform_pixel2map(x, y) # 对应棋盘坐标
      if not i is None and not j is None: # 棋子落在棋盘上,排除边缘
        if self.chessboard.get_xy_on_logic_state(i, j) == EMPTY: # 棋子落在空白处
          self.draw(i, j)
          self.ai_down = False
          board = self.chessboard.board()
          self.AI = AI(board) # 新建线程对象,传入棋盘参数
          self.AI.finishSignal.connect(self.AI_draw) # 结束线程,传出参数
          self.AI.start() # run

  def AI_draw(self, i, j):
    if self.step != 0:
      self.draw(i, j) # AI
      self.x, self.y = self.coordinate_transform_map2pixel(i, j)
    self.ai_down = True
    self.update()

  def draw(self, i, j):
    x, y = self.coordinate_transform_map2pixel(i, j)

    if self.piece_now == BLACK:
      self.pieces[self.step].setPixmap(self.black) # 放置黑色棋子
      self.piece_now = WHITE
      self.chessboard.draw_xy(i, j, BLACK)
    else:
      self.pieces[self.step].setPixmap(self.white) # 放置白色棋子
      self.piece_now = BLACK
      self.chessboard.draw_xy(i, j, WHITE)

    self.pieces[self.step].setGeometry(x, y, PIECE, PIECE) # 画出棋子
    self.sound_piece.play() # 落子音效
    self.step += 1 # 步数+1

    winner = self.chessboard.anyone_win(i, j) # 判断输赢
    if winner != EMPTY:
      self.mouse_point.clear()
      self.gameover(winner)

  def drawLines(self, qp): # 指示AI当前下的棋子
    if self.step != 0:
      pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)
      qp.setPen(pen)
      qp.drawLine(self.x - 5, self.y - 5, self.x + 3, self.y + 3)
      qp.drawLine(self.x + 3, self.y, self.x + 3, self.y + 3)
      qp.drawLine(self.x, self.y + 3, self.x + 3, self.y + 3)

  def coordinate_transform_map2pixel(self, i, j):
    # 从 chessMap 里的逻辑坐标到 UI 上的绘制坐标的转换
    return MARGIN + j * GRID - PIECE / 2, MARGIN + i * GRID - PIECE / 2

  def coordinate_transform_pixel2map(self, x, y):
    # 从 UI 上的绘制坐标到 chessMap 里的逻辑坐标的转换
    i, j = int(round((y - MARGIN) / GRID)), int(round((x - MARGIN) / GRID))
    # 有MAGIN, 排除边缘位置导致 i,j 越界
    if i < 0 or i >= 15 or j < 0 or j >= 15:
      return None, None
    else:
      return i, j

  def gameover(self, winner):
    if winner == BLACK:
      self.sound_win.play()
      reply = QMessageBox.question(self, 'You Win!', 'Continue?',
                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)
    else:
      self.sound_defeated.play()
      reply = QMessageBox.question(self, 'You Lost!', 'Continue?',
                     QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

    if reply == QMessageBox.Yes: # 复位
      self.piece_now = BLACK
      self.mouse_point.setPixmap(self.black)
      self.step = 0
      for piece in self.pieces:
        piece.clear()
      self.chessboard.reset()
      self.update()
    else:
      self.close()

if __name__ == '__main__':
  app = QApplication(sys.argv)
  ex = GoBang()
  sys.exit(app.exec_())

简要说明一下

class AI(QtCore.QThread):
  finishSignal = QtCore.pyqtSignal(int, int)

  # 构造函数里增加形参
  def __init__(self, board, parent=None):
    super(AI, self).__init__(parent)
    self.board = board

  # 重写 run() 函数
  def run(self):
    self.ai = searcher()
    self.ai.board = self.board
    score, x, y = self.ai.search(2, 2)
    self.finishSignal.emit(x, y)

这里加了一个线程执行AI的计算,前面有个 from ai import searcher ,ai还没有写,先从网上找了一个博弈的算法。searcher()就是AI类。该线程传入参数是 board 就是棋盘状态。调用self.ai.search(2, 2),第一个2是博弈树的深度,值越大AI越聪明,但是计算时间也越长。第二个2是说电脑执白棋,如果为1则是黑棋。线程结束后传入参数 x, y 就是AI计算后线程传出的参数。

class LaBel(QLabel):
  def __init__(self, parent):
    super().__init__(parent)
    self.setMouseTracking(True)

  def enterEvent(self, e):
    e.ignore()

重新定义Label类是为了让黑棋图片随着鼠标的移动而移动。如果直接用QLabel的话不能达到预期的效果,具体为什么自己去摸索吧。

最后是所有的脚本代码,在这之后还会继续学习,将脚本打包成可执行文件,并且加入神经网络的算法。

基于PyQt5的五子棋编程(人机对弈)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • pycharm+PyQt5+python最新开发环境配置(踩坑)

    安装工具: Pycharm 专业版2017.3 PyQT5 python3 测试工程文件及所需工具: 1.首先安装Pycharm,先前一篇介绍安装破解版的可以参考:https://www.jb51.net/softs/299378.html 2.新建一个空的python工程,找到setting安装第三方模块PyQT5,点加号,先安PyQT5,再安装pyqt5-tools,后面包含qtdesinger 3.以上模块都安完,设置扩展工具的参数找到setting->tools->external t

  • Python中常用的内置方法

    1.最大值 max(3,4) ##运行结果为4 2.最小值 min(3,4) ##运行结果为3 3.求和 sum(range(1,101)) ##求1-100的和 使用过这个函数求1-100的偶数或者奇数的和更简单 sum(range(1,101,2)) ##1-100之间的奇数和 sum(range(2,101,2)) ##1-100之间的偶数和 4.枚举 返回索引值和对应的value值 for i,v in enumerate('hello'): print(i,v) 5.zip 可以使两个

  • Python魔法方法详解

    据说,Python 的对象天生拥有一些神奇的方法,它们总被双下划线所包围,他们是面向对象的 Python 的一切. 他们是可以给你的类增加魔力的特殊方法,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而这一切都是自动发生的. Python 的魔术方法非常强大,然而随之而来的则是责任.了解正确的方法去使用非常重要! 魔法方法 含义 基本的魔法方法 __new__(cls[, ...]) new 是在一个对象实例化的时

  • PyQt5内嵌浏览器注入JavaScript脚本实现自动化操作的代码实例

    概要 应同学邀请,演示如何使用 PyQt5 内嵌浏览器浏览网页,并注入 Javascript 脚本实现自动化操作. 下面测试的是一个廉价机票预订网站(http://www.flyscoot.com/),关键点如下 使用 QWebEngineView 加载网页,并显示进度. 在默认配置(QWebEngineProfile)中植入 Javascript 内容,这样脚本会在所有打开的网页中执行,不论跳转到哪个网址. Javascript 脚本使用网址中的路径名,判断当前网页位置,从而决定执行哪种操作.

  • PyQt5实现类似别踩白块游戏

    本文实例为大家分享了PyQt5实现类似别踩白块游戏的具体代码,供大家参考,具体内容如下 #引入可能用到的库 from PyQt5.QtWidgets import (QWidget, QApplication,QPushButton,QMessageBox,QLabel,QDesktopWidget,QMainWindow) from PyQt5.QtCore import Qt,QRect,QSize,QPoint,QTimer from PyQt5.QtGui import QPainter

  • pycharm配置pyqt5-tools开发环境的方法步骤

    本文介绍使用python+pyqt5开发桌面程序的一个可视化UI视图布局 一.环境包的安装 1.如果还不知道虚拟环境的可以参考,或者直接使用pipenv 2.安装pyqt5 pip3 install pyqt5 3.安装pyqt5-tools(注意目前只支持在window系统下,如果你是mac电脑请自行安装虚拟机) pip3 install pyqt5-tools 4.使用pip3 list查看安装是否成功 二.在pycharm中配置pyqt5-tools工具 在pycharm编辑器中主要配置有

  • Python函数中不定长参数的写法

    1.不定长参数的写法,用 *变量名 表示 2.不定长参数累加 3.不定长参数,使用**c接受m=23,n=56的值: 传参时,a必写,b.c可以缺省 def fun(a, b, *args): print(a) print(b) print(args) print("="*30) ret = a + b for i in args: ret += i return ret print(fun(1,2,3,4)) 结果: 1 2 (3, 4) ======================

  • Python判断变量名是否合法的方法示例

    问题: 变量名是否合法: 1.变量名可以由字母,数字或者下划线组成 2.变量名只能以字母或者下划线开头 s = 'hello@' 判断变量名的第一个元素是否为字母或者下划线 s[0] 如果第一个元素符合条件,判断除了第一个元素之外的其他元素s[1:] 思路: 1.变量名的第一个字符是否为字母或下划线 2.如果是,继续判断 --> 4 3.如果不是,报错 4.依次判断除了第一个字符之外的其他字符 5.判断是否为字母数字或者下划线 while True: s = input('变量名:') #定义退

  • python+pyqt5实现24点小游戏

    本文实例为大家分享了python实现24点游戏的具体代码,供大家参考,具体内容如下 描述:一副牌中A.J.Q.K可以当成是1.11.12.13.任意抽取4张牌,用加.减.乘.除(可加括号)把牌面上的数算成24.每张牌对应的数字必须用一次且只能用一次.在规定时间内输入算式,输入正确加十分,输入错误生命值减一,点击确定提交并进入下一题,点击清空可清空算式.点击开始游戏进入游戏,可重新开始游戏. from PyQt5 import QtCore, QtWidgets from PyQt5.QtWidg

  • PyQt5实现五子棋游戏(人机对弈)

    这篇博客主要是为了学习Python和PyQt,因为对棋类游戏比较热衷,所以从规则较简单的五子棋入手,利用PyQt5实现图形界面,做一个可以进行人机对弈的脚本,最后打包成应用程序.AI的算法打算用神经网络来完成,正在苦学TensorFlow中. 本来我以为五子棋规则很简单,不就像小学时候玩的那样,五个棋子连在一起就赢了嘛,但是后来发现事情并没有那么简单,现在的五子棋有禁手这个规则 ,"三三禁手" ."四四禁手"."长连禁手"等等,都是为了限制现行一

  • C语言实现井字棋游戏(人机对弈)

    井字棋游戏:即三子棋,英文名叫Tic-Tac-Tic,是一种在3*3格子上进行的连珠游戏,和五子棋比较类似,由于棋盘一般不画边线框,格线排成井字故得名. 题目分析 : 要完成该游戏的编写,我们需要先分析出完成整个游戏过程都需要干什么? 1.首先,需要定义出一个3*3的棋盘,根据相关知识,我们可以以二维数组的方式将棋盘表示出来: 2.棋盘定义出来后,需要将棋盘初始化,将3*3二维数组的每一个位置初始化为‘ ’(空格): 3.有了棋盘,我们就可以开始进行下棋了,首先要确定是玩家先下还是电脑先下.在这

  • 微信小程序五子棋游戏的棋盘,重置,对弈实现方法【附demo源码下载】

    本文实例讲述了微信小程序五子棋游戏的棋盘,重置,对弈实现方法.分享给大家供大家参考,具体如下: DEMO下载 五子棋对弈.悔棋DEMO 效果图 分析 1. 采用微信小程序的canvas制作五子棋: 2. 确定棋盘大小及格数: 3. 绘制棋盘--通过棋盘宽高和格数计算间距,同时保存坐标点: 4. 黑方和白方下子--定义一个布尔变量代表各自的身份: 5. 重置棋盘--重新开始: 6. 通过判断当前棋手,悔棋时进行改变. 绘制棋盘 drawLine(arr){ arr.forEach(current

  • JS+canvas实现的五子棋游戏【人机大战版】

    本文实例讲述了JS+canvas实现的五子棋游戏.分享给大家供大家参考,具体如下: 运行效果图: html代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>五子棋</title> <link rel="stylesheet" type="text/css" href="css

  • python实现人机对战的五子棋游戏

    本文实例为大家分享了python实现五子棋游戏的具体代码,供大家参考,具体内容如下 checkerboard.py from collections import namedtuple Chessman = namedtuple('Chessman', 'Name Value Color') Point = namedtuple('Point', 'X Y') BLACK_CHESSMAN = Chessman('黑子', 1, (45, 45, 45)) WHITE_CHESSMAN = Ch

  • Java实现简单的五子棋游戏示例代码

    目录 项目结构 核心代码 ArrComparator.java类 ChessMap.java类 ChessPanel.java类 效果图展示 项目结构 这个是在网上找的资源,出处记不得了,记录一下.程序的总体结构,很简单的: 核心代码 代码如下: ArrComparator.java类 import java.util.Comparator; /** * 排序 Comparator */ class ArrComparator implements Comparator<Object> { i

  • 原生JS+Canvas实现五子棋游戏实例

    一.功能模块 先看下现在做完的效果: 线上体验:https://wj704.github.io/five_game.html 主要功能模块为: 1.人机对战功能 2.悔棋功能 3.撤销悔棋功能 二.代码详解 2.1 人机对战功能实现 从效果图可以看到,棋盘的横竖可以放的位置为15*15,通过canvas画棋盘: //绘画棋盘 var drawChessBoard = function(){ for(var i = 0; i < 15; i++){ context.moveTo(15 + i *

  • 使用python实现简单五子棋游戏

    用python实现五子棋简单人机模式的练习过程,供大家参考,具体内容如下 第一次写博客,我尽力把它写好. 最近在初学python,今天就用自己的一些粗浅理解,来记录一下这几天的python简单人机五子棋游戏的练习,下面是实现过程的理解(是在cmd中运行的): 主要流程: *重点内容* - 首先是模块及类的划分 - 棋子类和棋盘类的方法 - 对策略类里的功能进行细分,调用棋子类和棋盘类 - 写出判断输赢的方法 - 用main函数进行整个游戏进度的控制 模块及类的划分 类的划分涉及到了面向对象的内容

  • H5+C3+JS实现双人对战五子棋游戏(UI篇)

    本篇文章实现的是双人对战模式,主要是实现除人机AI算法和判断输赢之外的其他功能,下一篇将会发布AI 框架搭建 <!Doctype html> <html> <head> <!-- 百度爬虫优化 --> <meta http-equiv="author" content="成兮,缘分五月" /> <meta http-equiv="Keywords" cotent="五子棋

  • 如何利用pygame实现简单的五子棋游戏

    前言 写程序已经丢掉很长一段时间了,最近觉得完全把技术丢掉可能是个死路,还是应该捡起来,所以打算借CSDN来记录学习过程, 由于以前没事的时候断断续续学习过python和用flask框架写过点web,所以第一步想捡起python,但是,单纯学习python有点枯燥,正好看到pygame,感觉还挺简单,所以想先写个小游戏练练手. 准备 python基础相关准备: python基础知识准备,廖雪峰的python基础知识简单好学,熟悉python基本的语法, 链接地址 pygame的基础知识,参考目光

随机推荐