//! The tunable balance table. Editing `configs/default.json` (or this struct's
//! [`Default`]) **is** the balance act — the same philosophy as `combat/`.
//!
//! Floating point is used for the few rate/scaling knobs (the Maw clock, loot
//! scaling). Determinism is preserved because all *randomness* flows through the
//! integer ChaCha8 [`crate::rng::Rng`]; the float knobs are fixed per run and applied
//! identically everywhere, so a given (config, seed) reproduces bit-for-bit.

use serde::{Deserialize, Serialize};

/// The complete set of balance knobs for one DEEPFALL game.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Config {
    /// Number of floors. Floor 1 = Threshold (top); `floors` = deepest.
    pub floors: u32,
    /// Rooms per floor.
    pub rooms_per_floor: u32,
    /// Room collapse glory = `base_value * (1 + depth_coeff * (depth - 1))`.
    /// `1.0` ≈ linear `base*depth`; lower softens the deep/shallow cliff.
    pub depth_coeff: f64,

    /// Dice drawn per hand (before Lineage/Calling modifiers).
    pub hand_size: u32,
    /// Standing Dread at/above this triggers a Setback.
    pub dread_threshold: i32,
    /// Loot lost on a Setback when the bag is too small to lose a die.
    pub setback_loot: f64,
    /// Mettle needed to recruit one die.
    pub recruit_cost: i32,

    /// Loot per Greed symbol before depth scaling.
    pub loot_per_greed: f64,
    /// Loot depth multiplier = `1 + loot_depth_factor * (depth - 1)`.
    pub loot_depth_factor: f64,

    /// Maw advance per round = `maw_base_rate + maw_accel*round + maw_dread_push*total_dread`.
    pub maw_base_rate: f64,
    pub maw_accel: f64,
    pub maw_dread_push: f64,
    /// Hard cap on floors collapsed per round (overflow carries). Default 1 keeps
    /// "one floor away" an honest promise (kills the multi-collapse feel-bad).
    pub max_collapses_per_round: u32,
    /// Dread pressure added by each Press-On (feeds the Maw that turn).
    pub press_dread_push: f64,

    /// Cash-Out bank += `cashout_depth_dividend * deepest_reached`.
    pub cashout_depth_dividend: f64,
    /// The Cautious bot Cashes Out once its bankable value clears this bar.
    pub cashout_min_bank: f64,
    /// Cash-Out flat bonus per crew retired "with boots on" — a survival incentive the
    /// 6-human playtest demanded (the careful banker finished last and felt cheated).
    pub cashout_extraction_bonus: f64,
    /// Fraction of unbanked loot salvaged when Taken with **no** Last Stand — the
    /// "Desperate Strike" so a silent death isn't a humiliating zero (playtest ask).
    pub taken_salvage_frac: f64,
    /// Worthy-End spike = `worthy_factor * deepest_floor` (+ optional held bonus).
    pub worthy_factor: f64,
    /// Extra Worthy-End credit per point of held room value. **0 by default** to
    /// avoid double-counting rooms already scored at collapse.
    pub worthy_held_bonus: f64,

    /// Fraction of a room's *unclaimed* value that rises one floor on collapse
    /// ("refugee plunder"). Makes the Maw frontier the always-hot zone.
    pub value_rise_frac: f64,
    /// A re-entering party inserts at `max(1, frontier - reentry_gap)`.
    pub reentry_gap: u32,
    /// Rubber-band: a trailing party's exit payouts scale by
    /// `1 + comeback_bp/10000 * (deficit / leader_score)`. 0 disables it.
    pub comeback_bp: i64,

    /// Safety valve against a stuck game; a game should end well before this.
    pub max_rounds: u32,
}

impl Default for Config {
    /// Mirrors `configs/default.json`. Keep the two in sync.
    fn default() -> Self {
        Config {
            floors: 6,
            rooms_per_floor: 4,
            // v2 (iter 2): sub-linear depth (was 1.0). The linear base*depth cliff —
            // a 36-pt floor-6 Vault vs a 2-pt floor-1 Stone — made "always dive deepest"
            // strictly dominant and blew the combo spread wide open.
            depth_coeff: 0.7,

            hand_size: 4,
            dread_threshold: 3,
            setback_loot: 3.0,
            recruit_cost: 2,

            loot_per_greed: 1.0,
            // v2 (iter 2): 0.5 -> 0.3, less depth snowball for aggressive divers.
            loot_depth_factor: 0.3,

            maw_base_rate: 0.15,
            maw_accel: 0.06,
            maw_dread_push: 0.02,
            max_collapses_per_round: 1,
            press_dread_push: 0.5,

            cashout_depth_dividend: 2.0, // iter7: back to 2.0 — 3.0 made banking dominate worthy
            // v2 (iter 2): 8 -> 12. At 8 the Cautious bot nickel-and-dimed (churned weak
            // shallow parties) and cratered to a 1.8% win rate; banking richer hauls
            // before retiring makes the safe line viable.
            cashout_min_bank: 12.0,
            // Playtest-driven (2026-06-19): reward surviving the delve, and give a silent
            // death a small consolation. See PLAYTEST.md.
            cashout_extraction_bonus: 3.0,
            taken_salvage_frac: 0.33,
            // v2 (iter 3-4): 4 -> 2.5. At 4 the generic Worthy End (any deep party on a
            // Throne) made the WorthyEnd strategy a 45% king; lowering the base while
            // raising the Wardancer multiplier (now 2.2x) shifts the payoff onto the
            // specialist instead of the strategy.
            worthy_factor: 2.5,
            worthy_held_bonus: 0.0,

            value_rise_frac: 0.5,
            reentry_gap: 1,
            comeback_bp: 0,

            max_rounds: 40,
        }
    }
}

impl Config {
    /// Glory awarded for a room of `base_value` on floor `depth` (1 = top).
    pub fn room_glory(&self, base_value: f64, depth: u32) -> f64 {
        base_value * (1.0 + self.depth_coeff * (depth as f64 - 1.0))
    }

    /// Loot multiplier for acting on floor `depth`.
    pub fn loot_depth_mult(&self, depth: u32) -> f64 {
        1.0 + self.loot_depth_factor * (depth as f64 - 1.0)
    }
}
