Jidi Snakes 3V3

及第平台贪吃蛇3V3环境启发式算法, 可以迁移至1V1, 2P, 5P等多个环境

贪吃蛇(Snake)起源于1976年的街机游戏Blockade. 在此游戏中, 玩家控制一条蛇, 一边吃豆子, 一边避免碰撞自身和障碍物. 1996年, Next Generation将贪食蛇类游戏列入"史上100款最佳游戏".

随着多智能体强化学习的发展, 国内外涌现了众多多智能体游戏环境, 多人贪吃蛇游戏便是其一. 此类游戏虽然规则简单, 但涉及到多条蛇之间的合作或竞争, 使得策略复杂.

本文介绍了一种适用于及第平台贪吃蛇1V1, 3V3, 2P, 5P四个环境的启发式算法.

环境介绍

这里仅介绍及第贪吃蛇3V3的环境.

  • 本游戏共有两方,每个玩家分别在n*m的网格中操纵三条蛇。n为垂直高度,m为水平宽度。
  • 蛇是一系列坐标构成的有限不重复有顺序的序列,序列中相邻坐标均相邻,即两坐标的x轴坐标或y轴坐标相同,序列中第一个坐标代表蛇头。玩家只能通过控制蛇头的朝向(上、下、左和右)来控制蛇。
  • 蛇以恒定的速度前进(前进即为序列头插入蛇头指向方向下一格坐标,并删除序列末尾坐标)。蛇的初始位置为网格中的随机位置,初始长度为3格。每吃掉一个豆子长度加1。在游戏中豆子数量维持一个常数(5)不变,即被吃掉则重新随机生成等数量的豆子。
  • 蛇头在自己蛇的身体(即序列重复)、其他蛇的身体(即与其他序列有相同坐标)均判定为死亡。当输入了非法方向时(如正在向左前进,无法选择向右),随机选择一个合法方向。与传统贪吃蛇不同的是,蛇头超过边界时可以穿越到对称位置,不算做死亡。蛇死亡时可以按照初始化的设置重生。
  • 到达指定步数时游戏结束。游戏结束后,蛇身长度总和大的一方获胜。
  • 观测为一个字典,字典的键为1至7的正整数和"board_width"、"board_height"、"last_direction"、"controlled_snake_index"。其中1表示豆子,2至7表示蛇;字典的中的值为一个以[h,w]坐标为元素的列表,表示豆子的位置或蛇身的位置,其中h表示距左上角原点的垂直距离,w表示距原点的水平宽度;在2至7对应的值当中,列表元素从左至右表示对应蛇的蛇头至蛇尾的位置;"board_width"的值为地图的宽,"board_height" 的值为地图的高,"last_direction"的值为上一步各个蛇的方向,"controlled_snake_index"的值为控制蛇的序号。
  • 动作空间为长度为n_action_dim的列表,其中n_action_dim=1,每个元素为Gym当中的Discrete类( Discrete Link),如[Discrete(4)]。

算法强度

及第平台贪吃蛇1V1, 3V3, 2P, 5P四个环境中取得前两名 (截至2021年8月21日).

RLChina2021智能体竞赛四强 (八强中唯一的启发式算法)

RLChina智能体挑战赛 - 辛丑年秋赛季第二名

算法概览

本算法会依次判断给定条件是否满足, 从而做出下列五个动作之一:

  1. defensive move: 衔尾
  2. claim move: 以最短路径吃豆子
  3. safe move: 最大化生存空间
  4. attack move: 侵略性的动作
  5. random move: 没有操作空间, 随机动作

本文主要介绍claim movesafe move是如何确定的.

领土矩阵 (Territory Matrix)

领土矩阵反映了每条蛇能先于其他任意一条蛇到达的区域. 本节首先描述该矩阵的计算方式, 然后给出一种编码方式.

注意在贪吃蛇3V3环境中, 一方控制深绿, 浅绿, , 一方控制, , .

计算方式

计算领土矩阵的一个例子如下图所示. 简单说明如下:

  • 领土的申明由两个循环构成:

    1. 外循环
      • 对每条蛇, 维护一个待探列表, 外循环直至该列表为空才结束, 比如在循环开始时, 该列表即为六条蛇的蛇头.
      • 每次循环开始首先去掉蛇尾 (若蛇尾存在的话), 这样做的目的是为了使得领土的计算更加精确, 否则将有很多蛇身未被申明.
    2. 内循环
      • 即每条蛇按顺序申明领土, 比如下面的例子的顺序为:深绿浅绿.
  • 同一层外循环中, 短的蛇覆盖长的蛇的领土, 这可以保证短的蛇做更多攻击性的动作, 而长的蛇趋向防守.

  • 同一层外循环中, 若蛇长度相同, 则将领土视为有争议的区域, 均放入待探列表, 并存储信息放入争议列表.

  • 同一层外循环中, 若短的蛇可以申明一块争议区域, 且产生争议的两条蛇的长度都比其大, 则短的蛇直接申明该点, 并将该点移出争议列表.

