好久没有写过博客了,多久,大概8年???最近重新把写作这事儿捡起来……最近在折腾AI,写个AI相关的给团队的小伙伴们看吧。

搞了这么多年的机器学习,从分类到聚类,从朴素贝叶斯到SVM,从神经网络到深度学习,各种神秘的项目里用了无数次,但是感觉干的各种事情离我们生活还是太远了。最近AlphaGo Zero的发布,深度学习又火了一把,小伙伴们按捺不住内心的躁动,要搞一个游戏AI,好吧,那就从规则简单、老少皆宜的五子棋开始讲起。

好了,废话就说这么多,下面进入第一讲,实现一个五子棋。

小伙伴:此处省去吐槽一万字,说好的讲深度学习,怎么开始扯实现一个五子棋程序了,大哥你不按套路出牌啊……

我:工欲善其事必先利其器,要实现五子棋的AI,连棋都没有,AI个锤子!

老罗:什么事?

……

五子棋分为有禁手和无禁手,我们先实现一个普通版本的无禁手版本作为例子,因为这个不影响我们实现一个AI。补充说明一下,无禁手黑棋必胜,经过比赛和各种研究,人们逐渐知道了这个事实就开始想办法来限制黑棋先手优势。于是出现了有禁手规则,规定黑棋不能下三三,四四和长连。但随着比赛的结果的研究的继续进行,发现其实即使是对黑棋有禁手限制,还是不能阻止黑棋开局必胜的事实,像直指开局中花月,山月,云月,溪月,寒星等,斜指开局中的名月,浦月,恒星,峡月,岚月都是黑棋必胜。于是日本人继续提出了交换和换打的思想,到了后来发展成了国际比赛中三手交换和五手二打规则,防止执黑者下出必胜开局或者在第五手下出必胜打。所以结论是,在不正规的比赛规则或者无禁手情况下,黑棋必胜是存在的。

(1)五子棋下棋逻辑实现

这里用Python来实现,因为之后的机器学习库也是Python的,方便一点。

界面和逻辑要分开,解耦合,这个是毋庸置疑的,并且之后还要训练AI,分离这是必须的。所以我们先来实现一个五子棋的逻辑。

我们先来考虑五子棋是一个15*15的棋盘,棋盘上的每一个交叉点(或格子)上一共会有3种状态:空白、黑棋、白棋,所以先建个文件 consts.py

做如下定义:

from enum import EnumN = 15class ChessboardState(Enum):EMPTY = 0BLACK = 1WHITE = 2

棋盘的状态,我们先用一个15*15的二维数组chessMap来表示,建一个类 gobang.py

currentI、currentJ、currentState 分别表示当前这步着棋的坐标和颜色,再定义一个get和set函数,最基本的框架就出来了,代码如下:

from enum import Enum
from consts import *class GoBang(object):def __init__(self):self.__chessMap = [[ChessboardState.EMPTY for j in range(N)] for i in range(N)]self.__currentI = -1self.__currentJ = -1self.__currentState = ChessboardState.EMPTYdef get_chessMap(self):return self.__chessMapdef get_chessboard_state(self, i, j):return self.__chessMap[i][j]def set_chessboard_state(self, i, j, state):self.__chessMap[i][j] = stateself.__currentI = iself.__currentJ = jself.__currentState = state

这样界面端可以调用get函数来获取各个格子的状态来决定是否绘制棋子,以及绘制什么样的棋子;每次下棋的时候呢,在对应的格子上,通过坐标来设置棋盘Map的状态。

所以最基本的展示和下棋,上面的逻辑就够了,接下来干什么呢,得考虑每次下棋之后,set了对应格子的状态,是不是需要判断当前有没有获胜。所以还需要再加两个函数来干这个事情,思路就是从当前位置从东、南、西、北、东南、西南、西北、东北8个方向,4根轴,看是否有连续的大于5颗相同颜色的棋子出现。假设我们目前落子在棋盘正中,需要判断的位置如下图所示的米字形。

