我是一个围棋业余3段的人,学了三年,一直想拥有一个属于自己的AI,目前围棋基础还可以,就是苦于自己的编程实力不够,最近看到了一个非常棒的教程,有兴趣的去找tyler_download看他的文章,代码非常详细具体,适合小白,现将我的学习心得和要点一一陈述出来

要做围棋AI先要把架子打出来,先实现基本围棋游戏再考虑引入神经网络,今天就先完成棋盘和落子的类构建

如果你有一定围棋基础,理解下面的代码就很容易,如果没有,可以先去学习围棋的气、交叉点、胜负判断等知识,我在分析时也会穿插一些围棋基本知识

步骤

   1 棋手类

         分析:围棋有黑棋和白棋,这里的棋手代表黑白两方,属性就是颜色,方法是返回对方的颜色

import enum#棋手(棋子颜色)
class Player(enum.Enum):black=1white=2#返回对方棋子颜色def other(self):if self == Player.black:return Player.whiteelse:return Player.black

  2.  棋盘交叉点类

分析: 围棋的棋子是下在交叉点上的,有9*9=81,13*13=169,19*19=361三种,而一个交叉点有上下左右四个方向的相邻点,而这个相邻点在我们之后算棋子的气(棋子的自由点)以及棋子块相连都有非常重要的用处,因此定义这个类的属性就是该点的行与列,方法就是返回该点的相邻点的集合

from collections import namedtuple#棋盘交叉点
class Point(namedtuple("Point","row col")):#增加可读性,即可通过point.row,point.col得到行列#返回棋盘一个点的四个方向相邻点def neighbor(self):return[Point(self.row,self.col+1),Point(self.row,self.col-1),Point(self.row+1,self.col),Point(self.row-1,self.col)]

那位博主为了增代码可读性,引入了namedtuple

 3.落子类

分析:落子的一个属性是所下的交叉点,然后落子前需要判断当前玩家有无pass,如果pass就让另一个玩家下,还要判断当前玩家有无投降,如果投降就不会做下面的事情

import copy
#落子
class Move():#初始化(落子为空,pass为假,投降为假)def __init__(self,point = None,is_pass = False,is_resign = False):assert (point is not None) ^is_pass ^is_resign   #断言,三种条件(落子为空,玩家pass了,玩家投降了)都不会执行下面语句,直接跳出self.point = point#是否下了self.is_play = (self.point is not None)self.is_pass = is_passself.is_resign = is_resign#加上@classmethod可以直接用类名.方法名直接调用,而不用实例化对象@classmethoddef play(cls,currentpoint):return Move(point = currentpoint)@classmethod#让对方去继续下def pass_turn(cls):return Move(is_pass = True)@classmethod#投降def resign(cls):return Move(is_resign = True)

4.棋子块类

分析:   一个棋子及棋子块当它的上下左右相邻点有同色的棋子的话,那么它们就可以相连成一个棋子块,相连的时候它们的气(未下子的相邻点个数)并不是等于两者各自的气相加,因为相连的时候,两个棋子或棋子块的 气 中有重合的气,要减掉,而重合的气就是相连后的棋子块中棋子的个数

#棋子块(相邻在一起的同色棋子,有棋子颜色,当前棋子集合,以及气(自由点集合的长度))
class GoBlock():def __init__(self,color,stones,liberties):self.color = colorself.stones = set(stones)#棋子块里棋子的集合self.liberties = set(liberties)#棋子块的自由点集合#增加气(自由点)def add_liberty(self,point):self.liberties.add(point)#减少气(自由点)def remove_liberty(self,point):self.liberties.remove(point)@property#返回棋盘块的气(自由点的个数)def num_liberties(self):return len(self.liberties)#定义相等(类别相同,颜色相同,当前落子集合相同,当前自由点相同)def __eq__(self, other):return isinstance(other,GoBlock) and self.color == other.color and self.stones == other.stones and self.liberties == other.liberties#另一个与当前棋子块相同颜色的棋子块与之相连,要注意自由点的改变,def merge_with(self,current_goblock):assert self.color == current_goblock.color #保证棋子块颜色要相同combined_stones = self.stones| current_goblock.stones#合并两个集合combined_liberties = (self.liberties|current_goblock.liberties)-combined_stones #合并后原来的棋子块的自由点与当前棋子块的自由点会有重合的点,因此要减去重合的点return GoBlock(self.color,combined_stones,combined_liberties)

5. 棋盘类 

分析:棋盘类主要要做的是放置玩家落下的棋子,而玩家落子具随机性,可能会出现非法的落子

在非应氏规则中,以下落子是非法的(今天先解决前面两个简单的,关于劫、眼的概念大家也要去了解)

1.棋子没有下在棋盘内的交叉点上,即落在棋盘外了

2.棋子落下的地方已经有棋子了