下面做更加细节的逐图分析, 首先给出该例子的原图如下:

Step 1

首先去除尾部.

然后遍历每条蛇的待探列表, 将已探测点移出待探列表, 将即将探测的点 (下图共\(3\times5+2=17\)个) 加入待探列表.

Step 2

探索完深绿浅绿后, 深绿在两个点产生了争议.

接着探索完后, 由于长度比深绿都短, 直接覆盖了所占的两块领土, 以及深绿的一块争议领土.

Step 4

探索完深绿浅绿后, 由于长度比浅绿长, 且无处申明领地, 故在此步后待探列表变为空集, 从此再也不能进行领土申明.

Step 5

这一步深绿左下部分没有在待探列表中的点 (可以观察到这一步待探列表中的点回溯至其对应蛇的蛇头的距离 (若不做特殊说明, 下文简记为距离) 均为\(4\), 后文会将其定义为该点的state), 因此不能沿着左下进行探索. 之后会发现深绿进入了的包围圈.

最终结果

下图呈现了领土矩阵的最终计算结果, 做几点说明:

  • 每个方格中的数字表示自该点回溯至其对应蛇的蛇头所需要的步数, 定义为该蛇的state.

  • 非黑色的网格线代表蛇的初始蛇身, 按颜色一一对应 (共用边框使用了混合色).

  • 每条蛇的蛇头所处位置均被申明, 按照深绿浅绿的被衔尾顺序依次为:

    1. 褐色深绿: 深绿
    2. 浅绿浅绿: 浅绿自衔尾
    3. 浅绿: 浅绿
    4. 浅绿: 浅绿
    5. : 自衔尾
    6. 浅绿褐色: 浅绿
  • 每条蛇的所申明的领土数统计如下表: (共用领土按比例均分)

    深绿 浅绿
    申明领土数 12.5 69 7.5 14 59 38
  • 每条蛇所申明领土上的豆子数统计如下表: (共用领土按比例均分)

    深绿 浅绿
    申明豆子数 1 0 0 0 1 3

编码方式

编码方式如下, 做几点说明:

  • 上图的矩阵只有state信息, 但不能表达其属于哪条蛇
  • 下表中数值为正的编码很好理解, 其商代表该点与蛇头的距离, 余数代表该点所属蛇的编号
  • 下表中数值为负的编码, \(-6\)表示空白格, 其余被\(6\)整除的数都留空未用; 剩余的数, 其商代表该点与蛇头的距离相反数, 余数代表争议蛇的数量\(-1\), 该点产生争议的蛇的具体信息另计入一个字典中, 待需要时, 通过领土矩阵的编码进行索引.
  • 该编码方式可以扩展至有限多条蛇的对抗, 这也是该算法能直接应用于多个环境的原因.
余0 余1 余2 余3 余4 余5
商-2 -12: 未用 -11: 两条蛇争议 (state=2) -10: 三条蛇争议 (state=2) -9: 四条蛇争议 (state=2) -8: 五条蛇争议 (state=2) -7: 六条蛇争议 (state=2)
商-1 -6: 空白格 -5: 两条蛇争议 (state=1) -4: 三条蛇争议 (state=1) -3: 四条蛇争议 (state=1) -2: 五条蛇争议 (state=1) -1: 六条蛇争议 (state=1)
商0 0: 深绿蛇身 1: 浅绿蛇身 2: 蛇身 3: 蛇身 4: 蛇身 5: 蛇身
商1 6: 深绿领土 (state=1) 7: 浅绿领土 (state=1) 8: 领土 (state=1) 9: 领土 (state=1) 10: 领土 (state=1) 11: 领土 (state=1)
商2 12: 深绿领土 (state=2) 13: 浅绿领土 (state=2) 14: 领土 (state=2) 15: 领土 (state=2) 16: 领土 (state=2) 17: 领土 (state=2)

指派表 (Assignment Table)

指派表用于确定可能存在的claim move.

按从上到下, 从右到左的顺序依次给五颗豆标号.以豆子编号为行, 蛇编号为列, 创建一个\(5\times6\)的表格, 记录下蛇与其申明豆子的距离并填写, 得到如下表格.

