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开发WebService系列教程之REST,web.py,eurasia,Django
Jun 30 Python
Python中几种操作字符串的方法的介绍
Apr 09 Python
详细解析Python当中的数据类型和变量
Apr 25 Python
Python中装饰器兼容加括号和不加括号的写法详解
Jul 05 Python
python实现遍历文件夹修改文件后缀
Aug 28 Python
Python实现字典排序、按照list中字典的某个key排序的方法示例
Dec 18 Python
Django数据库类库MySQLdb使用详解
Apr 28 Python
PyTorch中Tensor的维度变换实现
Aug 18 Python
Centos7下源码安装Python3 及shell 脚本自动安装Python3的教程
Mar 07 Python
Python 通过监听端口实现唯一脚本运行方式
May 05 Python
Django的ListView超详细用法(含分页paginate)
May 21 Python
keras load model时出现Missing Layer错误的解决方式
Jun 11 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
咖啡是不是喝了会上瘾?咖啡是必须品吗!
2021/03/04 新手入门
php+javascript的日历控件
2009/11/19 PHP
三种php连接access数据库方法
2013/11/11 PHP
10个对初学者非常有用的PHP技巧
2016/04/06 PHP
PHP函数rtrim()使用中的怪异现象分析
2017/02/24 PHP
PHP的PDO错误与错误处理
2019/01/27 PHP
laravel 5.5 关闭token的3种实现方式
2019/10/24 PHP
页面中iframe相互传值传参
2009/12/13 Javascript
Raphael一个用于在网页中绘制矢量图形的Javascript库
2013/01/08 Javascript
js键盘事件的keyCode
2014/07/29 Javascript
js事件冒泡、事件捕获和阻止默认事件详解
2016/08/04 Javascript
Bootstrap组合上、下拉框简单实现代码
2017/03/06 Javascript
vue.js实现备忘录功能的方法
2017/07/10 Javascript
深入剖析Express cookie-parser中间件实现示例
2018/02/01 Javascript
iview日期控件,双向绑定日期格式的方法
2018/03/15 Javascript
JS实现简单的星期格式转换功能示例
2018/07/23 Javascript
JS使用canvas中的measureText方法测量字体宽度示例
2019/02/02 Javascript
vue封装可复用组件confirm,并绑定在vue原型上的示例
2019/10/31 Javascript
Python编程实现的简单Web服务器示例
2017/06/22 Python
python和opencv实现抠图
2018/07/18 Python
解决PySide+Python子线程更新UI线程的问题
2019/01/11 Python
Python 限制线程的最大数量的方法(Semaphore)
2019/02/22 Python
python向图片里添加文字
2019/11/26 Python
spyder 在控制台(console)执行python文件,debug python程序方式
2020/04/20 Python
CSS3实现网站商品展示效果图
2020/01/18 HTML / CSS
Ryderwear美国官网:澳大利亚高端健身训练装备品牌
2018/04/24 全球购物
美国在线宠物商店:Chewy
2019/01/12 全球购物
过程装备与控制工程专业个人的求职信
2013/12/01 职场文书
小学端午节活动方案
2014/03/13 职场文书
健康教育评估方案
2014/05/25 职场文书
商场周年庆活动方案
2014/08/19 职场文书
工程承包协议书范本
2014/09/29 职场文书
中学生的1000字检讨书
2014/10/11 职场文书
单位综合评价意见
2015/06/05 职场文书
2016年五一劳动节专题校园广播稿
2015/12/17 职场文书
PostgreSQL 插入INSERT、删除DELETE、更新UPDATE、事务transaction
2022/04/12 PostgreSQL