import { describe, expect, it } from 'vitest' import { CREW_CHANCE, generateStartingCrew, makeCrewName, rollCrewMember, startingCrewRng } from './crew' import { explorationRng } from './world' describe('generateStartingCrew', () => { it('gives a new game 2–3 members, each with exactly one good and one bad trait', () => { for (const seed of [1, 7, 42, 999]) { const crew = generateStartingCrew(startingCrewRng(String(seed))) expect(crew.length).toBeGreaterThanOrEqual(2) expect(crew.length).toBeLessThanOrEqual(3) for (const member of crew) { expect(member.name.trim().length).toBeGreaterThan(0) expect(member.role.trim().length).toBeGreaterThan(0) expect(member.traitGood.trim().length).toBeGreaterThan(0) expect(member.traitBad.trim().length).toBeGreaterThan(0) } // Names are unique within the crew; roles too (drawn without replacement). expect(new Set(crew.map((m) => m.name)).size).toBe(crew.length) expect(new Set(crew.map((m) => m.role)).size).toBe(crew.length) } }) it('is deterministic under a fixed seed and differs across seeds', () => { const a = generateStartingCrew(startingCrewRng('42')) expect(generateStartingCrew(startingCrewRng('42'))).toEqual(a) // same save → same crew const b = generateStartingCrew(startingCrewRng('43')) expect(JSON.stringify(b)).not.toBe(JSON.stringify(a)) // different seed → different crew }) }) describe('rollCrewMember', () => { it('finds a member with the documented probability and never reuses a taken name', () => { const rng = startingCrewRng('42') let found = 0 for (let i = 0; i < 1000; i++) found += rollCrewMember(rng, new Set()) ? 1 : 0 // 18% ± a comfortable band — catches an inverted or missing chance without flaking. expect(found).toBeGreaterThan(90) expect(found).toBeLessThan(270) const taken = new Set(['Ava Vance']) for (let i = 0; i < 50; i++) { const member = rollCrewMember(startingCrewRng(String(i)), taken) if (member) expect(member.name).not.toBe('Ava Vance') // never reuses a roster name } }) it('is deterministic per save+planet when drawn from the exploration stream', () => { const first = rollCrewMember(explorationRng('42', 13), new Set()) expect(rollCrewMember(explorationRng('42', 13), new Set())).toEqual(first) // same save+planet → same result // Across many planets both outcomes occur (someone is found, someone isn't). let found = 0 for (let planetId = 1; planetId <= 200; planetId++) { if (rollCrewMember(explorationRng('42', planetId), new Set())) found++ } expect(found).toBeGreaterThan(0) expect(found).toBeLessThan(200) }) it('keeps the crew chance documented and sane', () => { expect(CREW_CHANCE).toBeGreaterThanOrEqual(0.15) expect(CREW_CHANCE).toBeLessThanOrEqual(0.2) }) }) describe('makeCrewName', () => { it('never reuses a taken name and always terminates', () => { const rng = startingCrewRng('7') const taken = new Set() for (let i = 0; i < 40; i++) makeCrewName(rng, taken) expect(taken.size).toBe(40) }) })