/**
 * The expected-value damage engine.
 *
 * Closed-form math over schema profiles + a flat {@link Buff} stack. No
 * sampling, no I/O. Auto-injects every weapon-keyword on the attacker's
 * profile as a buff (so callers don't have to enumerate intrinsics), then
 * resolves the stack via {@link resolveBuffs}, then walks
 * attacks → hits → wounds → unsaved → damage → after-fnp → models-killed.
 *
 * The dataset is required (and defaults to the embedded one) — without it
 * the engine can't look up weapon-keyword effects.
 */
import type { Phase, Unit, Weapon } from "../generated.js";
import { type Buff, type EngineContext, type ResolvedModifiers } from "./buffs.js";
import { Dataset } from "../data/dataset.js";
export type AttackProfileRef = {
    weapon: Weapon;
    profileIndex: number;
};
export type TargetProfileRef = {
    unit: Unit;
    profileIndex: number;
    /** Override target model count (otherwise read from `unit.model_count.min`). */
    modelCount?: number;
};
export type Stage = {
    name: "attacks" | "hits" | "wounds" | "unsaved" | "damage" | "after-fnp" | "models-killed";
    expected: number;
    detail: string;
};
export type EngineInput = {
    attacker: AttackProfileRef;
    target: TargetProfileRef;
    modelsFiring: number;
    /** User / ability / manual buffs. Weapon-keyword buffs are auto-injected. */
    buffs: Buff[];
    context: EngineContext;
};
export type EngineOutput = {
    stages: Stage[];
    resolved: ResolvedModifiers;
};
/**
 * Compute the expected per-stage projection for one (attacker, target, buffs)
 * triple. The dataset defaults to the embedded one — pass an alternate when
 * crunching against a different bundle (e.g. tests).
 */
export declare function crunch(input: EngineInput, dataset?: Dataset): EngineOutput;
export type { Phase };
//# sourceMappingURL=engine.d.ts.map