import { describe, it, expect, vi } from 'vitest' import { getEmbeddedDropTables } from '../src/game/embedded-drop-tables.ts' import { GameEngine } from '../src/game/engine.ts' import type { MonsterKind } from '../src/game/monsters.ts' import { getMonsterTreasureClass } from '../src/game/monsters.ts' import * as dropPipeline from '../src/game/drop-pipeline.ts' import { executeDropPipeline } from '../src/game/drop-pipeline.ts' import { rollBaseUpgrade, DifficultyLevelsTable, parseDifficultyLevelsTable, ITEM_UPGRADE_FLAG_EXCEPTIONAL, ITEM_UPGRADE_FLAG_ELITE, type UpgradableItemBase, } from '../src/game/item-upgrade.ts' import { D2Rng } from '../src/game/d2-rng.ts' describe('Challenger M1 Adversarial Stress Test Suite', () => { const dropTables = getEmbeddedDropTables() const dummyTerrain = { widthPx: 1000, heightPx: 1000, overlap: () => 0, } const idleInput = { movement: { x: 0, y: 0 }, attacking: false, pickingUp: false, talking: false, saving: false, loading: false, digits: [], } function createTestEngine( difficulty: 'normal' | 'nightmare' | 'hell' = 'normal', customMonsterKinds?: Map, customDropTables?: any, ) { return new GameEngine(dummyTerrain, { spawn: { x: 0, y: 0 }, stats: [], xpTable: [0, 100, 200], dropTables: customDropTables ?? dropTables, difficulty, monsterKinds: customMonsterKinds, skills: [], npcDefs: [], questDefs: [], combatOptions: {} as any, talkRadius: 50, pickupRadius: 50, inventoryCols: 10, inventoryRows: 4, }) } // ========================================================================= // Dimension 1: Fuzz and test invalid inputs to GameEngine drop processing // ========================================================================= describe('Dimension 1: Invalid & Adversarial Inputs to GameEngine Drop Processing', () => { it('rejects missing monsterLevel (undefined, null)', () => { const engine = createTestEngine('normal') for (const mlvl of [undefined, null]) { engine.world.pendingKills = [ { kind: 'kill', x: 10, y: 10, subjectId: 'fallen1', monsterRank: 'normal', monsterLevel: mlvl as any, } as any, ] expect(() => engine.tick(idleInput)).toThrow( /GameEngine drop failure: missing or invalid monsterLevel for death event/, ) } }) it('rejects invalid numeric monsterLevel (0, negative, NaN, strings, objects)', () => { const invalidLevels = [ 0, -1, -100, -Infinity, Number.NaN, '10', '', false, true, {}, [], ] for (const mlvl of invalidLevels) { const engine = createTestEngine('normal') engine.world.pendingKills = [ { kind: 'kill', x: 10, y: 10, subjectId: 'fallen1', monsterRank: 'normal', monsterLevel: mlvl as any, } as any, ] expect(() => engine.tick(idleInput)).toThrow( /GameEngine drop failure: missing or invalid monsterLevel for death event/, ) } }) it('fuzzes unknown monsters and injection payloads in subjectId and superUniqueId', () => { const adversarialIds = [ '__proto__', 'constructor', 'toString', 'valueOf', '', ' ', 'DROP TABLE monsters;', '', 'null', 'undefined', 'non_existent_monster_666', '!!!invalid!!!', '1234567890', ] for (const id of adversarialIds) { const engine = createTestEngine('normal') engine.world.pendingKills = [ { kind: 'kill', x: 10, y: 10, subjectId: id, monsterRank: 'normal', monsterLevel: 10, } as any, ] expect(() => engine.tick(idleInput)).toThrow( /GameEngine drop failure: unknown monster/, ) } }) it('rejects unknown superUnique with unknown subjectId', () => { const engine = createTestEngine('normal') engine.world.pendingKills = [ { kind: 'kill', x: 10, y: 10, superUniqueId: 'NonExistentSuperUnique_XYZ', monsterRank: 'unique', monsterLevel: 50, } as any, ] expect(() => engine.tick(idleInput)).toThrow( /GameEngine drop failure: unknown monster \(subjectId: "undefined", superUniqueId: "NonExistentSuperUnique_XYZ"\) not found in canonical drop tables/, ) }) it('rejects corrupt combat monster with empty getTreasureClass() result', () => { const customKinds = new Map() customKinds.set('corrupt_combatant', { id: 'corrupt_combatant', baseId: 'corrupt_combatant', name: 'Corrupt Combatant', boss: false, levels: { normal: 10, nightmare: 40, hell: 70 }, treasureClasses: ['Act 1 H2H A'], getTreasureClass: () => '', } as any) const engine = createTestEngine('normal', customKinds) engine.world.pendingKills = [ { kind: 'kill', x: 10, y: 10, subjectId: 'corrupt_combatant', monsterRank: 'normal', monsterLevel: 10, } as any, ] expect(() => engine.tick(idleInput)).toThrow( /GameEngine drop failure: corrupt or unresolvable treasure class for monster "corrupt_combatant" on difficulty "normal"/, ) }) it('rejects combat monster resolving to unregistered TC', () => { const customKinds = new Map() customKinds.set('unregistered_tc_demon', { id: 'unregistered_tc_demon', baseId: 'unregistered_tc_demon', name: 'Unregistered Demon', levels: { normal: 10, nightmare: 40, hell: 70 }, treasureClasses: ['Totally_Fake_TC_Name_999'], } as any) const engine = createTestEngine('normal', customKinds) engine.world.pendingKills = [ { kind: 'kill', x: 10, y: 10, subjectId: 'unregistered_tc_demon', monsterRank: 'normal', monsterLevel: 10, } as any, ] expect(() => engine.tick(idleInput)).toThrow( /GameEngine drop failure: treasure class "Totally_Fake_TC_Name_999" resolved for monster "unregistered_tc_demon" not found in drop tables/, ) }) it('asserts executeDropPipeline is NEVER called when validation fails', () => { const dropSpy = vi.spyOn(dropPipeline, 'executeDropPipeline') const engine = createTestEngine('normal') const invalidEvents = [ { kind: 'kill', x: 0, y: 0, subjectId: 'fallen1', monsterLevel: 0 }, { kind: 'kill', x: 0, y: 0, subjectId: 'fallen1', monsterLevel: -1 }, { kind: 'kill', x: 0, y: 0, subjectId: 'unknown_mob', monsterLevel: 10 }, ] for (const ev of invalidEvents) { engine.world.pendingKills = [ev as any] expect(() => engine.tick(idleInput)).toThrow() } expect(dropSpy).not.toHaveBeenCalled() dropSpy.mockRestore() }) }) // ========================================================================= // Dimension 2: Verify all 251 authentic zero-drop critters and Ancient Barbarians // ========================================================================= describe('Dimension 2: All 251 Authentic Zero-Drop Critters & Ancient Barbarians', () => { it('verifies exactly 251 monsterKinds have authentic empty TCs on Normal', () => { const normalEmptyKinds: string[] = [] for (const [id, kind] of dropTables.monsterKinds) { const tc = typeof kind.getTreasureClass === 'function' ? kind.getTreasureClass('normal', 1) : '' if (!tc || tc.trim() === '') { normalEmptyKinds.push(id) } } expect(normalEmptyKinds.length).toBe(251) }) it('slays all 251 authentic zero-drop critters on Normal without throwing, yielding 0 drops', () => { const engine = createTestEngine('normal') for (const [id, kind] of dropTables.monsterKinds) { const tc = typeof kind.getTreasureClass === 'function' ? kind.getTreasureClass('normal', 1) : '' if (!tc || tc.trim() === '') { engine.world.pendingKills = [ { kind: 'kill', x: 10, y: 10, subjectId: id, monsterRank: 'normal', monsterLevel: 1, } as any, ] expect(() => engine.tick(idleInput), `Critter ${id} failed`).not.toThrow() expect(engine.metrics.dropsRolled, `Critter ${id} rolled drops`).toBe(0) expect(engine.ground.length, `Critter ${id} dropped ground items`).toBe(0) } } }) it('slays all 3 Arreat Summit Ancient Barbarians across Normal, Nightmare, and Hell with 0 drops', () => { const ancientSuperUniques = [ 'Ancient Barbarian 1', 'Ancient Barbarian 2', 'Ancient Barbarian 3', ] for (const diff of ['normal', 'nightmare', 'hell'] as const) { const engine = createTestEngine(diff) for (const ancientName of ancientSuperUniques) { const suDef = dropTables.superUniques.get(ancientName) expect(suDef, `SuperUnique ${ancientName} must exist`).toBeDefined() expect(suDef?.treasureClass, `Ancient ${ancientName} TC must be empty`).toBe('') engine.world.pendingKills = [ { kind: 'kill', x: 10, y: 10, subjectId: suDef!.monsterId, superUniqueId: ancientName, monsterRank: 'unique', monsterLevel: diff === 'hell' ? 90 : diff === 'nightmare' ? 68 : 37, } as any, ] expect(() => engine.tick(idleInput), `Ancient ${ancientName} on ${diff} threw error`).not.toThrow() expect(engine.metrics.dropsRolled, `Ancient ${ancientName} on ${diff} rolled drops`).toBe(0) expect(engine.ground.length, `Ancient ${ancientName} on ${diff} dropped ground items`).toBe(0) } } }) it('verifies ambient critters (chicken, rat, bird1, bird2) drop 0 items across all 3 difficulties', () => { const critters = ['chicken', 'rat', 'bird1', 'bird2'] for (const diff of ['normal', 'nightmare', 'hell'] as const) { const engine = createTestEngine(diff) for (const critter of critters) { engine.world.pendingKills = [ { kind: 'kill', x: 10, y: 10, subjectId: critter, monsterRank: 'normal', monsterLevel: 1, } as any, ] expect(() => engine.tick(idleInput)).not.toThrow() expect(engine.metrics.dropsRolled).toBe(0) expect(engine.ground.length).toBe(0) } } }) }) // ========================================================================= // Dimension 3: Verify base upgrade divisor calculations across all 3 difficulties // ========================================================================= describe('Dimension 3: Base Upgrade Divisor Calculations & Difficulty Odds', () => { it('verifies DifficultyLevelsTable parsing from authentic 1.13c table', () => { const diffTable = dropTables.difficultyLevels expect(diffTable).toBeDefined() expect(diffTable.Normal.UberCodeOddsNormal).toBe(0) expect(diffTable.Normal.UberCodeOddsGood).toBe(0) expect(diffTable.Normal.UltraCodeOddsNormal).toBe(0) expect(diffTable.Normal.UltraCodeOddsGood).toBe(0) expect(diffTable.Nightmare.UberCodeOddsNormal).toBe(10) expect(diffTable.Nightmare.UberCodeOddsGood).toBe(20) expect(diffTable.Nightmare.UltraCodeOddsNormal).toBe(0) expect(diffTable.Nightmare.UltraCodeOddsGood).toBe(0) expect(diffTable.Hell.UberCodeOddsNormal).toBe(20) expect(diffTable.Hell.UberCodeOddsGood).toBe(40) expect(diffTable.Hell.UltraCodeOddsNormal).toBe(30) expect(diffTable.Hell.UltraCodeOddsGood).toBe(40) }) it('fails fast if DifficultyLevelsTable constructor receives incomplete difficulty entries', () => { expect(() => new DifficultyLevelsTable([], new Map())).toThrow( /Failed to initialize DifficultyLevelsTable: expected Normal, Nightmare, and Hell entries/, ) }) it('fails fast if executeDropPipeline cannot resolve difficulty odds', () => { const corruptedTables = { ...dropTables, difficultyOdds: new Map(), difficultyLevels: new Map(), } expect(() => executeDropPipeline(corruptedTables as any, { tcName: 'Act 1 Equip A', nLevel: 10, monsterType: 1, difficulty: 'normal', }), ).toThrow(/Failed to resolve difficulty odds for difficulty: "normal"/) }) it('verifies rollBaseUpgrade on Normal difficulty NEVER upgrades base items (10,000 rolls)', () => { const mockBase: UpgradableItemBase = { id: 'cap', name: 'Cap', kind: 'armor', invWidth: 2, invHeight: 2, maxStack: 1, value: 12, damage: 0, defense: 5, tags: ['helm'], level: 1, normcode: 'cap', ubercode: 'xap', ultracode: 'uap', } const xapBase: UpgradableItemBase = { ...mockBase, id: 'xap', name: 'War Hat', level: 34 } const uapBase: UpgradableItemBase = { ...mockBase, id: 'uap', name: 'Shako', level: 58 } const lookup = (code: string) => (code === 'xap' ? xapBase : code === 'uap' ? uapBase : mockBase) const rng = new D2Rng(1337) // Normal difficulty: qf4 = 0, qf5 = 0 for (let i = 0; i < 10000; i++) { const res = rollBaseUpgrade(mockBase, 0, 0, rng, lookup) expect(res.isUber).toBe(false) expect(res.base.id).toBe('cap') } }) it('verifies statistical distribution of base upgrades on Nightmare difficulty (50,000 rolls)', () => { const mockBase: UpgradableItemBase = { id: 'cap', name: 'Cap', kind: 'armor', invWidth: 2, invHeight: 2, maxStack: 1, value: 12, damage: 0, defense: 5, tags: ['helm'], level: 1, normcode: 'cap', ubercode: 'xap', ultracode: 'uap', } const xapBase: UpgradableItemBase = { ...mockBase, id: 'xap', name: 'War Hat', level: 34 } const uapBase: UpgradableItemBase = { ...mockBase, id: 'uap', name: 'Shako', level: 58 } const lookup = (code: string) => (code === 'xap' ? xapBase : code === 'uap' ? uapBase : mockBase) const rng = new D2Rng(4242) // Nightmare Normal monster: qf4 = 10, qf5 = 0 (10 / 1024 ~= 0.009765625) let upgrades = 0 const N = 50000 for (let i = 0; i < N; i++) { const res = rollBaseUpgrade(mockBase, 10, 0, rng, lookup) if (res.isUber) { upgrades++ expect(res.base.id).toBe('xap') } } const expectedRate = 10 / 1024 const actualRate = upgrades / N // 3 sigma tolerance const sigma = Math.sqrt((expectedRate * (1 - expectedRate)) / N) expect(Math.abs(actualRate - expectedRate)).toBeLessThan(3.5 * sigma) }) it('verifies statistical distribution of base upgrades on Hell difficulty (50,000 rolls)', () => { const mockBase: UpgradableItemBase = { id: 'cap', name: 'Cap', kind: 'armor', invWidth: 2, invHeight: 2, maxStack: 1, value: 12, damage: 0, defense: 5, tags: ['helm'], level: 1, normcode: 'cap', ubercode: 'xap', ultracode: 'uap', } const xapBase: UpgradableItemBase = { ...mockBase, id: 'xap', name: 'War Hat', level: 34 } const uapBase: UpgradableItemBase = { ...mockBase, id: 'uap', name: 'Shako', level: 58 } const lookup = (code: string) => (code === 'xap' ? xapBase : code === 'uap' ? uapBase : mockBase) const rng = new D2Rng(9999) // Hell Good monster: qf4 = 40, qf5 = 40 (40/1024 ~= 0.0390625) let eliteUpgrades = 0 let exceptionalUpgrades = 0 const N = 50000 for (let i = 0; i < N; i++) { const res = rollBaseUpgrade(mockBase, 40, 40, rng, lookup) if (res.isUber) { if (res.base.id === 'uap') { eliteUpgrades++ } else if (res.base.id === 'xap') { exceptionalUpgrades++ } } } // Expected elite rate = 40/1024 const pElite = 40 / 1024 const actualElite = eliteUpgrades / N const sigmaElite = Math.sqrt((pElite * (1 - pElite)) / N) expect(Math.abs(actualElite - pElite)).toBeLessThan(3.5 * sigmaElite) expect(exceptionalUpgrades).toBeGreaterThan(0) }) it('handles bases with xxx or missing upgrade codes without throwing', () => { const unupgradableBase: UpgradableItemBase = { id: 'amu', name: 'Amulet', kind: 'misc', invWidth: 1, invHeight: 1, maxStack: 1, value: 100, damage: 0, defense: 0, tags: ['jewl'], level: 1, ubercode: 'xxx', ultracode: 'xxx', } const rng = new D2Rng(100) const res = rollBaseUpgrade(unupgradableBase, 1000, 1000, rng, () => undefined) expect(res.isUber).toBe(false) expect(res.base.id).toBe('amu') }) }) })