NAV Navbar
lua

概览

欢迎来到松饼人物编辑器 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()]即为自己的Existexists[self:right():index()]即为下家的Exist

Exist类型只有一个incmk方法,用于改变某种牌的存在感。 其基本用法为:

<exist>:incmk(<t>, <mk>)

其中<t>T34T37类型的麻将牌,<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 毫秒的轻量级的计算。 计算向听数、有效牌、或查看手牌组成都是没问题的。 比较容易出问题的是相对精确的和了率分析与打点期望预估。

例:通过onmonkeycheckinit的配合,实现万子清一色配牌:

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 标准库与内建函数。

能用的部分:

将来可能会放宽限制,支持更多的标准库成员。

随机数生成器 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

T37T34的扩展牌本,严格区分赤宝牌与普通数牌5。

T37是一个T34。 所有适用于T34的方法(如suitval等)也适用于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 T34T37 要查看个数的牌
返回值 类型 描述
<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 T34T37 要改变存在感的牌
<mk> number 存在感变化量
<rinshan> boolean 或 nil 选择影响岭上牌还是壁牌

改变 A 区壁牌或岭上牌出口处的下一张量子牌的存在感分布。

<t>T34类型的数牌 5 时,同时影响赤牌与黑牌; 当<t>T37类型时,严格区分赤宝牌。

改变 B 区中某牌存在感

<mount>:lightb(<t>, <mk>, <rinshan>)

参数 类型 描述
<mount> userdata Mount,非只读 Mount对象
<t> userdata T34T37 要改变存在感的牌
<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 T34T37 要改变存在感的牌
<mk> number 存在感变化量
<bspace> boolean true则影响 B 区,false则影响 A 区

mk增加出口exitpostile的存在感。

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 T34T37 要计数的牌
返回值 类型 描述
<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 T34T37 或其数组 宝牌指示牌
<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对象时完成, 调用fuhan等方法只是查询已算好的结果,不会消耗大量运算时间。 相对地,构建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>并未构成形式和了,则报错并中止运行。

关于FormctxRule

例:计算当前听牌在两立直情况下的食和得点

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

FormctxRule都是 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:getformctxgame: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>truefalse均可,不影响结果。

纯手牌得点

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>truefalse均可,不影响结果。

供托需根据座位(多家和的情况)另行计算。

得点

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 焦点是否由切牌产生

「焦点牌」指当前牌局中可能会有人食和的牌。 多数情况下,焦点牌就是最后一张弃牌。 刚配完牌,没人打牌时,没有焦点牌。 有人加杠时,焦点牌为加杠牌。

Game Event 表结构

ongameevent中使用的event变量类型为 table, 包含typeargs两个字段。 type字段为 string 类型,表示事件的类别; args字段为 table 类型,根据type的不同含有不同的字段。

(文档准备中。目前可通过自行pairs遍历查看args内容。)

lua