概览
欢迎来到松饼人物编辑器 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
内容。)