import { describe, expect, it } from 'vitest'; import { REFUSE_QUIP, REFUSE_BINNED_QUIP, tallyHaul, sorterVerdict } from '../src/rules/haul'; import { rollDrops } from '../src/rules/floorScore'; import { SeededRNG } from '../src/core/SeededRNG'; import type { Drop } from '../src/rules/floorScore'; const d = (kind: Drop['kind'], value = 0, label = kind): Drop => ({ kind, value, label }); describe('the coin sorter', () => { it('counts money and nothing else', () => { const t = tallyHaul([d('note', 20), d('coins', 7), d('curio'), d('phone'), d('baggie')], true); expect(t.counted).toBe(27); }); it('jams on the objects, and that is the whole joke', () => { const t = tallyHaul([d('curio'), d('curio'), d('note', 5)], false); expect(t.jams).toBe(2); expect(t.lines.filter((l) => l.fate === 'jammed')).toHaveLength(2); expect(t.lines.find((l) => l.fate === 'counted')?.value).toBe(5); }); it('the phone goes sideways into lost property, never into the total', () => { const t = tallyHaul([d('phone')], false); expect(t.lines[0]!.fate).toBe('rejected'); expect(t.counted).toBe(0); }); it('the bag gets a different line depending on what you did with it', () => { expect(tallyHaul([d('baggie')], true).lines[0]!.quip).toBe(REFUSE_QUIP); expect(tallyHaul([d('baggie')], false).lines[0]!.quip).toBe(REFUSE_BINNED_QUIP); }); it('keeps the order you picked things up in — the desk replays your night', () => { const haul: Drop[] = [ { kind: 'note', value: 10, label: 'a' }, { kind: 'curio', value: 0, label: 'b' }, { kind: 'coins', value: 3, label: 'c' }, ]; expect(tallyHaul(haul, false).lines.map((l) => l.label)).toEqual(['a', 'b', 'c']); }); it('is deterministic — the same night tells the same joke', () => { const haul = rollDrops(new SeededRNG(4207).stream('sweep'), 40); expect(tallyHaul(haul, true)).toEqual(tallyHaul(haul, true)); }); it('every line has words, whatever the machine made of it', () => { const haul = rollDrops(new SeededRNG(9).stream('sweep'), 60); for (const line of tallyHaul(haul, true).lines) { expect(line.quip.length).toBeGreaterThan(5); expect(line.label.length).toBeGreaterThan(0); } }); it('an empty haul is handled, and says so', () => { const t = tallyHaul([], false); expect(t).toMatchObject({ counted: 0, jams: 0 }); expect(t.lines).toEqual([]); expect(sorterVerdict(t)).toContain('nothing in the pockets'); }); it('the sign-off escalates with how badly you treated the machine', () => { const clean = sorterVerdict(tallyHaul([d('note', 5)], false)); const rough = sorterVerdict(tallyHaul(Array(5).fill(d('curio')), false)); expect(clean).not.toBe(rough); expect(rough).toContain('looked at'); }); it('a real swept night always gives the desk something to do', () => { for (const seed of [4207, 4208, 99]) { const t = tallyHaul(rollDrops(new SeededRNG(seed).stream('sweep'), 40), true); expect(t.lines.length).toBeGreaterThan(0); expect(t.counted).toBeGreaterThan(0); } }); });