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绘制简单折线图代码示例
Dec 19 Python
Python异常处理操作实例详解
May 10 Python
Python查看微信撤回消息代码
Jun 07 Python
python学习之hook钩子的原理和使用
Oct 25 Python
使用CodeMirror实现Python3在线编辑器的示例代码
Jan 14 Python
python实现递归查找某个路径下所有文件中的中文字符
Aug 31 Python
python读取mysql数据绘制条形图
Mar 25 Python
Window版下在Jupyter中编写TensorFlow的环境搭建
Apr 10 Python
Pytorch数据拼接与拆分操作实现图解
Apr 30 Python
python高级特性简介
Aug 13 Python
Python .py生成.pyd文件并打包.exe 的注意事项说明
Mar 04 Python
pytorch中的model=model.to(device)使用说明
May 24 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 is_file 判断给定文件名是否为一个正常的文件
2010/05/10 PHP
PHP5中使用PDO连接数据库的方法
2010/08/01 PHP
php重定向的三种方法分享
2012/02/22 PHP
提升PHP性能的21种方法介绍
2013/06/25 PHP
PHP图像处理之imagecreate、imagedestroy函数介绍
2014/11/19 PHP
PHP的Json中文处理解决方案
2016/09/29 PHP
jquery ui dialog ie8出现滚动条的解决方法
2010/12/06 Javascript
JS中prototype关键字的功能介绍及使用示例
2013/07/21 Javascript
jQuery 借助插件Lavalamp实现导航条动态美化效果
2013/09/27 Javascript
一个判断抢购时间是否到达的简单的js函数
2014/06/23 Javascript
js简单工厂模式用法实例
2015/06/30 Javascript
JavaScript截断字符串的方法
2015/07/15 Javascript
JS给Array添加是否包含字符串的简单方法
2016/10/29 Javascript
Vue概念及常见命令介绍(1)
2016/12/08 Javascript
angularjs实现下拉列表的选中事件示例
2017/03/03 Javascript
bootstrap-table组合表头的实现方法
2017/09/07 Javascript
使用webpack-dev-server处理跨域请求的方法
2018/04/18 Javascript
vue2过滤器模糊查询方法
2018/09/16 Javascript
如何使用Node.js爬取任意网页资源并输出PDF文件到本地
2019/06/17 Javascript
JS数组方法join()用法实例分析
2020/01/18 Javascript
使用python对excle和json互相转换的示例
2018/10/23 Python
python 批量解压压缩文件的实例代码
2019/06/27 Python
使用Python和Scribus创建一个RGB立方体的方法
2019/07/17 Python
pygame实现贪吃蛇游戏(上)
2019/10/29 Python
python 获取当前目录下的文件目录和文件名实例代码详解
2020/03/10 Python
Python实现计算图像RGB均值方式
2020/06/04 Python
Django Form常用功能及代码示例
2020/10/13 Python
描述RIP和OSPF区别以及特点
2015/01/17 面试题
秋季运动会通讯稿
2014/01/24 职场文书
品质管理部岗位职责范文
2014/03/01 职场文书
政府信息公开实施方案
2014/05/09 职场文书
我的中国梦演讲稿高中篇
2014/08/19 职场文书
委托书的写法
2014/08/30 职场文书
催款律师函范文
2015/05/27 职场文书
有关西游记的读书笔记
2015/06/25 职场文书
2015领导干部廉洁自律工作总结
2015/07/23 职场文书