那代码怎么写呢,最最笨的办法,按照字面意思来翻译咯,比如横轴,先看当前位置左边有多少颗连续同色的,再看右边有多少颗连续同色的,左边加右边,就是当前横轴上的连续数,如果大于5,则胜利。

    def have_five(self, current_i, current_j):#四个方向计数 竖 横 左斜 右斜hcount = 1temp = ChessboardState.EMPTY#H-左for j in range(current_j - 1, -1, -1):  #横向往左 from (current_j - 1) to 0temp = self.__chessMap[current_i][j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breakhcount = hcount + 1        #H-右for j in range(current_j + 1, N):  #横向往右 from (current_j + 1) to Ntemp = self.__chessMap[current_i][j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breakhcount = hcount + 1        #H-结果if hcount >= 5:return True

以此类推,再看竖轴、再看左斜、再看右斜。于是,have_five函数变成这样了:

    def have_five(self, current_i, current_j):#四个方向计数 横 竖 左斜 右斜hcount = 1vcount = 1lbhcount = 1rbhcount = 1temp = ChessboardState.EMPTY#H-左for j in range(current_j - 1, -1, -1):  #横向往左 from (current_j - 1) to 0temp = self.__chessMap[current_i][j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breakhcount = hcount + 1#H-右for j in range(current_j + 1, N):  #横向往右 from (current_j + 1) to Ntemp = self.__chessMap[current_i][j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breakhcount = hcount + 1#H-结果if hcount >= 5:return True        #V-上for i in range(current_i - 1, -1, -1):  # from (current_i - 1) to 0temp = self.__chessMap[i][current_j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breakvcount = vcount + 1#V-下for i in range(current_i + 1, N):  # from (current_i + 1) to Ntemp = self.__chessMap[i][current_j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breakvcount = vcount + 1#V-结果if vcount >= 5:return True        #LB-上for i, j in zip(range(current_i - 1, -1, -1), range(current_j - 1, -1, -1)):  temp = self.__chessMap[i][j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breaklbhcount = lbhcount + 1#LB-下for i, j in zip(range(current_i + 1, N), range(current_j + 1, N)):  temp = self.__chessMap[i][j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breaklbhcount = lbhcount + 1#LB-结果if lbhcount >= 5:return True        #RB-上for i, j in zip(range(current_i - 1, -1, -1), range(current_j + 1, N)):  temp = self.__chessMap[i][j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breakrbhcount = rbhcount + 1#RB-下for i, j in zip(range(current_i + 1, N), range(current_j - 1, -1, -1)):  temp = self.__chessMap[i][j]if temp == ChessboardState.EMPTY or temp != self.__currentState:breakrbhcount = rbhcount + 1#LB-结果if rbhcount >= 5:return True

这样是不是就写完了,五子棋的逻辑全部实现~

NO,别高兴得太早,我想说,我好恶心,上面那个代码,简直丑爆了,再看一眼,重复的写了这么多for,这么多if,这么多重复的代码块,让我先去吐会儿……

好了,想想办法怎么改,至少分了4根轴,是重复的对不对,然后每根轴分别从正负两个方向去统计,最后加起来,两个方向,也是重复的对不对。

于是我们能不能只写一个方向的代码,分别调2次,然后4根轴,分别再调4次,2*4=8,一共8行代码搞定试试。

因为有45°和135°这两根斜轴的存在,所以方向上应该分别从x和y两个轴来控制正负,于是可以这样,先写一个函数,按照方向来统计:

xdirection=0,ydirection=1       表示从y轴正向数;

xdirection=0,ydirection=-1     表示从y轴负向数;

xdirection=1,ydirection=1       表示从45°斜轴正向数;

……

不一一列举了,再加上边界条件的判断,于是有了以下函数:

    def count_on_direction(self, i, j, xdirection, ydirection, color):count = 0for step in range(1, 5): #除当前位置外,朝对应方向再看4步if xdirection != 0 and (j + xdirection * step < 0 or j + xdirection * step >= N):breakif ydirection != 0 and (i + ydirection * step < 0 or i + ydirection * step >= N):breakif self.__chessMap[i + ydirection * step][j + xdirection * step] == color:count += 1else:breakreturn count

于是乎,前面的have_five稍微长的好看了一点,可以变成这样:

def have_five(self, i, j, color):#四个方向计数 横 竖 左斜 右斜hcount = 1vcount = 1lbhcount = 1rbhcount = 1hcount += self.count_on_direction(i, j, -1, 0, color)hcount += self.count_on_direction(i, j, 1, 0, color)if hcount >= 5:return Truevcount += self.count_on_direction(i, j, 0, -1, color)vcount += self.count_on_direction(i, j, 0, 1, color)if vcount >= 5:return Truelbhcount += self.count_on_direction(i, j, -1, 1, color)lbhcount += self.count_on_direction(i, j, 1, -1, color)if lbhcount >= 5:return Truerbhcount += self.count_on_direction(i, j, -1, -1, color)rbhcount += self.count_on_direction(i, j, 1, 1, color)if rbhcount >= 5:return True

