not-tonight/tests/floorScore.test.ts

48 lines
1.7 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
DROPS_BASE,
DROPS_MAX,
NOTE_VALUES,
rollDrops,
} from '../src/rules/floorScore';
import { SeededRNG } from '../src/core/SeededRNG';
describe('the floor score', () => {
it('a busier night is a messier floor, up to a limit', () => {
const rng = new SeededRNG(1);
expect(rollDrops(rng.stream('a'), 0).length).toBe(DROPS_BASE);
expect(rollDrops(rng.stream('b'), 24).length).toBe(DROPS_BASE + 4);
expect(rollDrops(rng.stream('c'), 500).length).toBe(DROPS_MAX);
});
it('is deterministic per stream — the same night drops the same floor', () => {
const a = rollDrops(new SeededRNG(7).stream('sweep'), 40);
const b = rollDrops(new SeededRNG(7).stream('sweep'), 40);
expect(a).toEqual(b);
});
it('cash is real denominations; curios, phones and baggies are worth $0', () => {
for (let seed = 1; seed <= 20; seed++) {
for (const d of rollDrops(new SeededRNG(seed).stream('s'), 60)) {
if (d.kind === 'note') expect(NOTE_VALUES).toContain(d.value);
else if (d.kind === 'coins') expect(d.value).toBeGreaterThan(0);
else expect(d.value).toBe(0);
}
}
});
it('at most one baggie and one phone per night', () => {
for (let seed = 1; seed <= 30; seed++) {
const drops = rollDrops(new SeededRNG(seed).stream('s'), 60);
expect(drops.filter((d) => d.kind === 'baggie').length).toBeLessThanOrEqual(1);
expect(drops.filter((d) => d.kind === 'phone').length).toBeLessThanOrEqual(1);
}
});
it('every drop has words — the label IS the reward for curios', () => {
for (const d of rollDrops(new SeededRNG(11).stream('s'), 30)) {
expect(d.label.length).toBeGreaterThan(5);
}
});
});