Skip to content

Instantly share code, notes, and snippets.

@menjaraz
Created May 11, 2026 16:35
Show Gist options
  • Select an option

  • Save menjaraz/82eccc064f2cf801cd00926bf81897e6 to your computer and use it in GitHub Desktop.

Select an option

Save menjaraz/82eccc064f2cf801cd00926bf81897e6 to your computer and use it in GitHub Desktop.
Saturation Attack Simulation
use std::error::Error;
use std::fmt;
/// Represents a defense system with interceptors.
#[derive(Debug, Clone)]
pub struct DefenseSystem {
pub interceptors: usize,
pub interceptor_success_rate: f64,
}
impl DefenseSystem {
/// Creates a new defense system.
///
/// # Arguments
/// * `interceptors` - Number of interceptors.
/// * `interceptor_success_rate` - Probability of a single interceptor destroying a missile (must be in [0.0, 1.0]).
///
/// # Returns
/// * `DefenseSystem` if inputs are valid, otherwise an error.
pub fn new(interceptors: usize, interceptor_success_rate: f64) -> Result<Self, DefenseError> {
if interceptor_success_rate < 0.0 || interceptor_success_rate > 1.0 {
Err(DefenseError::InvalidProbability)
} else {
Ok(DefenseSystem {
interceptors,
interceptor_success_rate,
})
}
}
/// Calculates the probability that at least one missile penetrates the defense.
///
/// # Arguments
/// * `missiles` - Number of missiles launched.
///
/// # Returns
/// * Probability of at least one penetration.
pub fn penetration_probability(&self, missiles: usize) -> f64 {
if missiles > self.interceptors {
1.0 // Saturation: M > N guarantees penetration
} else {
let (floor, remainder) = self.distribute_interceptors(missiles);
let prob_all_intercepted = self.calculate_all_intercepted_prob(floor, remainder, missiles);
1.0 - prob_all_intercepted
}
}
/// Distributes interceptors optimally across missiles.
///
/// # Arguments
/// * `missiles` - Number of missiles.
///
/// # Returns
/// * Tuple: (floor, remainder), where:
/// - `floor` = minimum interceptors per missile.
/// - `remainder` = number of missiles with an extra interceptor.
fn distribute_interceptors(&self, missiles: usize) -> (usize, usize) {
let floor = self.interceptors / missiles;
let remainder = self.interceptors % missiles;
(floor, remainder)
}
/// Calculates the probability that all missiles are intercepted.
///
/// # Arguments
/// * `floor` - Minimum interceptors per missile.
/// * `remainder` - Number of missiles with an extra interceptor.
/// * `missiles` - Total number of missiles.
///
/// # Returns
/// * Probability that all missiles are intercepted.
fn calculate_all_intercepted_prob(
&self,
floor: usize,
remainder: usize,
missiles: usize,
) -> f64 {
let mut prob_all_intercepted = 1.0;
// Probability for missiles with floor + 1 interceptors
if remainder > 0 {
let intercept_prob = 1.0 - (1.0 - self.interceptor_success_rate).powi((floor + 1) as i32);
prob_all_intercepted *= intercept_prob.powi(remainder as i32);
}
// Probability for missiles with floor interceptors
if missiles > remainder {
let intercept_prob = 1.0 - (1.0 - self.interceptor_success_rate).powi(floor as i32);
prob_all_intercepted *= intercept_prob.powi((missiles - remainder) as i32);
}
prob_all_intercepted
}
}
/// Custom error type for invalid inputs.
#[derive(Debug, Clone, PartialEq)]
pub enum DefenseError {
InvalidProbability,
}
impl fmt::Display for DefenseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DefenseError::InvalidProbability => write!(f, "Probability must be between 0.0 and 1.0"),
}
}
}
impl Error for DefenseError {}
/// Helper macro to print penetration probability with precise formatting.
macro_rules! print_penetration_prob {
($defense:expr, $missiles:expr) => {
let prob = $defense.penetration_probability($missiles);
println!(
"Missiles (M) = {:<3} | Interceptors (N) = {:<3} | Success Rate (p) = {:<4.2} | Penetration Probability = {:<.6}",
$missiles,
$defense.interceptors,
$defense.interceptor_success_rate,
prob
);
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_saturation_attack() {
// Example 1: M = 10, N = 8, p = 0.7
let defense = DefenseSystem::new(8, 0.7).unwrap();
assert_eq!(defense.penetration_probability(10), 1.0);
// Example 2: M = 5, N = 10, p = 0.8
let defense = DefenseSystem::new(10, 0.8).unwrap();
assert!((defense.penetration_probability(5) - 0.1846).abs() < 0.0001);
// Example 3: M = 3, N = 5, p = 0.9
let defense = DefenseSystem::new(5, 0.9).unwrap();
assert!((defense.penetration_probability(3) - 0.1198).abs() < 0.0001);
}
#[test]
fn test_invalid_probability() {
assert!(DefenseSystem::new(5, -0.1).is_err());
assert!(DefenseSystem::new(5, 1.1).is_err());
}
}
fn main() {
println!("=== Saturation Attack Simulation ===");
println!("------------------------------------");
// Example 1: M = 10, N = 8, p = 0.7
let defense1 = DefenseSystem::new(8, 0.7).unwrap();
print_penetration_prob!(defense1, 10);
// Example 2: M = 5, N = 10, p = 0.8
let defense2 = DefenseSystem::new(10, 0.8).unwrap();
print_penetration_prob!(defense2, 5);
// Example 3: M = 3, N = 5, p = 0.9
let defense3 = DefenseSystem::new(5, 0.9).unwrap();
print_penetration_prob!(defense3, 3);
println!("------------------------------------");
println!("End of Simulation.");
}
@menjaraz

Copy link
Copy Markdown
Author

The “mathematics of saturation” is not really about perfect destruction.

It is about:

  • exhausting interceptors,
  • forcing prioritization,
  • creating leaks in the shield,
  • increasing economic cost,
  • and sustaining pressure longer than the defender can maintain readiness.

Many analysts now describe this as:

“the arithmetic of attrition” or “the brutal mathematics of saturation.”

In modern missile warfare, quantity itself becomes a weapon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment