66 lines
3.1 KiB
TypeScript
66 lines
3.1 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { LiteHashDRBG, RandUtil } from '@sammo-ts/common';
|
|
import { observeAiRng, type AiTraceStep } from '../src/turn/ai/generalAi/trace.js';
|
|
|
|
describe('AI RNG observation', () => {
|
|
it.each(['audit-a', 'audit-b', 'audit-c'])(
|
|
'preserves results, identity and following random state for %s',
|
|
(seed) => {
|
|
const baseline = new RandUtil(LiteHashDRBG.build(seed));
|
|
const events: AiTraceStep[] = [];
|
|
const observed = observeAiRng(new RandUtil(LiteHashDRBG.build(seed)), (event) => events.push(event));
|
|
const candidates = [
|
|
{ id: 1, secret: 'hidden' },
|
|
{ id: 2, secret: 'hidden' },
|
|
];
|
|
const draw = (rng: RandUtil) => [
|
|
rng.nextFloat1(),
|
|
rng.nextBool(0),
|
|
rng.nextBool(1),
|
|
rng.nextBool(0.5),
|
|
rng.nextBool(0.3),
|
|
rng.nextRange(-10, 100),
|
|
rng.nextRangeInt(1, 30),
|
|
rng.nextInt(1, 2),
|
|
rng.nextIntInclusive(0),
|
|
rng.choice([42]),
|
|
rng.choice(candidates),
|
|
rng.choice(new Set(candidates)),
|
|
rng.choice({ first: candidates[0]!, second: candidates[1]! }),
|
|
rng.choiceUsingWeight({ a: 0, b: 2, c: 3 }),
|
|
rng.choiceUsingWeightPair([
|
|
[candidates[0]!, 2],
|
|
[candidates[1]!, 3],
|
|
]),
|
|
rng.shuffle(candidates),
|
|
];
|
|
const expected = draw(baseline);
|
|
const actual = draw(observed);
|
|
expect(actual).toEqual(expected);
|
|
expect(actual[10]).toBe(expected[10]);
|
|
expect(events).toHaveLength(16); // helper 내부 호출을 별도 판단으로 중복 기록하지 않는다.
|
|
expect(JSON.stringify(events)).not.toContain('hidden');
|
|
expect(JSON.stringify(events)).not.toContain(seed);
|
|
expect(events[10]).toMatchObject({
|
|
kind: 'RNG',
|
|
method: 'choice',
|
|
result: { entityId: candidates.indexOf(actual[10] as (typeof candidates)[number]) + 1 },
|
|
});
|
|
expect(observed.nextFloat1()).toBe(baseline.nextFloat1());
|
|
}
|
|
);
|
|
it('does not fabricate successful observations for failed choices', () => {
|
|
const events: AiTraceStep[] = [];
|
|
const rng = observeAiRng(new RandUtil(LiteHashDRBG.build('error')), (step) => events.push(step));
|
|
expect(() => rng.choice([])).toThrow('Empty items');
|
|
expect(events).toEqual([]);
|
|
});
|
|
it('marks unsupported result shapes instead of copying arbitrary debug data', () => {
|
|
const events: AiTraceStep[] = [];
|
|
const rng = observeAiRng(new RandUtil(LiteHashDRBG.build('projection')), (step) => events.push(step));
|
|
const value = { secret: 'not a candidate id' };
|
|
expect(rng.choice([value])).toBe(value);
|
|
expect(events).toEqual([{ kind: 'RNG', method: 'choice', parameters: null, result: { unprojected: true } }]);
|
|
});
|
|
});
|