概览
欢迎来到松饼人物编辑器 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,ipairsprinttypetostring
将来可能会放宽限制,支持更多的标准库成员。
随机数生成器 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内容。)