60c21ae346
rules 是纯计算,src/rules 共 1761 行:方法.md 第六部分的全部公式、第七部分的余额与扣次、4.2 的入口分配,以及种子与规则登记表。 预期值有三个来源:方法.md 的手算例;tests/fixtures/rules/ 下不看实现写的 Python 脚本;参考项目 calc15、calc14b 里与现行方法一致的部分。审阅做了两轮,每轮都换一个全新上下文的子 Agent,提出的问题都已修复或写明不修的理由。 用户确认的约定(原话"1–8 都同意"):入池、采纳、消融中发现都用严格不等号;成本用 max(0, ĝ₀);没测的格附"含它的最好组合"的净值;战绩在分数原值上判。三个数改为严格递增后,删掉了尖峰的全部专门处理(bfb96ba)。vocab 里 Range 的说明同步改为严格递增。 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
66 lines
2.4 KiB
TypeScript
66 lines
2.4 KiB
TypeScript
import assert from "node:assert/strict";
|
||
import { readFileSync } from "node:fs";
|
||
import type { Range } from "../src/vocab/index.ts";
|
||
|
||
// tests/fixtures/rules/ 下的预期值由独立的 Python 脚本照 docs/方法.md 和约定表算出(脚本开头注释写了格式)。
|
||
// 这里只负责读文件和比较数字;把场景映射到 rules 的函数上,在各个 rules-fixtures-*.test.ts 里做。
|
||
|
||
export interface Scenario {
|
||
name: string;
|
||
section: string;
|
||
params: Record<string, any>;
|
||
input: Record<string, any>;
|
||
expected: any;
|
||
note: string;
|
||
}
|
||
|
||
export interface FixtureFile {
|
||
generator: string;
|
||
tolerance: Record<string, any>;
|
||
models?: Record<string, { params: Record<string, any>; input: Record<string, any> }>;
|
||
scenarios: Scenario[];
|
||
}
|
||
|
||
export function loadFixture(file: string): FixtureFile {
|
||
return JSON.parse(readFileSync(new URL(`./fixtures/rules/${file}`, import.meta.url), "utf8")) as FixtureFile;
|
||
}
|
||
|
||
/** 按名字取场景,并登记"已覆盖";每个测试文件最后核对 JSON 里的场景都被取过。 */
|
||
export function scenarioPicker(fixture: FixtureFile): {
|
||
get: (name: string) => Scenario;
|
||
unused: () => string[];
|
||
} {
|
||
const used = new Set<string>();
|
||
return {
|
||
get(name) {
|
||
const s = fixture.scenarios.find((x) => x.name === name);
|
||
assert.ok(s, `JSON 里没有场景 ${name}`);
|
||
used.add(name);
|
||
return s;
|
||
},
|
||
unused: () => fixture.scenarios.map((s) => s.name).filter((n) => !used.has(n)),
|
||
};
|
||
}
|
||
|
||
/** 绝对误差 tol 以内算相同。 */
|
||
export function close(actual: number, expected: number, tol: number, what: string): void {
|
||
assert.ok(
|
||
Number.isFinite(actual) && Math.abs(actual - expected) <= tol,
|
||
`${what}:实现 ${actual},预期 ${expected},差 ${actual - expected},容差 ${tol}`,
|
||
);
|
||
}
|
||
|
||
/** 网格值:|差| ≤ 1e-12 + 1e-9·|值|(ablation.json tolerance.grid)。 */
|
||
export function closeGrid(actual: number, expected: number, what: string): void {
|
||
close(actual, expected, 1e-12 + 1e-9 * Math.abs(expected), what);
|
||
}
|
||
|
||
export function closeArray(actual: readonly number[], expected: readonly number[], tol: number, what: string): void {
|
||
assert.equal(actual.length, expected.length, `${what}:长度`);
|
||
actual.forEach((a, i) => close(a, expected[i]!, tol, `${what}[${i}]`));
|
||
}
|
||
|
||
export function range(r: { q1: number; q5: number; q9: number }): Range {
|
||
return { q1: r.q1, q5: r.q5, q9: r.q9 };
|
||
}
|