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


Posted in Python onMarch 25, 2020

本文代码基于 python3.6 和 pygame1.9.4。

五子棋比起我之前写的几款游戏来说,难度提高了不少。如果是人与人对战,那么,电脑只需要判断是否赢了就可以。如果是人机对战,那你还得让电脑知道怎么下。

我们先从简单的问题来看。

开端

画棋盘

首先肯定是要画出棋盘来,用 pygame 画出一个 19 × 19 或 15 × 15 的棋盘并不是什么难事,这在之前的文章中已经多次用到,就不赘述了。

画棋子

需要说一下的是画棋子,因为没找到什么合适的棋子图片,所以只要自己来画棋子。
我们用 pygame.draw.circle 画出来的圆形是这样的:

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

锯齿状十分明显,pygame.draw 中有画抗锯齿直线的函数 aaline,但是并没有 aacircle 这样的函数来画一个抗锯齿的圆。

这里就需要用到 pygame.gfxdraw 啦。pygame.gfxdraw 目前还仅是实验版本,这意味着这个 API  可能会在以后的 pygame 版本中发生变化或消失。

要绘制抗锯齿和填充形状,请首先使用函数的aa *版本,然后使用填充版本。例如:

col = (255, 0, 0)
surf.fill((255, 255, 255))
pygame.gfxdraw.aacircle(surf, x, y, 30, col)
pygame.gfxdraw.filled_circle(surf, x, y, 30, col)

我们用这个方法在棋盘上画一个棋子试试看。

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

可以看到效果已明显改善。

落子

落子需要判断鼠标事件,当鼠标左键点击,获取鼠标点击的位置,然后根据棋盘的位置,计算出棋子落在棋盘的位置。

while True:
 for event in pygame.event.get():
 if event.type == QUIT:
 sys.exit()
 elif event.type == MOUSEBUTTONDOWN:
 pressed_array = pygame.mouse.get_pressed()
 if pressed_array[0]: # 鼠标左键点击
  mouse_pos = pygame.mouse.get_pos()
  click_point = _get_clickpoint(mouse_pos)

胜利判定

当一子落下,如何判定是否胜利?

可以肯定的是,当某一子落下的时候,如果出现了 5 连,那么落下的这颗子必定在这条 5 连线上。那么这个问题就可以简化了,我们无需全盘扫描,只需要在落子位置上横竖撇捺扫描一下,判断是否出现 5 连即可。

我们定义一个棋盘类,类中实例化一个 19 × 19 的二维数组,初始值皆为 0,表示空,用 1 表示黑子,2 表示白子。这个类对外提供一个落子方法 drop,接收参数落子方和落子坐标,如果落子后胜利,则返回胜利者,否则返回 None。

Chessman = namedtuple('Chessman', 'Name Value Color')
Point = namedtuple('Point', 'X Y')
 
BLACK_CHESSMAN = Chessman('黑子', 1, (45, 45, 45))
WHITE_CHESSMAN = Chessman('白子', 2, (219, 219, 219))
 
offset = [(1, 0), (0, 1), (1, 1), (1, -1)]
 
 
class Checkerboard:
 def __init__(self, line_points):
 self._line_points = line_points
 self._checkerboard = [[0] * line_points for _ in range(line_points)]
 
 def _get_checkerboard(self):
 return self._checkerboard
 
 checkerboard = property(_get_checkerboard)
 
 # 判断是否可落子
 def can_drop(self, point):
 return self._checkerboard[point.Y][point.X] == 0
 
 def drop(self, chessman, point):
 """
 落子
 :param chessman: 黑子/白子
 :param point:落子位置
 :return:若该子落下之后即可获胜,则返回获胜方,否则返回 None
 """
 print(f'{chessman.Name} ({point.X}, {point.Y})')
 self._checkerboard[point.Y][point.X] = chessman.Value
 
 if self._win(point):
 print(f'{chessman.Name}获胜')
 return chessman
 
 # 判断是否赢了
 def _win(self, point):
 cur_value = self._checkerboard[point.Y][point.X]
 for os in offset:
 if self._get_count_on_direction(point, cur_value, os[0], os[1]):
 return True
 
 def _get_count_on_direction(self, point, value, x_offset, y_offset):
 count = 1
 for step in range(1, 5):
 x = point.X + step * x_offset
 y = point.Y + step * y_offset
 if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:
 count += 1
 else:
 break
 for step in range(1, 5):
 x = point.X - step * x_offset
 y = point.Y - step * y_offset
 if 0 <= x < self._line_points and 0 <= y < self._line_points and self._checkerboard[y][x] == value:
 count += 1
 else:
 break
 
 return count >= 5

