概览
欢迎来到松饼人物编辑器 API 文档! 通过松饼人物编辑器,你可以制作原创角色,发明原创技能,并分享到社区。
人物的技能由单个 Lua 脚本文件进行定义。 如果没有接触过 Lua,可食用 主站 上的的零基础教程。
本文档目前比较简略,我们还在持续添加更多的细节说明与代码样例。 遇到任何问题,可在企鹅群提问。
下文会用到一些松饼麻雀内部特有的概念和术语, 例如「牌山 A 区」、「猴子阶段」、「毫兔值」等等。 将来本文档会对这些概念做一些详细的解释, 而目前这些概念的具体定义暂时可以在 Libsaki 代码导读 中查到。那个代码导读是针对 C++ 的,但基本的设计与 Lua 相通。 大多数术语都定义在其中的「牌山系统」和「发牌姬」两个章节。
关于「只读」属性
松饼为 Lua 的 userdata 添加了「只读」的概念。 以下文档中,如果一个 userdata 变量注有「只读」字样, 那么就无法通过该变量调用标有「非只读」的方法。
例如game:getmount():lighta(T34.new("1m"), 100)就无法执行,
因为game:getmount()的返回值是只读的,
无法调用其非只读方法lighta。
回调函数
在 Lua 脚本文件中,以特定的名称定义函数,即可在特定的时机插入自己的逻辑。 这些函数都不需要参数, 必要的数据可从特定的全局变量中获取。
有一个特殊的全局变量self,在 Lua 文件的任何位置都可用。
该变量为 userdata Who 类型,代表当前 Lua 文件对应的自身角色。
其它的全局变量(例如game, who, mount等)只在特定的回调函数中可用,
下面的文档中有详细说明。
请勿通过任何绕弯的手段调用不属于当前回调函数的全局变量。 例如:
-- 错误示范
function ondice()
  rand_copy = rand
end
function ondraw()
  local num = rand_copy:gen(10)
end
ondraw的可用全局变量里没有rand, 但以上代码通过复制引用,间接地在ondraw中使用了rand。 该代码无法正常执行,会在运行时报错。
转骰子前搞事情
例:生成一个 0 ~ 9 的随机数,保存到全局变量
doge
doge = 0
function ondice()
  doge = rand:gen(10)
end
函数名
ondice
可用全局变量
| 变量名 | 类型 | 描述 | 
|---|---|---|
| game | userdata Game | 当前牌桌数据 | 
| rand | userdata Rand | 随机数生成器 | 
具体用法
定义ondice函数,即可在每局开始时转骰子之前搞事情。
通常我们会在这个阶段初始化一些状态,或生成随机数。
猴子阶段前搞事情
例:增加自己的配牌中摸到 1m 的概率:
function onmonkey()
  local exist = exists[self:index()]
  exist:incmk(T34.new("1m"), 400)
end
函数名
onmonkey
可用全局变量
| 变量名 | 类型 | 描述 | 
|---|---|---|
| game | userdata Game | 当前牌桌数据 | 
| exists | table( Exist数组) | 4 个人的存在感分布 | 
具体用法
定义onmonkey函数,即可在猴子配牌阶段搞事情。
通常我们会在这个阶段通过操作exists来影响配牌中各种牌出现的概率。
exists是一个数组,里面有四个Exist类型的 userdata 对象,
分别控制四个人的配牌。该数组需与Who类型的index函数配合使用 ——
例如,exists[self:index()]即为自己的Exist,
exists[self:right():index()]即为下家的Exist。
Exist类型只有一个incmk方法,用于改变某种牌的存在感。
其基本用法为:
<exist>:incmk(<t>, <mk>)
其中<t>为T34或T37类型的麻将牌,<mk>为毫兔单位的存在感变化量。
当<t>为T37类型时,该变化严格区分赤宝牌。
猴子阶段配牌检查
例:要求自己的配牌向听数小于等于 1:
function checkinit()
  -- 他家配牌,无条件满意
  -- 最多坚持 100 次生成
  if who ~= self or iter > 100 then
    return true
  end
  -- 自家配牌,向听数小于 1
  return init:step() <= 1
end
例:要求他家配牌不含西风:
function checkinit()
  -- 自家配牌,无条件满意
  -- 最多坚持 100 次生成
  if who == self or iter > 100 then
    return true
  end
  -- 自家配牌,西风数为 0
  return init:ct(T34.new("3f")) == 0