还是一大排重复的代码呀,我还是觉得它丑啊,我真的不是处女座,但是这个函数是真丑啊,能不能让它再帅一点,当然可以,4个重复块再收成一个函数,循环调4次,是不是可以,好,就这么干,于是have_five就又漂亮了一点点:

    def have_five(self, i, j, color):#四个方向计数 横 竖 左斜 右斜directions = [[(-1, 0), (1, 0)], \[(0, -1), (0, 1)], \[(-1, 1), (1, -1)], \[(-1, -1), (1, 1)]]for axis in directions:axis_count = 1for (xdirection, ydirection) in axis:axis_count += self.count_on_direction(i, j, xdirection, ydirection, color)if axis_count >= 5:return Truereturn False

嗯,感觉好多了,这下判断是否有5颗相同颜色棋子的逻辑也有了,再加一个函数来给界面层返回结果,逻辑部分的代码就差不多了:

    def get_chess_result(self):if self.have_five(self.__currentI, self.__currentJ, self.__currentState):return self.__currentStateelse:return ChessboardState.EMPTY

于是,五子棋逻辑代码就写完了,完整代码 gobang.py 如下:

#coding:utf-8from enum import Enum
from consts import *class GoBang(object):def __init__(self):self.__chessMap = [[ChessboardState.EMPTY for j in range(N)] for i in range(N)]self.__currentI = -1self.__currentJ = -1self.__currentState = ChessboardState.EMPTYdef get_chessMap(self):return self.__chessMapdef get_chessboard_state(self, i, j):return self.__chessMap[i][j]def set_chessboard_state(self, i, j, state):self.__chessMap[i][j] = stateself.__currentI = iself.__currentJ = jself.__currentState = statedef get_chess_result(self):if self.have_five(self.__currentI, self.__currentJ, self.__currentState):return self.__currentStateelse:return ChessboardState.EMPTYdef count_on_direction(self, i, j, xdirection, ydirection, color):count = 0for step in range(1, 5): #除当前位置外,朝对应方向再看4步if xdirection != 0 and (j + xdirection * step < 0 or j + xdirection * step >= N):breakif ydirection != 0 and (i + ydirection * step < 0 or i + ydirection * step >= N):breakif self.__chessMap[i + ydirection * step][j + xdirection * step] == color:count += 1else:breakreturn countdef have_five(self, i, j, color):#四个方向计数 横 竖 左斜 右斜directions = [[(-1, 0), (1, 0)], \[(0, -1), (0, 1)], \[(-1, 1), (1, -1)], \[(-1, -1), (1, 1)]]for axis in directions:axis_count = 1for (xdirection, ydirection) in axis:axis_count += self.count_on_direction(i, j, xdirection, ydirection, color)if axis_count >= 5:return Truereturn False

小伙伴:大哥,憋了半天,就憋出这么不到60行代码?

我:代码不在多,实现则灵……

明天来给它加个render,前端界面就有了,就是一个简单的完整游戏了,至于AI,别急嘛。

好吧,就这样…

UI部分在这里:

[深度学习]实现一个博弈型的AI,从五子棋开始(2)

转载于:https://www.cnblogs.com/erwin/p/7828956.html

