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和IPython中使用Docker
Apr 28 Python
python关键字and和or用法实例
May 28 Python
基于Python实现对PDF文件的OCR识别
Aug 05 Python
详解Python下ftp上传文件linux服务器
Jun 21 Python
CentOS6.9 Python环境配置(python2.7、pip、virtualenv)
May 06 Python
python读取并定位excel数据坐标系详解
Jun 26 Python
python写程序统计词频的方法
Jul 29 Python
python绘制随机网络图形示例
Nov 21 Python
CentOS 7如何实现定时执行python脚本
Jun 24 Python
python利用google翻译方法实例(翻译字幕文件)
Sep 21 Python
python statsmodel的使用
Dec 21 Python
浅谈tf.train.Saver()与tf.train.import_meta_graph的要点
May 26 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
正则表达式语法
2006/10/09 Javascript
利用php来自动调用不同服务器上的flash
2006/10/09 PHP
php中怎么搜索相关联数组键值及获取之
2013/10/17 PHP
golang与php实现计算两个经纬度之间距离的方法
2016/07/22 PHP
Laravel5.1 框架表单验证操作实例详解
2020/01/07 PHP
javascript 语法基础 想学习js的朋友可以看看
2009/12/16 Javascript
actionscript与javascript的区别
2011/05/25 Javascript
使用Promise解决多层异步调用的简单学习心得
2016/05/17 Javascript
第八篇Bootstrap下拉菜单实例代码
2016/06/21 Javascript
巧用jQuery选择器提高写表单效率的方法
2016/08/19 Javascript
Vue 过渡实现轮播图效果
2017/03/27 Javascript
自带气泡提示的vue校验插件(vue-verify-pop)
2017/04/07 Javascript
在微信小程序中渲染HTML内容的方法示例
2018/09/28 Javascript
vue动态配置模板 'component is'代码
2019/07/04 Javascript
详解vuex数据传输的两种方式及this.$store undefined的解决办法
2019/08/26 Javascript
uni-app使用微信小程序云函数的步骤示例
2020/05/22 Javascript
electron踩坑之remote of undefined的解决
2020/10/06 Javascript
实例讲解Python爬取网页数据
2018/07/08 Python
详解python Todo清单实战
2018/11/01 Python
python合并已经存在的sheet数据到新sheet的方法
2018/12/11 Python
python 自定义对象的打印方法
2019/01/12 Python
windows10下安装TensorFlow Object Detection API的步骤
2019/06/13 Python
python内置函数sorted()用法深入分析
2019/10/08 Python
python3.7 openpyxl 删除指定一列或者一行的代码
2019/10/08 Python
用python画一只可爱的皮卡丘实例
2019/11/21 Python
已安装tensorflow-gpu,但keras无法使用GPU加速的解决
2020/02/07 Python
PYcharm 激活方法(推荐)
2020/03/23 Python
使用python批量转换文件编码为UTF-8的实现
2020/04/03 Python
python 实现单例模式的5种方法
2020/09/23 Python
Python3 用什么IDE开发工具比较好
2020/11/28 Python
高一英语教学反思
2014/01/22 职场文书
领导班子群众路线与四风问题对照检查材料思想汇报
2014/10/11 职场文书
典型事迹材料范文
2014/12/29 职场文书
中层干部考核评语
2015/01/04 职场文书
关于保护环境的建议书
2019/06/24 职场文书
Android开发手册自定义Switch开关按钮控件
2022/06/10 Java/Android