大乐透与刮刮乐插件

适配 monetary 的自定义积分彩票插件,提供群聊大乐透下注、开奖与奖池分配,并附带刮刮乐小游戏。
作者: 蜥蜴


背景

在运行于社群的聊天机器人中,积分制的娱乐玩法是常见的互动需求。若以线下方式组织大乐透类游戏,需要一位负责人完成以下工作:

  1. 记录每位参与者的下注金额与下注号码
  2. 汇总奖池并随时接受新的下注
  3. 开奖后逐条核对号码并计算奖励
  4. 依据命中情况分配奖池积分

该流程在参与人数增加时工作量显著上升。koishi-plugin-lottery-lizard 将下注、开奖与积分结算交由机器人执行,参与者可在群聊中完成完整的游戏流程。


工具定位

koishi-plugin-lottery-lizard 是一款基于 Koishi 框架的积分娱乐插件,主要能力如下:

能力 说明
大乐透下注 消耗积分选择号码下注,支持多次独立下注
奖池计算 依据参与人数动态调整奖池积分
自动开奖 生成开奖号码并按命中权重分配奖池
刮刮乐 消耗积分购买刮刮乐,即时返回中奖结果
期望管理 提供期望与方差信息,便于调整奖项权重

当前版本为 0.0.3,依赖 Koishi 4.18.8,并需搭配 monetary 与数据库服务使用。


安装

前置条件

  • 已搭建并可正常运行的 Koishi 项目(koishi-app)
  • 已启用 Koishi 的数据库服务(插件声明 inject: ['database', 'monetary'],下注与积分均依赖数据库)
  • 已安装并启用 monetary 插件(插件声明 inject: ['monetary'],积分读写依赖该服务)