这里我定义了一个偏移量,我们一共要计算横竖撇捺 4 条线,任意一条线出现 5 连就算获胜。计算方法实际上是一样的,只是方向不同,所以定义一个偏移量数组,不同的偏移量表示不同的方向,这样就可以利用循环来实现了,节省了很多代码。

电脑落子

这就是全篇的重头戏了,要怎么教电脑下五子棋。
首先声明,我用的是相对传统的方式,不是深度学习。

五子棋就是要实现 5 连,所以,一开始,我的想法是:将所有连线保存在一个数组中,落子的时候选择最长的连线落子。但这样有个问题解决不掉,如何让电脑识别“三三”呢?

后来网上看到篇文章,使用的方法是:遍历棋盘上的空位,计算每一个位置其横竖撇捺 8 个方向上是否有己方的子,有一个就加 10 分,最后选得分最高的位置落子。

这样不太严谨,写出来的电脑估计水平很菜,但是这个思路却是对的,落子就是要找到最值得的地方,那么我们干脆对每一个可落子的地方来做一个评估,选出最优解。

这里我们需要了解一下五子棋的几种基本棋形:连五,活四,冲四,活三,眠三,活二,眠二。

连五

顾名思义,五颗同色棋子连在一起,赢了。

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

活四

四颗同色棋子连在一起,并且左右两边都没有对方棋子阻挡,有两个连五点。

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

冲四

四颗同色棋子连在一起,并且一边有对方棋子阻挡,或者四颗棋子不是连的,当中有个空挡,这时只有一个连五点。

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

活三、跳活三

活三:三颗同色棋子连在一起。

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

跳活三:中间隔了一个空格的活三。

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

眠三

只能够形成冲四的三,无外乎两种情况,一是一边被挡住了,一是当中有 2 个空格。(其实我在代码中仅考虑了第一种情况,即便形成冲四,也不是什么危险局面。)

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

活二和眠二

活二,能够形成活三的二;眠二,能够形成眠三的二。这里就不放图了,参考活三眠三。

打分机制

理解了这些棋形,那么按我们之前的思路,就是如何打分了。

  • 首先,连五肯定是不存在的,出现连五胜负已分,所以只要棋局还在进行中,就不会出现连五。那么,什么优先级最高?自然就是活四了。
  • 其次是对方的“四”,对方活四,你防不防都一样输了,对方冲四,你就必须防守。
  • 再次是我方的活三或冲四,活三跟冲四其实是一个级别的,对方必须防守。
  • 再次是对方的活三或冲四。

以此类推下去。我们可以总结一点规律:

  • 相同的棋形,我方优于对方。
  • 冲四跟活三一个级别,眠三跟活二一个级别。
  • 如果中间有空格的话,肯定是要比没空格的略微低级一点,但不至于降级。

基本逻辑就是这样,这一块的代码我写得也不好,整个判断写了100多行,就不贴代码了,大家可以直接下源码看。

五子棋执黑是必赢的,代码中,玩家就是执黑先手,电脑执白后手,所以,下的好是完全可以赢电脑的,不过一个小小失误也很可能被电脑翻盘。

更多关于python游戏的精彩文章请点击查看以下专题:

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