3.在不是打劫且对方的气不是只有一口的时候,落在别人的眼里

4.当对方提劫后,在未找劫材的时候去提劫

5.棋子落下后使自己的棋子气为0,即自填(在应氏规则里是允许的)

以下代码有几个要点:

1.same_color存当前落子点的上下左右相邻点中与之同色的棋子,opposite_color存当前落子点上下左右相邻点与            之不同色的棋子,而围棋并不是每个落子点都有上下左右四个有效的相邻点,因为棋盘的四个角只有两个方向的                              相邻点有效,另外两个方向的相邻点在棋盘外了无效;还有棋盘的四条边只有三个方向的相邻点是有效的,另                              一 个方向在棋盘外无效

2.在围棋中,如果一个棋子块的气为0,那么它的生命就结束了,就要从棋盘中拿掉,而棋子拿掉后会产生一些变                            化:(1)所拿掉棋子块所包含的棋子所在的交叉点应该是空,标明这些点可以重新落子

(2)拿到棋子块后,其他棋子块的气就要发生变化,要加气,而找其他块,可以去找这个点的邻接点所在                                             棋子块

3.原博主在棋盘类中除了行和列以外还加了一个grid属性,通过分析代码我们可以发现这个集合存的是一个棋盘上

所有点所在的棋子块,通过grid.get(point)找到这个点所在的棋子块,如果棋子块不存在,表明该点未下过

#棋盘
class Board():#初始化棋盘(水平交叉点,垂直交叉点,主要有9*9,13*13,19*19)def _init_(self,row_nums,col_nums):self.row = row_numsself.col = col_numsself.grid = {}#棋盘上所有点所带棋子块的集合#放置棋子,要确保位置是在棋盘内def place(self,player,point):#确保是在棋盘内assert self.is_on_grid(point) #不在格子内会执行is_on_grid直接跳出#确保所放置的棋子位置没有其他棋子assert self.grid.get(point) #有的话就跳出same_color = []#相同棋子颜色组合opposite_color = [] #不同颜色棋子组合liberties = [] #该落子点的自由点集合(即它本身的气)for neighbor in point.neighbor:#判断改点的邻接情况(若在棋盘外则返回)if not self.is_on_grid(point):continueneighbor_string = self.grid.get(neighbor)if neighbor_string is None:#邻接点没有棋子,那该邻接点是落子点的自由点,即可在自由点集合里加入liberties.append(neighbor)elif neighbor_string.color == player:#如果相邻点与当前玩家的颜色相同,同时没有加入到相同棋子颜色集合中,在集合中加入if(neighbor_string not in same_color):same_color.append(neighbor_string)else:#如果相邻点与当前玩家的颜色不同,同时没有加入到不同棋子颜色集合中,在集合中加入if(neighbor_string not in opposite_color):opposite_color.append(neighbor_string)#将当前棋子与棋盘上相邻的相同颜色棋子连成一片new_block = GoBlock(player,[point],liberties)#当前落子的棋子块#遍历相同颜色棋子组合,去和他们合并for same_color_block in same_color:new_block = new_block.merge_with(same_color_block)for new_block_point in new_block.stones:#将每个交叉点都带上自己所在的棋子块self.grid[new_block_point] = new_block#遍历不同颜色棋子集合,减少他们的自由点for opposite_color_block in opposite_color:opposite_color_block.remove_liberty(point)#判断落子后,不同颜色棋子块有无自由点,即无空的气,要删除对方棋子for opposite_color_block in opposite_color:if(opposite_color_block.num_liberties == 0):self.remove_block(opposite_color_block)#判断有无在棋盘内def is_on_grid(self,point):return 1<= point.row <= self.row and 1<= point.col <= self.col#获取一个点所在的棋子块颜色,如果没有棋子块,就返回nonedef getColor(self,point):block = self.grid.get(point)if(block is None):return Noneelse:return block.color#获取一个交叉点所在棋子块,如果没有就返回Nonedef getBlock(self,point):block = self.grid.get(point)if(block is None):return Noneelse:return block#删除气没了的棋子块def remove_block(self,block):#遍历该块的棋子,删除所有点所在棋子块集合,以及所有点相邻棋子所在棋子块的气要增加for point in block.stones:for neighbor in point.neighbor:neighbor_block = self.grid.get(neighbor)#判断一个点的邻接点的所在棋子块状态#1.没有,找下个点if(neighbor_block is None):continue#2.有,但与当前落子点的棋子块是不同的棋子块,则要加气if(neighbor_block is not block):neighbor_block.add_liberty(point)#使得当前点所在的棋子块为空self.grid[point] = None

今天暂时写这么多,明天继续

