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智能体挑战赛 - 辛丑年秋赛季第二名
算法概览
本算法会依次判断给定条件是否满足, 从而做出下列五个动作之一:
defensive move
: 衔尾claim move
: 以最短路径吃豆子safe move
: 最大化生存空间attack move
: 侵略性的动作random move
: 没有操作空间, 随机动作
本文主要介绍claim move
和safe move
是如何确定的.
领土矩阵 (Territory Matrix)
领土矩阵反映了每条蛇能先于其他任意一条蛇到达的区域. 本节首先描述该矩阵的计算方式, 然后给出一种编码方式.
注意在贪吃蛇3V3环境中,
一方控制深绿
, 浅绿
, 青
,
一方控制红
, 粉
, 褐
.
计算方式
计算领土矩阵的一个例子如下图所示. 简单说明如下:
领土的申明由两个循环构成:
- 外循环
- 对每条蛇, 维护一个
待探列表
, 外循环直至该列表为空才结束, 比如在循环开始时, 该列表即为六条蛇的蛇头. - 每次循环开始首先去掉蛇尾 (若蛇尾存在的话), 这样做的目的是为了使得领土的计算更加精确, 否则将有很多蛇身未被申明.
- 对每条蛇, 维护一个
- 内循环
- 即每条蛇按顺序申明领土,
比如下面的例子的顺序为:
深绿
→浅绿
→青
→红
→粉
→褐
.
- 即每条蛇按顺序申明领土,
比如下面的例子的顺序为:
- 外循环
同一层外循环中, 短的蛇覆盖长的蛇的领土, 这可以保证短的蛇做更多攻击性的动作, 而长的蛇趋向防守.
同一层外循环中, 若蛇长度相同, 则将领土视为有争议的区域, 均放入
待探列表
, 并存储信息放入争议列表
.同一层外循环中, 若短的蛇可以申明一块争议区域, 且产生争议的两条蛇的长度都比其大, 则短的蛇直接申明该点, 并将该点移出
争议列表
.
下面做更加细节的逐图分析, 首先给出该例子的原图如下:
Step 1
首先去除尾部.
然后遍历每条蛇的待探列表
,
将已探测点移出待探列表
, 将即将探测的点 (下图共\(3\times5+2=17\)个)
加入待探列表
.
Step 2
探索完深绿
和浅绿
后,
青
与深绿
在两个点产生了争议.
接着探索完红
和粉
后,
褐
由于长度比深绿
和青
都短,
直接覆盖了青
所占的两块领土,
以及深绿
和青
的一块争议领土.
Step 4
探索完深绿
和浅绿
后,
青
由于长度比浅绿
长, 且无处申明领地,
故在此步后待探列表
变为空集, 从此再也不能进行领土申明.
Step 5
这一步深绿
左下部分没有在待探列表
中的点
(可以观察到这一步待探列表
中的点回溯至其对应蛇的蛇头的距离
(若不做特殊说明, 下文简记为距离) 均为\(4\), 后文会将其定义为该点的state),
因此不能沿着左下进行探索.
之后会发现深绿
进入了褐
的包围圈.
最终结果
下图呈现了领土矩阵的最终计算结果, 做几点说明:
每个方格中的数字表示自该点回溯至其对应蛇的蛇头所需要的步数, 定义为该蛇的state.
非黑色的网格线代表蛇的初始蛇身, 按颜色一一对应 (共用边框使用了混合色).
每条蛇的蛇头所处位置均被申明, 按照
深绿
→浅绿
→青
→红
→粉
→褐
的被衔尾顺序依次为:褐色
→深绿
:褐
衔深绿
尾浅绿
→浅绿
:浅绿
自衔尾浅绿
→青
:浅绿
衔青
尾浅绿
→红
:浅绿
衔红
尾粉
→粉
:粉
自衔尾浅绿
→褐色
:浅绿
衔褐
尾
每条蛇的所申明的领土数统计如下表: (共用领土按比例均分)
深绿
浅绿
青
红
粉
褐
申明领土数 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\).
这两种索引的优先级可以调整, 即产生了两种排序方式, 依次具体说明如下: (若优先级相同则随机选择一个)
数值递增>行列空值递减
分步解读如下:
- 优先级最高的数字为\(2\), 有两个元素满足, 在这里任选了第二行的元素, 划去其所在行和列;
- 随后优先级最高的数字为\(3\), 选择并划去其所在行和列;
- 最终只剩下了元素\(4\).
指派结果为:
- 🐍6⃣👉🍪2⃣
- 🐍5⃣👉🍪5⃣
- 🐍1⃣👉🍪1⃣
行列空值递减>数值递增
分步解读如下:
- 优先级最高的元素为\(3\)和\(4\), 因为其行列上的值除自身外均为空, 从中较小的元素\(3\), 划去其所在行和列;
- 随后优先级最高的元素为\(4\), 选择并划去其所在的行和列;
- 由于\(2<5\), 这里优先级最高的数字为\(2\), 任选了第三行的元素.
指派结果为:
- 🐍1⃣👉🍪1⃣
- 🐍5⃣👉🍪5⃣
- 🐍6⃣👉🍪2⃣
最终指派方式的确定
尚不清楚上两种方式的优劣, 直观上感觉将数值递增顺序作为第一级较好.
代码中两种排序都做了, 最终选取的是所得指派数较多的指派, 实践中发现大多数情况下两者得出的指派数是相同的.
一旦指派涉及了当前控制的蛇,
该蛇的claim move
就在指派结果中确定了,
即指派其去吃所对应的豆子. 由于领土矩阵的性质,
该指派是必定成功的.
遍历安全空间 (Safe Action Space Traverse)
上文叙述了claim move
是如何确定的,
本节以控制深绿
, 浅绿
,
青
三条蛇的玩家为例, 说明没有claim move
时,
如何确定safe move
.
逐一分析下图六条蛇的状态:
深绿
: 走claim move
, 四步后吃到豆子;浅绿
: 下一步有三个安全位置, 如下图所示;青
: 下一步有三个安全位置, 如下图所示;红
: 敌方蛇, 本算法没有能力预测其动向, 只能先假设下一步不动;粉
: 有claim move
, 预测敌方会走claim move
并在五步后吃到豆子;褐
: 有claim move
, 预测敌方会走claim move
并在两步后吃到豆子;
遍历己方没有claim move
的所有蛇的安全动作空间
(这里是浅绿
和青
, 共\(3\times3=9\)种情况),
计算下一时刻我方所有蛇的领土和 (注意红
在下一时刻静止,
因为本算法没有能力预测其动向), 选择使得领土和最大的动作组合.
至此我方三条蛇的动作均被确定.
这里做几点说明:
- 本节采用领土的大小作为判断动作组合好坏的标准, 这是最简单的指标, 可以设置更有效, 更复杂的指标.
- 若安全动作空间为空,
则蛇会做
attack move
或random move
, 设计方式很多, 这里不做过多说明.
Appendix
可视化
常见多人贪吃蛇环境介绍
Botzone 贪食蛇
Botzone平台上的双人游戏, 1v1, 无豆子, 蛇身随时间增长.
及第贪吃蛇
及第平台的一系列环境, 包括1V1, 3V3 2P, 5P, 特点为蛇可以穿越边界至对称位置.
Battlesnakes
国外较流行的平台, 有多种模式, 标准模式有以下特点:
- 每条蛇初始血量为100, 随时间减少, 吃豆子后回满.
- 纯竞争环境, 活到最后的蛇胜出.
- 若蛇正向上走, 但却输出了向下的动作, 会被判定为碰撞到自己身体并被移出游戏.
- 两蛇头相撞时, 短的蛇被移出游戏; 若长度相同, 则均被移出游戏.
与该环境相关的工作有Schier2019AdversarialNS, Chung2020BattlesnakeCA.
链接
Video: 【RLChina 2021】竞赛日, 我的分享在1:17:33-1:34:33
Slides: snake_3V3
Code: CarlossShi/Competition_3v3snakes