Python 相关文章推荐
python获取android设备的GPS信息脚本分享
Mar 06 Python
为Python程序添加图形化界面的教程
Apr 29 Python
Python实现的redis分布式锁功能示例
May 29 Python
Sanic框架请求与响应实例分析
Jul 16 Python
python使用xlrd和xlwt读写Excel文件的实例代码
Sep 05 Python
只需7行Python代码玩转微信自动聊天
Jan 27 Python
python求最大值最小值方法总结
Jun 25 Python
python颜色随机生成器的实例代码
Jan 10 Python
python GUI库图形界面开发之PyQt5中QWebEngineView内嵌网页与Python的数据交互传参详细方法实例
Feb 26 Python
python如何查看安装了的模块
Jun 23 Python
为2021年的第一场雪锦上添花:用matplotlib绘制雪花和雪景
Jan 05 Python
如何利用python和DOS获取wifi密码
Mar 31 Python
PyCharm 创建指定版本的 Django(超详图解教程)
Jun 18 #Python
使用python实现简单五子棋游戏
Jun 18 #Python
Pycharm新手教程(只需要看这篇就够了)
Jun 18 #Python
pyqt5 获取显示器的分辨率的方法
Jun 18 #Python
PyQt5 实现字体大小自适应分辨率的方法
Jun 18 #Python
Python3网络爬虫中的requests高级用法详解
Jun 18 #Python
在Qt5和PyQt5中设置支持高分辨率屏幕自适应的方法
Jun 18 #Python
You might like
php中filter函数验证、过滤用户输入的数据
2014/01/13 PHP
源码分析 Laravel 重复执行同一个队列任务的原因
2017/12/25 PHP
PHP操作Postgresql封装类与应用完整实例
2018/04/24 PHP
Jquery Ajax学习实例7 Ajax所有过程事件分析示例
2010/03/23 Javascript
JQuery.ajax传递中文参数的解决方法 推荐
2011/03/28 Javascript
jquery插件制作 提示框插件实现代码
2012/08/17 Javascript
jquery中EasyUI实现异步树
2015/03/01 Javascript
Juery解决tablesorter中文排序和字符范围的方法
2015/05/06 Javascript
详解js图片轮播效果实现原理
2015/12/17 Javascript
微信小程序 animation API详解及实例代码
2016/10/08 Javascript
canvas知识总结
2017/01/25 Javascript
jQuery EasyUI Accordion可伸缩面板组件使用详解
2017/02/28 Javascript
nodejs入门教程四:URL相关模块用法分析
2017/04/24 NodeJs
Vue基于NUXT的SSR详解
2017/10/24 Javascript
vue利用axios来完成数据的交互
2018/03/23 Javascript
Vue实现6位数密码效果
2018/08/18 Javascript
JS函数内部属性之arguments和this实例解析
2018/10/07 Javascript
vue项目创建并引入饿了么elementUI组件的步骤
2019/04/11 Javascript
python使用paramiko模块实现ssh远程登陆上传文件并执行
2014/01/27 Python
python通过加号运算符操作列表的方法
2015/07/28 Python
Python中字典的基础知识归纳小结
2015/08/19 Python
详解python发送各类邮件的主要方法
2016/12/22 Python
Python 3.x 连接数据库示例(pymysql 方式)
2017/01/19 Python
Python之dict(或对象)与json之间的互相转化实例
2018/06/05 Python
Python交互式图形编程的实现
2019/07/25 Python
Python编程学习之如何判断3个数的大小
2019/08/07 Python
python3的数据类型及数据类型转换实例详解
2019/08/20 Python
Python中if有多个条件处理方法
2020/02/26 Python
美国隐形眼镜零售商:LensPure
2019/03/10 全球购物
Vision Direct比利时:在线订购隐形眼镜
2019/08/27 全球购物
一份软件工程师的面试试题
2016/02/01 面试题
大学毕业寄语大全
2014/04/10 职场文书
销售经理岗位职责范本
2015/04/02 职场文书
老乡会致辞
2015/07/28 职场文书
2016年清明节期间群众祭祀活动工作总结
2016/04/01 职场文书
2019银行员工个人工作自我鉴定
2019/06/27 职场文书