方式一:通过插件市场安装(推荐)

  1. 启动 Koishi 项目,打开控制台(默认地址 http://localhost:5140)。
  2. 进入左侧「插件市场」。
  3. 在搜索框中输入 lottery-lizard。
  4. 找到插件后点击「添加」,Koishi 会自动完成依赖安装与配置写入。

方式二:通过 npm 安装

在 Koishi 项目根目录执行:

1
npm install koishi-plugin-lottery-lizard

随后在 koishi.yml 中手动添加配置:

1
2
3
4
5
6
plugins:
lottery-lizard:
currency: default
costPerTicket: 5
maxTicketCount: 10
logExpectation: false

方式三:通过 yarn / pnpm 安装

若项目使用 yarn 或 pnpm,可执行:

1
2
3
4
5
# yarn
yarn add koishi-plugin-lottery-lizard

# pnpm
pnpm add koishi-plugin-lottery-lizard

启用数据库

插件下注与积分读写均依赖数据库。若项目尚未配置,可在控制台「插件配置」中找到 database-sqlite 并启用:

1
2
3
plugins:
database-sqlite:
path: data/koishi.db

保存配置后重启 Koishi 项目,即可正常使用下注与开奖功能。

启用 monetary

插件声明了 inject: ['monetary'],积分读写依赖 monetary 服务。若尚未安装,可在插件市场搜索 monetary 添加并启用:

1
2
3
plugins:
monetary:
# 按需配置

currency 配置项需与 monetary 中定义的货币字段名称保持一致,默认值为 default。

验证安装

在任意已接入机器人的聊天窗口输入:

1
大乐透

若能返回大乐透规则文本,则说明安装成功。


配置

配置项

配置项 类型 默认值 取值范围 说明
currency string default — monetary 中的 currency 字段名称
prizes array 见下文 — 刮刮乐奖项设置
costPerTicket number 5 ≥ 1 刮刮乐每张的价格
maxTicketCount number 10 ≥ 1 单次最多购买的刮刮乐数量
logExpectation boolean false — 更新奖项后是否在控制台输出数学期望

奖项字段

prizes 为奖项数组,每一项包含以下字段:

字段 类型 说明
prob number 中奖概率权重
reward number 中奖金额
name string 奖项名称

默认奖项

名称 权重 金额
天选之人 1 4999
至尊欧皇 5 1000
欧洲人 100 100
疯狂星期四 300 50
运气爆棚 800 30
狗屎运 1000 20
再来一张 80000 5
安慰奖 6000 2

配置示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
plugins:
lottery-lizard:
currency: default
costPerTicket: 5
maxTicketCount: 10
logExpectation: true
prizes:
- prob: 1
reward: 4999
name: 天选之人
- prob: 5
reward: 1000
name: 至尊欧皇
# 其余奖项省略

开启 logExpectation 后,插件会在配置更新时向控制台输出单张数学期望、方差、各档命中率以及购买 maxTicketCount 张时的亏损率、平手率与盈利率。可据此调整奖项权重。


大乐透玩法

规则概述

  • 每次下注消耗至少 20 积分,选择 1~18 中的 7 个数字下注
  • 前 5 个数字为「前区」,不能重复;后 2 个数字为「后区」,可以重复
  • 每人可多次下注,全部独立结算
  • 开奖后按前区命中数量加权分配奖池
  • 后区每猜中 1 个,固定奖励 5 积分
  • 参与人数大于 3 人后,每增加 1 人,奖池额外增加 5 积分

开奖机制

前区命中数量对应权重如下:

前区命中数 权重
5 80
4 40
3 20
2 10
1 5
0 1

奖池按各玩家的权重占比分配,后区每命中一个额外固定奖励 5 积分,不影响奖池分配。


刮刮乐玩法

规则概述

  • 消耗积分购买刮刮乐,系统自动返回中奖结果,可一次购买多张
  • 单次购买数量上限由 maxTicketCount 控制;由于方差较大,购买数量过多时盈利概率会显著上升
  • 可参考控制台输出的期望与方差信息调整奖项权重设置

使用流程

大乐透

查看当前群聊大乐透下注情况

1
大乐透

返回当前下注次数、奖池积分与下注号码列表。若当前群聊暂无下注记录,则返回规则文本。

进行一次大乐透下注

1
大乐透.下注 25

若不输入下注积分数,则默认消耗 20 积分。下注后按提示输入号码:

1
1 2 3 4 5 1 1

号码范围为 1~18,前 5 个不能重复,后 2 个可以重复。号码的输入等待时长为 30 秒,超时视为取消下注。

若用户尚无积分记录,插件会赠送 20 积分作为初始资金并提示重新下注。

开奖

1
大乐透.开奖

机器人自动生成开奖号码,结算所有下注者的奖励,并清空当前群聊的下注记录。中奖与否均可获得奖池份额。

刮刮乐

购买刮刮乐

1
刮刮乐 5

默认购买 1 张,可选购买数量,购买后即时返回结果。若购买数量超过 maxTicketCount,插件会提示上限并以 maxTicketCount 计算。


技术实现

技术栈

技术 用途
Koishi 机器人框架
TypeScript 开发语言
monetary 积分服务
数据库服务 下注与积分持久化

数据表结构

插件通过 ctx.model.extend 扩展了两张表。

lotto 表存储大乐透下注记录:

字段 类型 说明
id string 主键,取值为时间戳字符串
uid unsigned 用户 ID
channelId string 群聊频道 ID
numbers json 下注号码(前区与后区)
cost unsigned 下注消耗的积分

monetary 表为 monetary 服务的数据表,插件通过 currency 定位货币字段。

核心设计

奖池计算

奖池由下注总额与额外奖励两部分构成:

1
2
3
4
5
6
function calcPoolFromDB(bets: any[]) {
const basePool = bets.reduce((sum, bet) => sum + (bet.cost || 0), 0)
const uniqueUids = new Set(bets.map(bet => bet.uid))
const extra = Math.max(uniqueUids.size - 3, 0) * 5
return basePool + extra
}

基础奖池为全部下注金额之和;当参与人数超过 3 人时,每多 1 人额外增加 5 积分。

权重分配

开奖时按前区命中数查表得到权重,累加后按占比分配奖池:

1
2
const weightTable = { 5: 80, 4: 40, 3: 20, 2: 10, 1: 5, 0: 1 }
const weight = weightTable[frontHit] ?? 1

每位玩家的份额为 Math.ceil(pool * weight / totalWeight),再叠加后区奖励。

刮刮乐权重抽取

刮刮乐按权重随机抽取奖项:

1
2
3
4
5
6
7
8
function drawPrize(prizes) {
const totalWeight = prizes.reduce((sum, p) => sum + p.prob, 0)
let r = Math.random() * totalWeight
for (const p of prizes) {
if (r < p.prob) return p
r -= p.prob
}
}

prob 为相对权重而非绝对概率,各奖项概率为 prob / 权重总和。

期望与方差输出

logExpectation 开启后,插件计算单张期望与方差,并通过卷积计算购买 maxTicketCount 张时的收益分布,输出亏损率、平手率与盈利率。该信息仅用于控制台显示,不参与实际开奖。

积分读写

积分通过 monetary 表读写,扣减前校验余额:

1
2
3
const newValue = row.value + change
if (newValue < 0) return false
await ctx.database.set('monetary', { uid, currency }, { value: newValue })

若用户尚无积分记录且操作为非扣减,插件会先创建记录。


注意事项

1. 服务依赖

插件声明了 inject: ['database', 'monetary'],下注与积分读写均依赖数据库与 monetary 服务。若未启用二者,相关指令不可用。

2. 货币字段一致

currency 需与 monetary 中定义的货币字段名称保持一致。若不一致,插件将无法正确读写积分,并会创建独立的积分记录。

3. 初始资金

当用户尚无积分记录时,插件会赠送 20 积分作为初始资金。该行为在下注与刮刮乐指令中均会触发。

4. 号码输入

下注号码需为 7 个 1~18 之间的数字,前 5 个不可重复,后 2 个可重复,以空格分隔。号码输入等待时长为 30 秒,超时视为取消。

5. 数据清理

开奖后插件会清除当前群聊的全部下注记录。群聊与群聊之间的下注数据相互独立。

6. 方差风险

刮刮乐的方差较大,购买数量过多时盈利概率会显著上升。建议通过 logExpectation 查看收益分布后再调整奖项权重。


常见问题

Q1:安装后输入指令无响应?

依次检查以下项:

  • 插件是否已在控制台启用
  • monetary 服务是否已安装并启用
  • database-sqlite 等服务是否已启用
  • 机器人是否已成功连接至目标平台
  • 指令是否完整输入(父指令 大乐透 与子指令之间以点号分隔)

Q2:提示「你的 default 不足」?

说明当前积分不足以完成本次操作。可通过 monetary 相关的指令获取积分,或检查 currency 配置是否与实际使用的货币字段一致。

Q3:开奖后群聊下注记录消失了?

属于预期行为。开奖后插件会清除当前群聊的全部下注记录,以便开始新的对局。

Q4:刮刮乐实际中奖率与设置不符?

prob 为相对权重而非绝对概率,实际概率为 prob / 权重总和。可开启 logExpectation 查看各档实际命中率。

Q5:能否调整大乐透的奖池规则?

当前版本的大乐透权重表与奖池规则写死在源码中,不支持通过配置修改。刮刮乐的奖项、单价与数量上限可通过配置调整。

Q6:下注后未输入号码会怎样?

号码输入等待时长为 30 秒。超时后视为取消下注,本次下注不会扣除积分。


开发计划

  • 支持自定义大乐透奖池规则
  • 增加更多彩票玩法
  • 支持跨群聊奖池
  • 提供更丰富的中奖播报样式

获取工具