[深度学习]实现一个博弈型的AI,从五子棋开始(1)相关推荐

  1. [深度学习]实现一个博弈型的AI,从五子棋开始

    好久没有写过博客了,多久,大概8年???最近重新把写作这事儿捡起来--最近在折腾AI,写个AI相关的给团队的小伙伴们看吧. 搞了这么多年的机器学习,从分类到聚类,从朴素贝叶斯到SVM,从神经网络到深度 ...

  2. 深度学习和机器博弈如何结合_对抗机器学习的博弈论方法

    深度学习和机器博弈如何结合 Artificial Intelligence has known a great success in recent years as it provided us wi ...

  3. 神经网络可解释性、深度学习新方法,2020 年 AI 有哪些势不可挡的研究趋势?...

    来演:雷锋网 2019 年最后一场学术顶会告诉我们 2020 年该研究什么! 文 | MrBear 作为 2019 年最后一场重量级的人工智能国际学术顶会,NeurIPS 2019 所反映出的一些人工 ...

  4. 深度学习笔记-[跟李沐学AI]-01引言

    DIVE INTO DEEP LEARNING 参考笔记:http://zh-v2.d2l.ai/chapter_introduction/index.html 符号 本书中使用的符号概述如下. 数字 ...

  5. 独家 | 手把手教你运用深度学习构建视频人脸识别模型(Python实现)

    作者:Faizan Shaikh 翻译:季洋 校对:王雨桐 本文约2700字,建议阅读10+分钟. 本文将展示如何使用开源工具完成一个人脸识别的算法. 引言 "计算机视觉和机器学习已经开始腾 ...

  6. 【深度学习】一个应用—肝脏CT图像自动分割(术前评估)

    [深度学习]一个应用-肝脏CT图像自动分割(术前评估) 文章目录 1 目标 2 数据集 3 LITS20173.1 LiTS数据的预处理3.2 LiTS数据的读取3.3 数据增强3.4 数据存储 4 ...

  7. 案例 :手把手教你运用深度学习构建视频人脸识别模型(Python实现)

    作者:Faizan Shaikh :翻译:季洋:校对:王雨桐: 本文约2700字,建议阅读10+分钟. 本文将展示如何使用开源工具完成一个人脸识别的算法. 引言 "计算机视觉和机器学习已经开 ...

  8. STM32运行深度学习指南基础篇(3)(STM32CubeMX.AI+Tensorflow)

    STM32运行深度学习指南基础篇(3)(STM32CubeMX.AI+Tensorflow) 在上一篇文章中我们已经有训练好的tflite模型,接下来我们要在Clion中实现,如果是Keil的朋友可以 ...

  9. “几何深度学习”受爱因斯坦启示:让AI摆脱平面看到更高的维度

    2020-01-13 17:06:19 新智元导读]卷积神经网络(CNN)已在平面视觉任务上大显身手,但遇见不规则曲面,其效果往往大打折扣.高通和阿姆斯特丹大学提出的"规范等变卷积神经网络& ...

最新文章

  1. PAT 显示格式错误
  2. python连sql server学生管理系统_Python 跨平台连接 SQL Server
  3. python booleans_Python 2.1 BooleansComparisons
  4. SSM解决Mapper映射文件不发布
  5. 娱乐:全方位戒除网瘾 如果你真的想告别WOW
  6. 大数据互联网架构阶段 QuartZ定时任务+RabbitMQ消息队列
  7. PHP易混淆函数的区分方法及意义
  8. Android:DELETE_FAILED_INTERNAL_ERROR Error while Installing APKs
  9. Django中的日期处理注意事项和自定义时间格式转换
  10. 设备上专用计算机管理办法,计算机设备管理办法
  11. 【ElasticSearch】Es 源码之 MetadataIndexUpgradeService 源码解读
  12. Jmeter如何将上一个请求的结果作为下一个请求的参数——使用正则表达式提取器转载...
  13. SLIC超像素分割详解(一):简介
  14. 海思HI35xx平台软件开发快速入门之H265解码实例
  15. 计算机电子表格证怎么样,初学者在电脑上如何制作电子表格
  16. linux下U盘的挂载方法
  17. 2012年度最佳分享:仿webQQ界面,详情请下载,不吃亏
  18. MybatisPlus乐观锁配置
  19. 如何使用TestFlight发布和安装测试版的app
  20. C#开发之Excel录入

热门文章

  1. mysql计算某一天所在周或月的第一天和最后一天
  2. Java学习之JDBC实现简单的CRUD(mysql数据库)
  3. 教你从0到1搭建秒杀系统-缓存与数据库双写一致
  4. python网页爬虫例子_Python 利用Python编写简单网络爬虫实例3
  5. 互动赠书 | 云上云下K8s多集群如何实现集群管理和安全治理的一致体验?
  6. SpringCloud 应用在 Kubernetes 上的最佳实践 — 部署篇(开发部署)
  7. 利用 FC OSS 快速搭建 Serverless 实时按需图像处理服务
  8. fz响应无法连接到服务器,fz链接不到服务器
  9. java 优先队列 用法_优先队列的基本用法(java和c++)
  10. java 成员类_Java类的五大成员之一——内部类