这篇博客主要是为了学习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的话不能达到预期的效果,具体为什么自己去摸索吧。

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

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

python五子棋双人对弈_PyQt5实现五子棋游戏(人机对弈)相关推荐

  1. Python开发双人对战乒乓球小游戏

    文章目录 效果展示 一.环境准备 二.开发步骤 1.创建游戏背景 2.创建球拍 3.让球拍动起来 5.创建乒乓球 6.让乒乓球动起来 7.接球 8.得分 三.完整代码 效果展示 一.环境准备 安装py ...

  2. 利用C++打造双人对战的五子棋游戏,界面新颖「附源码」

    五子棋游戏的历史可谓源远流长,是一款老少皆宜的两人对弈纯策略游戏,讲究的是有攻有守的五子棋技巧,玩法简单易上手,五个棋子连成一线就可获胜.单机五子棋,双人五子棋,好友联机对战模式-你想要的都能在这里找 ...

  3. 基于强化学习开发人机对弈五子棋游戏

    强化学习主要包括状态空间.价值函数.状态转移三个部分,通过状态之间的转移来得到每个状态的价值,强化学习的目标是使得总价值达到最大.注意,与监督学习不同的是,监督学习通常需要大量的样本来获得有价值的信息 ...

  4. 一小时学会C++开发双人对战版五子棋游戏,小白看完也能自己写游戏啦!

    VC++ 双人对战的五子棋游戏 界面新颖附源码,VC.NET源码项目,五子棋-UI美化实例源码.看一看截图吧,是不是看上去很有感觉的五子棋. 程序流程图: 游戏界面如下: 项目结构展示: 部分源码展示 ...

  5. 基于Python的Pygame带背景音乐的五子棋游戏的设计与实现

    源码获取:https://www.bilibili.com/video/BV1Ne4y1g7dC/ 基于Python的Pygame带背景音乐的五子棋游戏的设计与实现 用户需求分析 通过软件功能的分析, ...

  6. C++毕业设计——基于C+++EasyX+剪枝算法的能人机对弈的五子棋游戏设计与实现(毕业论文+程序源码)——五子棋游戏

    基于C+++EasyX+剪枝算法的能人机对弈的五子棋游戏设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于C+++EasyX+剪枝算法的能人机对弈的五子棋游戏设计与实现,文章末尾附有本毕业设 ...

  7. inventor2五子棋游戏apk_联机五子棋手机版下载|联机五子棋游戏下载v1.3.2 安卓版_ 单机手游网...

    单机100手游网下载吧! 联机五子棋手游简介 是可以和好友实时对战的棋类游戏,界面简单清新,操作方便快捷,系统为你匹配棋力相当的对手. 联机五子棋手机版特色 1.五子棋大师增加滑动落子方式,小屏手机操 ...

  8. Scratch3.0——助力新进程序员理解程序(难度案例三、五子棋双人对战-电脑需要AI写不出来)

    Scratch3.0--助力新进程序员理解程序(难度案例三.五子棋双人对战-电脑需要AI写不出来) 前言 一般来说,针对6-18岁的少年儿童开展的编程教育,现在,最常见的形式是线上和线下模式相结合的课 ...

  9. 五子棋双人对战c语言课程设计,五子棋(双人对战) C语言课程设计.doc

    五子棋(双人对战) C语言课程设计 C语言程序设计 题 目 五子棋(双人对战) 指导教师 曹东燕 学生姓名 夏文龙 于文杰 邢健 学 号 201000802032 201000802114 20100 ...

最新文章

  1. Python常用函数与技巧总结(二)
  2. hive的用户和用户权限
  3. Ambari集群移动现有复制到另外地方或更改ip地址,导致各项服务组件上为黄色问号代表心跳丢失的解决方案(图文详解)(博主推荐)...
  4. 小米手机与魅族的PK战结果 说明了什么
  5. linux vim 配置文件(高亮+自动缩进+行号+折叠+优化)
  6. Selenium WebDriver- 使用Frame中的HTML源码内容操作Frame
  7. python中intersection用法_Python Set intersection() 方法
  8. STRUTS学习笔记
  9. svn —— 版本回退
  10. Mybatis Plus
  11. SQL*Loader-704: 内部错误: ulconnect: OCIServerAttach [0] SQL*Loader-704: Internal error ulconnect: OCI
  12. 教新手了解怎么从网络中赚钱
  13. 自己动手写H3C校园网登录客户端(Linux平台版)
  14. ins是什么与Instagram有什么区别
  15. Linux 压缩、解压缩命令
  16. Java 内部类之匿名内部类
  17. Vue:如何制作表格数据分页查询
  18. linux如何查看ip
  19. 传统企业IT如何上云
  20. 晶体谐振器和晶体振荡器有什么区别?

热门文章

  1. SAP同一公司不同工厂间物料转储方案比较
  2. 做总账凭证FB50报错“错误调用功能模块 CHECK_PLANTS_ABROAD_ACTIVE”
  3. 企业计算机能力,以IT企业需求为导向的计算机人才培养模式研究
  4. linux查询关键词上下行_Linux:从文件中搜索关键字并显示行数(cat,grep函数)
  5. Python教程:threading中join与setDaemon的用法及区别讲解
  6. Python教程:shift函数实现数据偏移的方法
  7. Python中创建单例模式的六种方式
  8. Python 列表的应用场景有哪些?你使用对了吗?
  9. python中新式类和经典类
  10. python中使用for循环,while循环,一条命令打印99乘法表