end
函数名
checkinit
可用全局变量
| 变量名 | 类型 | 描述 | 
|---|---|---|
| game | userdata Game | 当前牌桌数据 | 
| init | userdata Hand,只读 | 待测配牌,无论亲子都是 13 张 | 
| who | userdata Who | 待测配牌所有者 | 
| iter | number | 检测轮数,从0 开始递增,最大 999 | 
具体用法
猴子阶段中,发牌姬会不断地为一家生成配牌,
直到四家都对生成的配牌表示满意。
这个用来表示是否满意的地方,就是checkinit的返回值。
若对配牌满意,checkinit则应当返回true;不满意则应当返回false。
init为发牌姬在第iter轮猴子配牌中为who生成的配牌草案。
我们可以对init做各种分析,看看它是否满足我们想要的条件。
checkinit中的逻辑应该是小于 1 毫秒的轻量级的计算。
计算向听数、有效牌、或查看手牌组成都是没问题的。
比较容易出问题的是相对精确的和了率分析与打点期望预估。
例:通过
onmonkey和checkinit的配合,实现万子清一色配牌:
function onmonkey()
  for i = 1, 9 do
    exists[self:index()]:incmk(T34.new(i .. "m"), 200)
  end
end
function checkinit()
  if who ~= self or iter > 100 then
    return true
  end
  return init:closed():ct("m") == 13
end
此外,checkinit只适合筛选高概率出现的配牌 ——
例如一向听、五向听、无字等等。
如果需要筛选出现概率本不高的牌(例如清一色),
则需要配合onmonkey或非猴子阶段的处理,先使得这种配牌出现的概率变高,
然后再通过checkinit进行筛选。
摸牌前搞事情
例:增加自己摸到白板的概率:
function ondraw()
  if who ~= self or rinshan then
    return
  end
  mount:lighta(T34.new("1y"), 200)
end
函数名
ondraw
可用全局变量
| 变量名 | 类型 | 描述 | 
|---|---|---|
| game | userdata Game | 当前牌桌数据 | 
| mount | userdata Mount | 牌山 | 
| who | userdata Who | 将要摸牌的人 | 
| rinshan | boolean | 下一张摸牌是否为岭上牌 | 
具体用法
摸牌前被调用,可干涉牌山。
发生事件后做处理
函数名
ongameevent
可用全局变量
| 变量名 | 类型 | 描述 | 
|---|---|---|
| game | userdata Game | 当前牌桌数据 | 
| event | table | 发生的事件 | 
具体用法
ongameevent会在发生某些事件前调用。
ongameevent内不可直接干涉牌山,
一舰用于更新一些当前角色的内部状态,或记录一些信息。
event参数是一个 table,里面记录了与本次事件相关的各种信息。
Game Event 表结构 一章中有对该 table 的详细说明。
Lua 标准库
为了防止世界被破坏, 松饼中只能使用一部分的 Lua 标准库与内建函数。
能用的部分:
- table中的所有函数
- string中的所有函数
- math中除去- math.random和- math.randomseed以外的所有函数
- pairs,- ipairs
- print
- type
- tostring
将来可能会放宽限制,支持更多的标准库成员。
随机数生成器 Rand
生成整数
例:生成一个 0 ~ 9 的随机数,保存到全局变量
doge
doge = 0
function ondice()
  doge = rand:gen(10)