AI围棋学习之路一----棋盘和落子的类构建相关推荐

  1. Scala学习之路 (六)Scala的类、对象、继承、特质

    一.类 1.类的定义 scala语言中没有static成员存在,但是scala允许以某种方式去使用static成员 这个就是伴生机制,所谓伴生,就是在语言层面上,把static成员和非static成员 ...

  2. Qt学习之路(1)------Qt常用类用法说明

    Qt常用类 向控制台输出文本 第一个例子,我们采用STL的方式: console.cpp #include <iostream>int main() {std::cout << ...

  3. Java学习之路(十):枚举类和注解

    一.枚举类的使用 当类的对象只有有限个,确定的(此类称为枚举类),如 星期:周一,- , 周日 性别:男(man),女(woman) 支付方式:Cash(现金),WeChatPay(微信支付),Ali ...

  4. C/C++学习之路: 继承

    C/C++学习之路: 继承 目录 继承概述 派生类访问控制 继承中的构造和析构 继承中同名函数的处理方法 非自动继承的函数 继承中静态成员特性 多继承 1. 继承概述 1. 继承基本概念 c++最重要 ...

  5. AI 学习之路——轻松初探 Python 篇(三)

    喜欢小之的文章的可以关注公众号「WeaponZhi」持续关注动态 这是「AI 学习之路」的第 3 篇,「Python 学习」的第 3 篇 Python 字符串使用和 C 语言比较类似,但还有一些我们值 ...

  6. AI 学习之路——轻松初探 Python 篇(一)

    喜欢小之的文章的可以关注公众号「WeaponZhi」持续关注动态 这是「AI 学习之路」的第 1 篇,「Python 学习」的第 1 篇 前言 1. Python 篇的组织结构 不管是学习人工智能还是 ...

  7. 吴恩达Deeplearning.ai课程学习全体验:深度学习必备课程 By 路雪2017年8月14日 11:44 8 月 8 日,吴恩达正式发布了 Deepleanring.ai——基于 Cours

    吴恩达Deeplearning.ai课程学习全体验:深度学习必备课程 By 路雪2017年8月14日 11:44 8 月 8 日,吴恩达正式发布了 Deepleanring.ai--基于 Course ...

  8. 零基础AI人工智能的学习之路-从0到1-浅谈

    文章目录 0.背景 0.1. 知己 0.2. 知彼 1.if转行,这些文章可以参考学习 2.学习之路 2.1 个人学习之路 2.2 推荐的学习之路 3.推荐学习利器 3.1 Kagge 3.2 菜鸟教 ...

  9. Unity3D学习之路——AI小坦克

    Unity3D学习之路--AI小坦克 作业要求: 坦克对战游戏 AI 设计 从商店下载游戏:"Kawaii" Tank 或 其他坦克模型,构建 AI 对战坦克.具体要求 使用&qu ...

最新文章

  1. EMC开发实习生电面经验
  2. ubuntu 10.04 安装 pyquery
  3. spring的事务有几种方式
  4. ubuntu 配置 jdk 环境
  5. 零基础如何学好Python?这2点一定要明白
  6. 2020\Simulation_1\7.音节判断
  7. linux命令怎么查看dat格式的文件,Linux 查看 elf可执行文件格式的两个命令
  8. jpa 实体图查询_JPA实体图
  9. 用计算机怎么弹离人愁数字,拇指琴新手入门曲谱——离人愁
  10. respond with a status of 40_高中英语作文高分秘籍!50组高级替换词+40个高级句型+88个高级词组,还不快记下!...
  11. java linkedlist源码分析_LinkedList源码分析(基于Java8)
  12. c语言字符串如何调换位置,c语言字符串从第m个位置开始复制
  13. 51单片机自学笔记(三)——电子电路基础
  14. 智能家居市场未来发展潜力巨大,未来可期!
  15. Word2003入门动画教程75:在Word中插入文本框
  16. Android 百度人脸识别问题总结
  17. 按下 Home 键后发生了什么事?
  18. 习题6 3.6.2 典型题例解析 3.6.3 自测训练
  19. docker logs-查看docker容器日志
  20. iOS直播类APP开发流程解析

热门文章

  1. 【动网论坛7.1 sp1 修改】-关于增加本帖地址[复制本页地址 粘贴]的修改方法...
  2. 鸿蒙合香丸有副作用吗,苏合香丸能长期吃吗 有没有副作用
  3. 转贴:[铿锵发金石 幽眇感鬼神] 高瞻“家园”诗4首浅议[蛮桂]
  4. 陕西国防 c语言第三章实训三答案,C语言程序设计实验指导
  5. 阿里架构师谈:工作三年的Java程序员应该达到什么水平?
  6. CMT2380F32模块开发4-UART例程
  7. MySQL 确定哪些是叶节点、分子节点、根节点
  8. 通过读取/proc/cpuinfo获取CPU信息
  9. Pandas的常用操作(一)
  10. 3、jQuery插件之datetimepicker时间插件