接下来对这五个元素使用双索引排序法, 递归地给出一种指派: 即每次按此排序选取一个元素后划去该元素所在行和列, 形成一个子矩阵, 然后对子矩阵重复上述操作.

这两种索引方式具体为:

  • 数值大小 (即state大小), 按递增顺序, 上表中最优先的数值为\(2\).
  • 所在行列值为空的个数和, 按递减顺序, (注意一旦选择了一种指派, 则矩阵会删去所选元素所在行列, 因此该指标是动态变化的), 此时最优先的元素是\(3,4\), 因为其所在行列上的值除自身外均为空, 故个数和为\(9\).

这两种索引的优先级可以调整, 即产生了两种排序方式, 依次具体说明如下: (若优先级相同则随机选择一个)

数值递增>行列空值递减

分步解读如下:

  1. 优先级最高的数字为\(2\), 有两个元素满足, 在这里任选了第二行的元素, 划去其所在行和列;
  2. 随后优先级最高的数字为\(3\), 选择并划去其所在行和列;
  3. 最终只剩下了元素\(4\).

指派结果为:

  1. 🐍6⃣👉🍪2⃣
  2. 🐍5⃣👉🍪5⃣
  3. 🐍1⃣👉🍪1⃣

行列空值递减>数值递增

分步解读如下:

  1. 优先级最高的元素为\(3\)\(4\), 因为其行列上的值除自身外均为空, 从中较小的元素\(3\), 划去其所在行和列;
  2. 随后优先级最高的元素为\(4\), 选择并划去其所在的行和列;
  3. 由于\(2<5\), 这里优先级最高的数字为\(2\), 任选了第三行的元素.

指派结果为:

  1. 🐍1⃣👉🍪1⃣
  2. 🐍5⃣👉🍪5⃣
  3. 🐍6⃣👉🍪2⃣

最终指派方式的确定

尚不清楚上两种方式的优劣, 直观上感觉将数值递增顺序作为第一级较好.

代码中两种排序都做了, 最终选取的是所得指派数较多的指派, 实践中发现大多数情况下两者得出的指派数是相同的.

一旦指派涉及了当前控制的蛇, 该蛇的claim move就在指派结果中确定了, 即指派其去吃所对应的豆子. 由于领土矩阵的性质, 该指派是必定成功的.

遍历安全空间 (Safe Action Space Traverse)

上文叙述了claim move是如何确定的, 本节以控制深绿, 浅绿, 三条蛇的玩家为例, 说明没有claim move时, 如何确定safe move.

逐一分析下图六条蛇的状态:

  1. 深绿: 走claim move, 四步后吃到豆子;
  2. 浅绿: 下一步有三个安全位置, 如下图所示;
  3. : 下一步有三个安全位置, 如下图所示;
  4. : 敌方蛇, 本算法没有能力预测其动向, 只能先假设下一步不动;
  5. : 有claim move, 预测敌方会走claim move并在五步后吃到豆子;
  6. : 有claim move, 预测敌方会走claim move并在两步后吃到豆子;

遍历己方没有claim move的所有蛇的安全动作空间 (这里是浅绿, 共\(3\times3=9\)种情况), 计算下一时刻我方所有蛇的领土和 (注意在下一时刻静止, 因为本算法没有能力预测其动向), 选择使得领土和最大的动作组合. 至此我方三条蛇的动作均被确定.

这里做几点说明:

  • 本节采用领土的大小作为判断动作组合好坏的标准, 这是最简单的指标, 可以设置更有效, 更复杂的指标.
  • 若安全动作空间为空, 则蛇会做attack moverandom move, 设计方式很多, 这里不做过多说明.

Appendix

可视化

常见多人贪吃蛇环境介绍

Botzone 贪食蛇

Botzone平台上的双人游戏, 1v1, 无豆子, 蛇身随时间增长.

及第贪吃蛇

及第平台的一系列环境, 包括1V1, 3V3 2P, 5P, 特点为蛇可以穿越边界至对称位置.

Battlesnakes

国外较流行的平台, 有多种模式, 标准模式有以下特点:

  1. 每条蛇初始血量为100, 随时间减少, 吃豆子后回满.
  2. 纯竞争环境, 活到最后的蛇胜出.
  3. 若蛇正向上走, 但却输出了向下的动作, 会被判定为碰撞到自己身体并被移出游戏.
  4. 两蛇头相撞时, 短的蛇被移出游戏; 若长度相同, 则均被移出游戏.

与该环境相关的工作有Schier2019AdversarialNS, Chung2020BattlesnakeCA.

链接

Video: 【RLChina 2021】竞赛日, 我的分享在1:17:33-1:34:33

Slides: snake_3V3

Code: CarlossShi/Competition_3v3snakes