end
local <n> = <rand>:gen(<limit>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <rand> | userdata Rand | 随机数生成器 | 
| <limit> | number | 上限,大于 0 的整数 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <n> | number | 一个大于等于 0,小于 <limit>的整数 | 
目前随机数只能在ondice中生成,
这是因为松饼中随机数的使用是受管制的。
这主要是为了方便所有预知类的能力的实现(不仅仅是怜的未来视)。
牌局可以预知的前提,是技能具有「可重入」的性质。
所谓可重入,是指输入一样时输出必然一样。
如果(真)随机数可以在任何时间点产生,
使用了这些随机数的技能会变得不可重入。
一个不方便控制的伪随机算法,
从使用的角度上看近似等同于真随机。
因此我们禁用了可控性有限的 Lua 标准库中的math.random,
并引入状态受控的伪随机生成器Rand。
随着将来版本中对随机数控制机制的完善,
松饼可能会放宽对Rand使用时机的限制。
获取生成器当前状态
local <s> = <rand>:state()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <rand> | userdata Rand | Rand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <s> | number | 随机数生成器当前状态 | 
获取状态主要用于调试等特殊用途。
麻将牌 T34
T34代表一张不区分赤宝牌的麻将牌。
通过数值与花色构造
local <t> = T34.new(<str>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <str> | string | 表示麻将牌数值与花色的字符串 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 创建出的麻将牌 | 
通过字符串创建。
第一个字符代表数值,第二个字符代表花色。
"1m", "2p", "3s"代表 1 万、2 饼、3 索;
"1f", "2f", "3f", "4f", "1y", "2y", "3y"分别代表东南西北白发中。
通过 ID-34 构造
local <t> = T34.new(<id-34>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <id-34> | number | ID-34 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 创建出的麻将牌 | 
通过 ID-34 创建。
id34 获取 ID-34
local <id-34> = <t>:id34()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <id-34> | number | ID-34 | 
获取花色
local <suit> = <t>:suit()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <suit> | string | 花色 | 
"m", "p", "s", "f", "y"分别表示万、饼、索、风牌、三元牌。
获取数值
local <val> = <t>:val()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <val> | number | 数值 | 
数牌返回 1~9; 风牌通过 1, 2, 3, 4 分别表示东南西北; 三元牌通过 1, 2, 3 分别表示白发中。
获取字符串表示
local <str> = <t>:str34()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <str> | string | 对应的字符串 | 
字牌判断
local <ok> = <t>:isz()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | <t>是否为字牌 | 
数牌判断
local <ok> = <t>:isnum()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | <t>是否为数牌 | 
老头牌判断
local <ok> = <t>:isnum19()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | <t>是否为老头牌 | 
么九牌判断
local <ok> = <t>:isyao()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | <t>是否为么九牌 | 
役牌判断
local <ok> = <t>:isyakuhai(<sw>, <rw>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T34 | 麻将牌对象 | 
| <sw> | number | 自风 | 
| <rw> | number | 场风 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | <t>是否为役牌 | 
相等判断
local <ok> = <t1> == <t2>
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t1> | userdata T34 | 一个麻将牌对象 | 
| <t2> | userdata T34 | 另一个麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | <t1>与<t2>的花色与数值是否相同 | 
大小判断
local <ok> = <t1> < <t2>
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t1> | userdata T34 | 一个麻将牌对象 | 
| <t2> | userdata T34 | 另一个麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | <t1>在标准理牌中是否排在<t2>之前 | 
宝牌判断
local <ok> = <t1> % <t2>
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t1> | userdata T34 | 一个麻将牌对象 | 
| <t2> | userdata T34 | 另一个麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | 指示牌 <t1>是否指示<t2>为宝牌 | 
宝牌推算
local <dora> = <id>:dora()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <id> | userdata T34 | 指示牌 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <dora> | userdata T34 | 被 <id>指示的宝牌 | 
指示牌推算
local <id> = <dora>:indicator()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <dora> | userdata T34 | 宝牌 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <id> | userdata T34 | 指示 <dora>的指示牌 | 
遍历 34 种牌
for _, <t> in ipairs(T34.all) do <...> end
麻将牌 T37
T37是T34的扩展牌本,严格区分赤宝牌与普通数牌5。
T37是一个T34。
所有适用于T34的方法(如suit,val等)也适用于T37。
所有能接受T34的地方也都能接受T37。
反之,T34不一定是T37。只接受T37的地方是不接受T34的。
通过字符串构造
local <t> = T37.new(<str>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <str> | string | 表示数值与花色的字符串 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T37 | 创建出的 T37对象 | 
可通过"0m", "0p", "0s"创建赤宝牌,其余同T34。
通过 ID-34 构造
local <t> = T37.new(<id-34>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <id-34> | number | ID-34 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T37 | 创建出的 T37对象 | 
此方法不创建赤牌。
赤牌判断
local <ok> = <t>:isaka5()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T37 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | 是否为赤宝牌 | 
获取对应的字符串
local <str> = <t>:str37()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t> | userdata T37 | 麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <str> | string | 表示数值与花色的字符串 | 
以"0m", "0p", "0s"表示赤 5。
全等判断
local <ok> = <t1>:lookssame(<t2>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <t1> | userdata T37 | 一个麻将牌对象 | 
| <t2> | userdata T37 | 另一个麻将牌对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | 花色、数值、以及赤牌属性是否皆相等 | 
身价标识 Who
相等判断
local <ok> = <who1> == <who2>
| 参数 | 类型 | 描述 | 
|---|---|---|
| <who1> | userdata Who | 一个 Who对象 | 
| <who2> | userdata Who | 另一个 Who对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | <who1>与<who2>是否指代同一人 | 
获取下家
local <who2> = <who1>:right()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <who1> | userdata Who | Who对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <who2> | userdata Who | <who1>的下家 | 
获取对家
local <who2> = <who1>:cross()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <who1> | userdata Who | Who对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <who2> | userdata Who | <who1>的对家 | 
获取上家
local <who2> = <who1>:left()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <who1> | userdata Who | Who对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <who2> | userdata Who | <who1>的上家 | 
转骰选人
local <who2> = <who1>:bydice(<dice>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <who1> | userdata Who | 转骰子的人 | 
| <dice> | number | 骰子点数 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <who2> | userdata Who | 被骰子指向的人 | 
摸牌次数选人
local <who2> = <who1>:byturn(<turn>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <who1> | userdata Who | Who对象 | 
| <turn> | number | 骰子点数 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <who2> | userdata Who | <who1>之后第<turn>个摸牌的人 | 
数字索引
local <index> = <who>:index()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <who> | userdata Who | Who对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <index> | number | 一个数字,常用于表键 | 
相对距离计算
local <dist> = <who1>:turnfrom(<who2>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <who1> | userdata Who | 等待方 | 
| <who2> | userdata Who | 被等待方 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <dist> | number | 双方距离 | 
在没有鸣牌的假设下,
计算从<who2>摸牌到<who1>摸牌之间最少需要的回合跳转数。
即:如果<who2> == <who1>,返回 0,
<who2> == <who1>:left(),返回 1,以此类推。
副露与暗杠 M37
下文中「副露与暗杠」统称为bark。
获取 bark 类型
local <type> = <m>:type()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <m> | userdata M37 | 一个 M37对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <type> | string | bark 的类型 | 
返回值为 "chii", "pon", "daiminkan","ankan", "kakan"中的一个。
获取 bark 中的一张牌
local <t> = <m>[<index>]
| 参数 | 类型 | 描述 | 
|---|---|---|
| <m> | userdata M37 | 一个 M37对象 | 
| <index> | number | 下标 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <type> | userdata T37 | 麻将牌 | 
通过下标获取 meld 中的一张牌。 对于吃、碰,下标的范围是 1~3; 对于杠,下标范围是 1~4。
下标的意义定义如下:
- 吃的下标顺序是数牌的数值顺序
- 碰的下标顺序是实际摆放的顺序
- 大明杠的情况需参考部分网麻中的类似加杠的浓缩式摆放, 其中前三张下标顺序同碰,代表下层实际摆放顺序, 而第四张牌则必定是手中原有的牌
- 暗杠的下标顺序为「赤牌在前」
- 加杠的前三张牌顺序同碰,第四张牌为加杠牌
对于碰与明杠而言,下标顺序唯一的意义在于区分赤牌的来源。
检查某张牌的存在
local <ok> = <m>:has(<t>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <m> | userdata M37 | 一个 M37对象 | 
| <t> | userdata T34 | 一个 M34对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | <m>中是否含有<t> | 
牌山 Mount
获取残枚
local <n> = <mount>:remainpii()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount | Mount对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <n> | number | 壁牌残杖 | 
此处「壁牌」指除王牌以外的普通山牌。
获取岭上牌残枚
local <n> = <mount>:remainrinshan()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount | Mount对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <n> | number | 岭上牌残杖 | 
可间接得知目前已成立的杠的个数。
查看 A 区中某牌个数
local <n> = <mount>:remaina(<t>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount | Mount对象 | 
| <t> | userdata T34或T37 | 要查看个数的牌 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <n> | number | A 区中 <t>的个数 | 
当<t>为T34类型的数牌 5 时,结果为包含赤牌与黑牌的总个数;
当<t>为T37类型时,严格区分赤宝牌。
查看宝牌指示牌
local <drids> = <mount>:getdrids()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount | Mount对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <drids> | table ( T37数组) | (杠)宝牌指示牌序列 | 
查看里宝牌指示牌
local <urids> = <mount>:geturids()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount | Mount对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <urids> | table ( T37数组) | (杠)里宝牌指示牌序列 | 
读取立直和牌后已亮出的(杠)里宝牌指示牌序列。 若「立直和牌」尚未发生,则返回空表。
改变 A 区中某牌存在感
<mount>:lighta(<t>, <mk>, <rinshan>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount,非只读 | Mount对象 | 
| <t> | userdata T34或T37 | 要改变存在感的牌 | 
| <mk> | number | 存在感变化量 | 
| <rinshan> | boolean 或 nil | 选择影响岭上牌还是壁牌 | 
改变 A 区壁牌或岭上牌出口处的下一张量子牌的存在感分布。
当<t>为T34类型的数牌 5 时,同时影响赤牌与黑牌;
当<t>为T37类型时,严格区分赤宝牌。
改变 B 区中某牌存在感
<mount>:lightb(<t>, <mk>, <rinshan>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount,非只读 | Mount对象 | 
| <t> | userdata T34或T37 | 要改变存在感的牌 | 
| <mk> | number | 存在感变化量 | 
| <rinshan> | boolean 或 nil | 选择影响岭上牌还是壁牌 | 
改变 B 区壁牌或岭上牌出口处的下一张量子牌的存在感分布。
当<t>为T34类型的数牌 5 时,同时影响赤牌与黑牌;
当<t>为T37类型时,严格区分赤宝牌。
改变某牌存在感(一般形式)
<mount>:inkmk(<exit>, <pos>, <t>, <mk>, <bspace>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount,非只读 | Mount对象 | 
| <exit> | string | 影响的出口(见下文说明) | 
| <t> | userdata T34或T37 | 要改变存在感的牌 | 
| <mk> | number | 存在感变化量 | 
| <bspace> | boolean | true则影响 B 区,false则影响 A 区 | 
以mk增加出口exit之pos处tile的存在感。
exit需传入"pii", "rinshan", "dorahyou", "urahyou"之一,
分别代表普通壁牌、岭上牌、未翻开的表宝牌指示牌、未挖出的里宝牌指示牌。
pos为 0 时代表出口处队首牌,1 时代表队首的下一张牌,以此类推。
当<t>为T34类型的数牌 5 时,同时影响赤牌与黑牌;
当<t>为T37类型时,严格区分赤宝牌。
放入 B 区
<mount>:loadb(<t>, <num>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount,非只读 | Mount对象 | 
| <t> | userdata T37 | 要放入 B 区的牌 | 
| <num> | number | 放入数量 | 
将指定数量的牌由 A 区放入 B 区。若 A 区储量不足,则能放多少放多少。
麻将牌多集 Tilecount
计数
local <n> = <tc>:ct(<t>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <tc> | userdata Tilecount | Tilecount对象 | 
| <t> | userdata T34或T37 | 要计数的牌 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <n> | number | <t>的数量 | 
对于数牌 5,参数<t>为T37类型时区分赤宝牌,T34类型时返回包含赤、黑的总数量。
花色计数
local <n> = <tc>:ct(<suit>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <tc> | userdata Tilecount | Tilecount对象 | 
| <suit> | string | 要计数的花色 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <n> | number | <t>的数量 | 
参数<suit>须为"m", "p", "s", "f", "y"之一。
手牌 Hand
本文档中「手牌」一词指包含纯手牌、自摸牌、副露、暗杠的广义手牌。
通过game:gethand(<who>)获取的手牌代表牌桌上的真实手牌,
具有「只读」属性,
只能由松饼系统修改,技能代码无法干涉(否则就是作弊了)。
如果需要修改手牌(为了计算手牌改变后的向听数等目的),
可以通过下文中的「复制构造」方法,
创建一份不带只读属性的假想手牌,从而调用要求Hand非只读的方法。
但修改不是乱改,否则是要谢罪的。
Hand应始终处于「14 - 3k 张」或「13 - 3k 张」的状态(k 代表 bark 数,范围是 0 ~ 4)。
如果你尝试让Hand脱离这两种状态,代码会报错并中止运行。
复制构造
local <hand2> = Hand.new(<hand>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | 被复制的手牌 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <hand2> | userdata Hand | 复制出的 Hand对象 | 
获取纯手牌
local <closed> = <hand>:closed()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <closed> | userdata Tilecount | 纯手牌 | 
获取手牌中除自摸牌、副露、暗杠以处的纯手牌部分。
获取 barks
local <barks> = <hand>:barks()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <barks> | table ( M37数组) | barks 序列 | 
获取表示副露与暗杠的 barks 部分。
计数
local <n> = <hand>:ct(<t>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| <t> | userdata T34 | 要计数的牌 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <n> | number | 手牌中 <t>的总数量 | 
赤 5 计数
local <n> = <hand>:ctaka5()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <n> | number | 手牌中赤 5 总数量 | 
听牌判断
local <ok> = <hand>:ready()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | number | 是否已形式听牌(不含纯空听) | 
计算向听数
local <step> = <hand>:step()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <step> | number | 向听数 | 
计算四面子向听数
local <step> = <hand>:step4()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <step> | number | 四面子向听数 | 
计算七对子向听数
local <step> = <hand>:step7()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <step> | number | (日麻)七对子向听数 | 
计算国标七对子向听数
local <step> = <hand>:step7gb()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <step> | number | 国标七对子向听数 | 
计算国士向听数
local <step> = <hand>:step13()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <step> | number | 国士向听数 | 
计算一类有效牌
local <effas> = <hand>:effa()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <effas> | table( T34数组) | 一类有效牌序列 | 
计算四面子一类有效牌
local <effa4s> = <hand>:effa4()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <effa4s> | table( T34数组) | 四面子一类有效牌序列 | 
门前清判断
local <ok> = <hand>:ismenzen()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | 手牌是否为门前清 | 
宝牌计数
local <n> = <indicator> % <hand>
| 参数 | 类型 | 描述 | 
|---|---|---|
| <indicator> | userdata T34,T37或其数组 | 宝牌指示牌 | 
| <hand> | userdata Hand | Hand对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <n> | number | 被指示出的宝牌数 | 
摸牌
<hand>:draw(<t37>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand,非只读 | 摸牌之前的手牌 | 
| <t37> | userdata T37 | 要摸入的牌 | 
为<hand>以摸牌的方式添加一张牌。
若<hand>并非处于可摸牌的状态(比如已经摸牌,或正在鸣牌),则报错并中止程序。
手切
<hand>:swapout(<t37>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand,非只读 | 切牌之前的手牌 | 
| <t37> | userdata T37 | 要手切的牌 | 
从<hand>中,以手切的方式去掉一张牌。
若<hand>并非处于可手切的状态,则报错并中止程序。
若<hand>的纯手牌部分不含<t37>,则报错并中止程序。
自摸切
<hand>:spinout()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand,非只读 | 切牌之前的手牌 | 
从<hand>中,以自摸切的方式去掉一张牌。
若<hand>并非处于可自摸切的状态,则报错并中止程序。
手役 Form
Form用于计算日麻和了形的手役。
一个Form对象代表一副和牌在当前规则下的的最终解释方式。
对于多解的手牌,松饼优先采用「高点法」。 点数相同时,采用「高翻法」。翻数相同时,无作为地选取任意一种解法。
役种的计算会在构建Form对象时完成,
调用fu,han等方法只是查询已算好的结果,不会消耗大量运算时间。
相对地,构建Form会消耗大量的运算时间(毫秒级),
因此构建Form的次数则应该尽可能减少。
构建一两次Form,所消耗的时间只有几毫秒,界面不会出现卡顿;
但在短时间内几十次、上百次地构建Form,所消耗的时间就是零点几秒,
会出现明显的卡顿。
因此,能不用Form,就尽量别用Form
—— 尤其是不要在大量循环的计算中使用Form。
Form是用来「绝对精确」地计算得点的。
然而在技能的实现中,我们往往并不需要绝对精确,
或者因条件不足无法做到绝对精确。
这时如果使用Form就是在浪费计算性能了。
如果只是需要粗略地估算得点,建议自已另行设计一套算法。
反正可以通过Hand读取手牌的完整信息,
理论上任何基于牌形的得点预估算法都是能自己做出来的。
从自摸构建
local <form> = Form.new(<hand>, <formctx>, <rule>, <drids>, <urids>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | 自摸和了形 | 
| <formctx> | userdata Formctx | 役种上下文 | 
| <rule> | userdata Rule | 麻将规则配置 | 
| <drids> | table ( T37数组) | 表宝牌指示牌序列,可省略 | 
| <urids> | table ( T37数组) | 里宝牌指示牌序列,可省略 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 创建出的 Form对象 | 
通过一组已通过自摸达到「形式和了」的手牌构建Form。
若<hand>并未达到形式和了,则报错并中止运行。
从食和构建
local <form> = Form.new(<hand>, <final>, <formctx>, <rule>, <drids>, <urids>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand | 食和听牌形 | 
| <final> | userdata T37 | 铳牌 | 
| <formctx> | userdata Formctx | 役种上下文 | 
| <rule> | userdata Rule | 麻将规则配置 | 
| <drids> | table ( T37数组) | 表宝牌指示牌序列,可省略 | 
| <urids> | table ( T37数组) | 里宝牌指示牌序列,可省略 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 创建出的 Form对象 | 
通过一组已通过食和达到「形式和了」的手牌构建Form。
若<hand>与<final>并未构成形式和了,则报错并中止运行。
关于Formctx和Rule
例:计算当前听牌在两立直情况下的食和得点
function ondraw()
  if who ~= self then
    return
  end
  local hand = game:gethand(self)
  if not hand:ready() then
    return
  end
  local formctx = game:getformctx(self)
  formctx.riichi = 2
  for _, t in ipairs(hand:effa()) do
    local form = Form.new(hand, T37.new(t:id34()), formctx, game:getrule())
    print(form:gain())
  end
end
Formctx和Rule都是 userdata,但用法类似于 table,
可以读写里面的各种属性。
Formctx用于描述一副手牌和牌时的上下文。
在日麻(及其它多数麻将规则)中,一副和了形的得点不仅仅是由牌形决定的,
还和和牌发生时的场况,和之前的一系列黑历史有关系。
Formctx就是用来记录这些信息的。
具体属性见下表。
| 属性 | 类型 | 描述 | 
|---|---|---|
| ippatsu | boolean | 一发 | 
| bless | boolean | 天和或地和 | 
| duringkan | boolean | 岭上或枪杠 | 
| emptymount | boolean | 海底或河底 | 
| riichi | number | 0=未立直,1=立直,2=两立直 | 
| roundwind | number | 场风数值 | 
| selfwind | number | 自风数值 | 
| extraround | number | 本场数 | 
Rule用于表示用户配置的规则细项,具体属性见下表。
| 属性 | 类型 | 描述 | 
|---|---|---|
| fly | boolean | 击飞 | 
| headjump | boolean | 头跳 | 
| nagashimangan | boolean | 流满 | 
| ippatsu | boolean | 一发 | 
| uradora | boolean | 里宝 | 
| kandora | boolean | 杠宝 | 
| daiminkanpao | boolean | 大明杠包牌 | 
| akadora | number | 赤牌数,只能是 0, 3, 4 | 
| hill | number | 丘(头名赏) | 
| returnlevel | number | 返点 | 
| roundlimit | number | 局数(4=东风,8=半庄) | 
game:getformctx与game:getrule返回的对象都是副本。
修改其返回傎并不会影响到当前牌桌上的上下文和规则。
例牌役满判断
local <ok> = <form>:isprototypalyakuman()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | 是否为例牌役满 | 
判断手牌是否为大三元、四暗刻等例牌役满。
是则返回true;返回false则代表是个累计役满,或者根本不是役满。
符数
local <fu> = <form>:fu()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <fu> | number | 符数 | 
翻数
local <han> = <form>:han()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <han> | number | 翻数 | 
基本点
local <base> = <form>:base()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <base> | number | 翻数 | 
基本点就是「符 ^ (翻 + 2)」那个玩意, 满贯 2000,跳满 3000 什么的。
表宝牌数
local <dora> = <form>:dora()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <dora> | number | (杠)表宝牌数 | 
当存在多枚相同的指示牌时,同一张宝牌会被计算对应次。
里宝牌数
local <uradora> = <form>:uradora()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <uradora> | number | (杠)里宝牌数 | 
当存在多枚相同的指示牌时,同一张里宝牌会被计算对应次。
赤宝牌数
local <akadora> = <form>:akadora()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <akadora> | number | 赤宝牌数 | 
役种集合
local <yakus> = <form>:yakus()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <yakus> | table(string 集合) | 役种集合 | 
此方法用于查看该和了形中成立的具体役种。
<yakus>是一个 Lua 表,键类型为 string,代表役种;键若存在,则对应的值为true。
各种 string 代表的役种见下表。
| 字符串 | 代表役种 | 
|---|---|
| "rci" | 立直 | 
| "ipt" | 一发 | 
| "tmo" | 门前清自摸和 | 
| "tny" | 断么九 | 
| "pnf" | 平和 | 
| "y1y","y2y","y3y" | 役牌白,役牌发,役牌中 | 
| "jk1","jk2","jk3","jk4" | 自风东,南,西,北 | 
| "bk1","bk2","bk3","bk4" | 场风东,南,西,北 | 
| "ipk" | 一杯口 | 
| "rns","hai","hou","ckn" | 岭上开花,海底捞月,河底摸鱼,枪杠 | 
| "ss1","it1","ct1" | 食下三色同顺,一气通贯,混全带么九 | 
| "wri" | 两立直 | 
| "ss2","it2","ct2" | 门前清三色同顺,一气通贯,混全带么九 | 
| "toi","ctt" | 对对和,七对子 | 
| "sak","skt" | 三暗刻,三杠子 | 
| "stk","hrt","s3g" | 三色同刻,混老头,小三元 | 
| "h1t","jc2" | 食下混一色,纯全带么九 | 
| "mnh","jc3" | 门前清混一色,纯全带么九 | 
| "rpk" | 两杯口 | 
| "c1t" | 食下清一色 | 
| "mnc" | 门前清清一色 | 
| "x13","xd3","x4a","xt1" | 国、元、暗、字 | 
| "xs4","xd4","xcr","xr1" | 小、大、清、绿 | 
| "xth","xch","x4k","x9r" | 天、地、杠、九 | 
| "w13","w4a","w9r" | 面、单、纯 | 
有役判断
local <ok> = <form>:hasyaku()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <ok> | boolean | 是否有役 | 
纯手牌失点
local <loss> = <form>:netloss(<explode>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| <explode> | boolean | 是否为自摸炸庄 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <loss> | number | 一家失点,不计供托场棒 | 
如果<form>为自摸,
<explode>传false则计算闲家失点,传true则计算庄家失点。
<form>为食和时,<explode>传true或false均可,不影响结果。
纯手牌得点
local <gain> = <form>:netgain()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <gain> | number | 和牌者得点,不计供托场棒 | 
失点
local <loss> = <form>:loss(<explode>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| <explode> | boolean | 是否为自摸炸庄 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <loss> | number | 一家失点,计场棒,不计供托 | 
如果<form>为自摸,
<explode>传false则计算闲家失点,传true则计算庄家失点。
<form>为食和时,<explode>传true或false均可,不影响结果。
供托需根据座位(多家和的情况)另行计算。
得点
local <gain> = <form>:gain()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <gain> | number | 和牌者得点,计场棒,不计供托 | 
供托需根据座位(多家和的情况)另行计算。
迷之咒语
local <spell> = <form>:spell()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <spell> | string | 迷之咒语 | 
以鸟语字符串表示所有役种,主要用于调试。
迷之报点
local <charge> = <form>:charge()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <form> | userdata Form | 手役 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <charge> | string | 迷之报点 | 
以鸟语字符串表示报点文字,主要用于调试。
场况 Game
获取某家手牌
local <hand> = <game>:gethand(<who>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| <who> | userdata Who | 谁的手牌 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <hand> | userdata Hand,只读 | 手牌 | 
获取局数
local <round> = <game>:getround()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <round> | number | 局数 | 
局数从 0 开始。0 为东一,1 为东二,以此类推。
获取本场数
local <extra> = <game>:getextraround()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <extra> | number | 本场数 | 
获取庄家
local <dealer> = <game>:getdealer()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <dealer> | userdata Who | 本局庄家 | 
获取某家自风
local <sw> = <game>:getselfwind(<who>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| <who> | userdata Who | 谁的自风 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <sw> | number | 自风牌的数值(1~4) | 
获取场风
local <rw> = <game>:getroundwind()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <rw> | number | 场风牌的数值(1~4) | 
获取牌山
local <mount> = <game>:getmount()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <mount> | userdata Mount,只读 | 牌山 | 
获取某家牌河
local <river> = <game>:getriver(<who>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| <who> | userdata Who | 谁的牌河 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <river> | table( T37数组) | 牌河里的牌的序列 | 
获取手役上下文
local <formctx> = <game>:getformctx(<who>)
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| <who> | userdata Who | 谁的手役上下文 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <formctx> | userdata Formctx | 详见 Form一节 | 
获取规则
local <rule> = <game>:getrule()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <rule> | userdata Rule | 详见 Form一节 | 
获取焦点牌
local <who>, <tile>, <discard> = <game>:getfocus()
| 参数 | 类型 | 描述 | 
|---|---|---|
| <game> | userdata Game | Game对象 | 
| 返回值 | 类型 | 描述 | 
|---|---|---|
| <who> | userdata Who | 焦点牌由谁产生 | 
| <tile> | userdata T37 | 焦点牌 | 
| <discard> | boolean | 焦点是否由切牌产生 | 
「焦点牌」指当前牌局中可能会有人食和的牌。 多数情况下,焦点牌就是最后一张弃牌。 刚配完牌,没人打牌时,没有焦点牌。 有人加杠时,焦点牌为加杠牌。
- 焦点牌为最后一张弃牌时,<who>为最后切牌的人,<discard>为true。
- 不存在焦点牌时,<who>和<tile>都是nil,<discard>为false。
- 焦点牌为加杠牌时,<who>为加杠者,<discard>为false。
Game Event 表结构
ongameevent中使用的event变量类型为 table,
包含type和args两个字段。
type字段为 string 类型,表示事件的类别;
args字段为 table 类型,根据type的不同含有不同的字段。
(文档准备中。目前可通过自行pairs遍历查看args内容。)
 
      