import { describe, it, expect, beforeAll } from 'vitest' import { ACT_BOSSES, BOSS_LIST, type BossDefinition } from '../src/boss.ts' import { getEmbeddedDropTables } from '../src/game/embedded-drop-tables.ts' import { executeDropPipeline, resolveItemTypeFlags, type DropTables, } from '../src/game/drop-pipeline.ts' import type { UpgradableItemBase } from '../src/game/item-upgrade.ts' import type { Item } from '../src/game/items.ts' import { runParityAudit } from '../scripts/audit-parity.ts' export type BaseTier = 'normal' | 'exceptional' | 'elite' export function classifyItemBaseTier(item: Item): BaseTier | null { if (item.base.kind !== 'weapon' && item.base.kind !== 'armor') { return null } const b = item.base as UpgradableItemBase const id = (b.id ?? '').trim().toLowerCase() const uber = (b.ubercode ?? '').trim().toLowerCase() const ultra = (b.ultracode ?? '').trim().toLowerCase() if (ultra && ultra !== 'xxx' && id === ultra) { return 'elite' } if (uber && uber !== 'xxx' && id === uber) { return 'exceptional' } return 'normal' } describe('Diablo II 1.13c Parity Baseline & Boss monsterType=4 Upgrade Odds (Issue #126)', () => { let dropTables: DropTables beforeAll(() => { dropTables = getEmbeddedDropTables() }) it('1. BOSS_LIST and ACT_BOSSES define monsterType === 4 for all 5 Act Bosses', () => { expect(BOSS_LIST).toBeDefined() expect(BOSS_LIST).toHaveLength(5) expect(BOSS_LIST).toBe(ACT_BOSSES) for (const boss of BOSS_LIST) { const def: BossDefinition = boss expect(def.monsterType, `Boss ${def.id} must explicitly set monsterType: 4`).toBe(4) } }) it('2. Difficultylevels.txt Good odds (UberCodeOddsGood / UltraCodeOddsGood) exceed Normal odds in Nightmare and Hell', () => { const nmOdds = dropTables.difficultyLevels.get('Nightmare')! const hellOdds = dropTables.difficultyLevels.get('Hell')! expect(nmOdds.uberCodeOddsGood).toBeGreaterThan(nmOdds.uberCodeOddsNormal) expect(hellOdds.uberCodeOddsGood).toBeGreaterThan(hellOdds.uberCodeOddsNormal) expect(hellOdds.ultraCodeOddsGood).toBeGreaterThan(hellOdds.ultraCodeOddsNormal) }) it('3. monsterType=4 (Boss) yields significantly higher Exceptional/Elite base upgrades than monsterType=1 (Normal) over identical seeds', () => { const sampleKills = 400 let normalMonsterUpgraded = 0 let normalMonsterEquipCount = 0 let bossMonsterUpgraded = 0 let bossMonsterEquipCount = 0 for (let i = 0; i < sampleKills; i++) { const seed = (0x12600000 + i * 7919) >>> 0 // Run with monsterType = 1 (Normal monster: uses uberCodeOddsNormal=20, ultraCodeOddsNormal=30) const dropsNormalType = executeDropPipeline({ tcName: 'Mephistoq (H)', nLevel: 87, monsterType: 1, dropTables, difficulty: 'hell', playerMf: 250, gamePlayers: 1, partyPlayers: 1, monsterRng: seed, }) for (const item of dropsNormalType) { const tier = classifyItemBaseTier(item) if (tier !== null) { normalMonsterEquipCount++ if (tier === 'exceptional' || tier === 'elite') { normalMonsterUpgraded++ } } } // Run with monsterType = 4 (Act Boss: uses uberCodeOddsGood=40, ultraCodeOddsGood=40) const dropsBossType = executeDropPipeline({ tcName: 'Mephistoq (H)', nLevel: 87, monsterType: 4, dropTables, difficulty: 'hell', playerMf: 250, gamePlayers: 1, partyPlayers: 1, monsterRng: seed, }) for (const item of dropsBossType) { const tier = classifyItemBaseTier(item) if (tier !== null) { bossMonsterEquipCount++ if (tier === 'exceptional' || tier === 'elite') { bossMonsterUpgraded++ } } } } expect(normalMonsterEquipCount).toBeGreaterThan(500) expect(bossMonsterEquipCount).toBe(normalMonsterEquipCount) const normalUpgradeRate = normalMonsterUpgraded / normalMonsterEquipCount const bossUpgradeRate = bossMonsterUpgraded / bossMonsterEquipCount // Boss (Good odds: 40/1024 + 40/1024) must strictly exceed Normal (20/1024 + 30/1024) expect(bossMonsterUpgraded).toBeGreaterThan(normalMonsterUpgraded) expect(bossUpgradeRate).toBeGreaterThan(normalUpgradeRate) }) it('4. executeDropPipeline works properly with monsterType: 4 across all 5 Act Bosses in Hell', () => { for (const boss of BOSS_LIST) { const drops = executeDropPipeline({ tcName: boss.questTc.hell, nLevel: boss.mlvl.hell, monsterType: boss.monsterType ?? 4, dropTables, difficulty: 'hell', playerMf: 250, gamePlayers: 1, partyPlayers: 1, monsterRng: 1337 + boss.act, }) expect(drops.length).toBeGreaterThan(0) for (const item of drops) { expect(item.ilvl).toBe(boss.mlvl.hell) } } }) it('5. runParityAudit generates structured JSON summary of quality, base upgrade, and affix distributions', () => { const summary = runParityAudit({ kills: 100, bossId: 'mephisto', difficulty: 'hell', playerMf: 250, gamePlayers: 1, }) expect(summary.config.boss).toBe('mephisto') expect(summary.config.monsterType).toBe(4) expect(summary.totalItems).toBeGreaterThan(0) expect(summary.baseTierUpgrade.totalEquipment).toBeGreaterThan(0) expect(summary.baseTierUpgrade.upgradedRatio).toBeGreaterThan(0) }) }) describe('ItemTypes.txt Quality Flags & classSpecific Propagation (Issue #127 / A2)', () => { let dropTables: DropTables beforeAll(() => { dropTables = getEmbeddedDropTables() }) it('1. resolveItemTypeFlags returns isAlwaysMagic: true for rin, amu, jew, cm1, cm2, cm3', () => { const alwaysMagicCodes = ['rin', 'amu', 'jew', 'cm1', 'cm2', 'cm3'] for (const code of alwaysMagicCodes) { const base = dropTables.getBase(code) expect(base, `Base ${code} must exist`).toBeDefined() const flags = resolveItemTypeFlags(base!, dropTables.itemTypes, dropTables.isA) expect(flags.isAlwaysMagic, `${code} must have isAlwaysMagic === true`).toBe(true) expect(flags.isNormalOnly, `${code} must have isNormalOnly === false`).toBe(false) if (code === 'cm1' || code === 'cm2' || code === 'cm3') { expect(flags.canBeRare, `${code} charm must have canBeRare === false`).toBe(false) } else { expect(flags.canBeRare, `${code} must have canBeRare === true`).toBe(true) } } }) it('2. 200 rolls of Ring / Amulet / Small Charm at playerMf: -100 and 0, nLevel: 1 NEVER produce normal/superior/low (and cm1 NEVER produces rare)', () => { const validQualities = new Set(['magic', 'rare', 'set', 'unique']) const forbiddenQualities = new Set(['normal', 'superior', 'low']) for (const mf of [-100, 0]) { for (const code of ['rin', 'amu', 'cm1']) { let countNormalOrSuperiorOrLow = 0 let countRare = 0 let totalGenerated = 0 for (let i = 0; i < 200; i++) { const drops = executeDropPipeline({ tcName: code, nLevel: 1, monsterType: 1, dropTables, difficulty: 'normal', playerMf: mf, gamePlayers: 1, partyPlayers: 1, monsterRng: (0x12700000 + i * 1009 + (mf === 0 ? 500000 : 0)) >>> 0, }) expect(drops.length).toBe(1) const item = drops[0]! totalGenerated++ const q = item.rarity ?? '' if (forbiddenQualities.has(q)) { countNormalOrSuperiorOrLow++ } if (q === 'rare') { countRare++ } expect(validQualities.has(q), `Item ${item.name} (${code}) rolled invalid quality ${q} at MF=${mf}`).toBe(true) expect(item.name).not.toMatch(/^Cracked /) expect(item.name).not.toMatch(/^Superior /) } expect(totalGenerated).toBe(200) expect(countNormalOrSuperiorOrLow).toBe(0) if (code === 'cm1') { expect(countRare).toBe(0) } } } }) it('3. resolveItemTypeFlags identifies class-specific equipment bases (Sorceress Orb, Paladin Shield, Amazon Bow, etc.) with isClassSpecific === true', () => { // Sorceress Orb: ob1 (Eagle Orb), obc (Eldritch Orb), obf (Dimensional Shard) // Paladin Auric Shield: pa1 (Targe), paa (Akaran Targe), paf (Vortex Shield) // Amazon Bow: am1 (Stag Bow), am6 (Ashwood Bow), amb (Matriarchal Bow) // Barbarian Primal Helm: ba1 (Jawbone Cap) // Druid Pelt: dr1 (Wolf Head) // Necromancer Voodoo Head: ne1 (Preserved Head) // Assassin Katar: ktr (Katar) const classSpecificCodes = ['ob1', 'obf', 'pa1', 'paf', 'am1', 'amb', 'ba1', 'dr1', 'ne1', 'ktr'] for (const code of classSpecificCodes) { const base = dropTables.getBase(code) expect(base, `Class-specific base ${code} must exist`).toBeDefined() const flags = resolveItemTypeFlags(base!, dropTables.itemTypes, dropTables.isA) expect(flags.isClassSpecific, `${code} (${base!.name}) must have isClassSpecific === true`).toBe(true) } // Generic equipment bases must have isClassSpecific === false const genericCodes = ['cap', 'uap', 'uit', 'lsd', 'crn', 'rin', 'amu', 'cm1'] for (const code of genericCodes) { const base = dropTables.getBase(code) expect(base, `Generic base ${code} must exist`).toBeDefined() const flags = resolveItemTypeFlags(base!, dropTables.itemTypes, dropTables.isA) expect(flags.isClassSpecific, `${code} (${base!.name}) must have isClassSpecific === false`).toBe(false) } }) })