@idlekit /核心
空闲游戏经济设计的仿真与分析基元。
它编译场景,执行运行,并评估长期kpi和面向设计的度量。
官方支持v1: Bun >=1.4.2 只有。Node.js和浏览器运行时不是v1兼容性契约的一部分。
bun add @idlekit/core
需要面包 >=1.4.2.
使用此包时,您需要:
- 场景验证/编译
- 仿真回路和
stepOnce - 内置策略和调优原语
- 增长、里程碑、会话模式和蒙特卡罗分析
- 假设的LTV估计通过
deriveMonetizationConfig,estimateLtvPerUser和estimateLtvDistribution
LTV估计
导入三个LTV函数 retentionAtDay / progressionFactor 帮助者和
MonetizationConfig, LtvEstimateArgs, LtvDistributionArgs, LtvPointEstimate 和
LtvDistribution 类型的 @idlekit/core。通过一项 ScenarioV1 来 deriveMonetizationConfig;
传递返回的配置, horizonDays 和 progression 来 estimateLtvPerUser.
estimateLtvDistribution 还读 draws, quantiles 和 seed;一个被忽略的人会后退
来 config.uncertainty,明确的一方获胜。它总是采样: config.uncertainty.enabled
是调用者是否调用它的开关,如在CLI中。CLI使用相同的实现。
LTV是未贴现的累积价值。的地平线 H 日数涵盖安装日数 [0, H)第0天
根据定义,安装日是否处于活动状态,如遥测和 idk calibrate。分数末位
每一天的留存率是成比例的,所以0.5天是安装日的一半。早些时候发布
覆盖的天 [1, H],降低了安装日收入,并在D1上收取了0.5天的期限
保留。同样的地平线现在增加了安装日和掉落日 H,因此LTV值会发生变化
通常会上升。
输入是假设性的假设,每个已安装的用户使用一种呼叫者选择的货币。 默认情况下,用户留存率和收益都不会受到影响。净收入为平台费用 毛利率;然后LTV减去CPI,包括在零日范围内。有限的发展 被夹在0…1。分位数波段描述的是对假设的敏感性,而不是拟合置信度 享受/保留的时间间隔或证据。
同步限制:0..36500天,1…10万次抽奖,最多1000万次抽奖日(至少
每个图纸一个工作单位)。分位数必须是1…0..1中的101个不同的值。每个输出键为
确切的百分比(q50, q90, q2.5 为0.025, q12.4 对于0.124),所以键永远不会碰撞或
圆的。种子必须是0..4294967295之间的整数;还有一些种子是扔出来的,而不是折叠起来的。
retentionAtDay 需要一个有限的日期>= 0和一个有效的曲线(第0天是1,在(0,1)中的任何一天读
D1,从D1开始的锚点为线性); progressionFactor 拒绝南
log和一个非有限且正的空间,并处理一个无限值的log(零值)
没有进展。无效输入和数字溢出引发 RangeError。抽样收缩
不一致的相关矩阵走向独立。
完全队列保留观察和安全贴片应用可在核心;
公众 idk calibrate CLI共享观察和规划实现。
公共估计配置也接受分位数端点。点估计验证整体
配置,包括分布读取的不确定性默认值;关键的区别和
当分发使用它们时检查种子范围。 ScenarioV1 授权分位数保留
他们现有的 (0, 1) 模式时传递给 deriveMonetizationConfig.
部分场景保留覆盖保留CLI的独立默认值。例如,
d1: 0.1 单独保留默认 d7: 0.2;估计保留了现有的插值。
显式完整的场景曲线仍然遵循场景排序验证。直接估计
配置验证每个速率的范围,而不向已完成的默认值添加排序约束。
点投影和分布以同样的方式插值这样一条上升曲线:1
共享保留冲击缩放每个锚,采样不再剪辑后一个锚
前一个,所以零不确定性分布等于点投影。
观察队列保留
进口 observeCohort, compareRetention, planRetentionPatch 和 applyRetentionPatch
从 @idlekit/core. CohortActivityRow 需要 userId 和整数 day; active
默认为true。Money字段是可选的,永远不会被这些保留api所匹配。
每个已安装的用户都需要一个零日行,包括不返回者,调用者必须这样做
提供具有通用的、完全成熟的完整活动导出 observedThroughDay.
API检查一致性;它不能证明出口的完整性。
observeCohort(rows, cutoff) 将未来的汇率/间隔保留为空。 planRetentionPatch
返回 ready, unavailable 或者 requires-review;每一个未被观察到的目标日都需要
明确保留的假设。与保留值上升或冲突的观测值
保持不变,不能成为自动补丁。 applyRetentionPatch({ scenario, observation }) 重新计算计划,验证并返回独立的场景副本。
如果补丁还没有准备好,它就会抛出问题,因为它永远不会取代收益、费用、CPI、不确定性,
队列大小或未观察到的保留。 assertCohortObservation 验证序列化的输入。
游戏专用线下结算
simulateSessionPattern({ scenario, pattern, prepareOffline }) 可选地调用同步
SessionOfflinePreparer 一次非空缺席。它接收 ctx, model,状态快照;
seconds, wallStartT, wallEndT 以及当前的政策快照。数值原型是
保存在快照中;将共享引擎、上下文和模型视为不可变的。
在先前封顶的间隙之后使用显式墙壁时间戳进行到期检查。
返回任意一个 model, stepSec 和 policy。它们只适用于这个缺口;遗漏继承
场景设置。 policy 代替帽/腐,所以 {} 删除两个。会议的行动
政策不变 scenario.run.offline.actions;回调不能覆盖它。替代
模型声明自己的 clocks.respondsTo 的需求。SDK仍然执行奖励,目标,
直到检查,行动过滤,观察和墙/奖励会计。
具有一个原子沉降的游戏可以在快照上计算它并返回一个模型
将其结果导入到一个奖励tick中 stepSec 等于它的有效积分秒
(当信用为零时使用正步骤)。在刻度边界处检查停止;一个点的
解决方案不会暴露中间游戏状态。准备本身不能给你任何好处
外部奖励:零积分或已经达成的目标可能根本不会执行任何转换。
对于部分解决方案或内部差距目标,提供一个每个较小刻度演变的模型。
这是一个SDK回调,而不是一个JSON场景字段。默认会话路径不变。 看到 大数脱机的例子.
简单的例子
import {
analyzeGrowth,
compileScenario,
createModelRegistry,
createNumberEngine,
defineModelFactory,
runScenario,
validateScenarioV1,
} from "@idlekit/core";
type Vars = { owned: number };
const linearFactory = defineModelFactory<number, "COIN", Vars>({
id: "linear",
version: 1,
create() {
return {
id: "linear",
version: 1,
income(ctx, state) {
return {
unit: ctx.unit,
amount: ctx.E.add(1, Number(state.vars.owned ?? 0)),
};
},
actions() {
return [];
},
};
},
});
const registry = createModelRegistry([linearFactory]);
const scenario = {
schemaVersion: 1,
unit: { code: "COIN" },
policy: { mode: "drop" },
model: { id: "linear", version: 1 },
initial: {
wallet: { unit: "COIN", amount: "0" },
vars: { owned: 0 },
},
clock: { stepSec: 1, durationSec: 60 },
};
const validated = validateScenarioV1(scenario, registry);
if (!validated.ok || !validated.scenario) {
throw new Error(`scenario should validate: ${JSON.stringify(validated.issues)}`);
}
const E = createNumberEngine();
const compiled = compileScenario({
E,
scenario: validated.scenario,
registry,
});
const run = runScenario(compiled);
const growth = analyzeGrowth({ run, series: "money", windowSec: 10 });
console.log(
JSON.stringify({
endMoney: E.toString(run.end.wallet.money.amount),
growthSegments: growth.segments,
}),
);
运行时
在内存中的持续时间 runScenario 或者 applyOfflineSeconds 因此,进口 runElapsedSec 从 @idlekit/core 和阅读 runElapsedSec(run)。它使用 run.stop.elapsedSec,滴答秒的总和。 state.t 是绝对奖励时间戳;在一个大的起始时间戳下, run.end.t - run.start.t 可以圆不同。例如,10个0.1秒的滴答声从 1e15 模拟时间戳相差1.25秒时的大约一秒。没有停止记录的结果退回到该时间戳差异。步进预算停止可以在要求的持续时间之前缩短单个运行;检查 run.stop?.reason 或使用 assertHorizonReached(run, label) 当需要全视界时。
为 simulateSessionPattern,读 summary.elapsedSec 对于墙时间, summary.activeSec 对于活动tick秒,和 summary.offlineCreditedSec 对于离线奖励滴答秒。总 session.run 没有单次停车记录,所以呢 runElapsedSec(session.run) 使用时间戳回退。 summary.rewardSec 也是奖励时间戳的差异;当您需要模拟滴答秒时,使用活动和离线记入总数。
@idlekit/core 是作为一个bunfirst ESM包来维护的。
文档
- 存储库: github.com/imjlk/idlekit
- 产品路线图: 文档/ roadmap.md
- 场景指南: 文档/ scenario-and-tuning.md
- 典型的工作例子: 例子/教程/ 14-orbital-foundry-v1.json
- 适配器模式示例: 例子/适配器模式