#[derive(Debug)]
pub struct Metabolism {
energy: u64,
initial_energy: u64,
is_dead: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConsumptionResult {
Success { remaining: u64 },
Insufficient { requested: u64, available: u64 },
Dead,
}
impl Metabolism {
pub fn new(initial_energy: u64) -> Self {
Metabolism {
energy: initial_energy,
initial_energy,
is_dead: false,
}
}
pub fn energy(&self) -> u64 {
self.energy
}
pub fn initial_energy(&self) -> u64 {
self.initial_energy
}
pub fn is_dead(&self) -> bool {
self.is_dead
}
pub fn consumption_rate(&self) -> f64 {
if self.initial_energy == 0 {
return 1.0;
}
let consumed = self.initial_energy - self.energy;
(consumed as f64) / (self.initial_energy as f64)
}
pub fn consume(&mut self, cost: u64) -> ConsumptionResult {
if self.is_dead {
return ConsumptionResult::Dead;
}
if cost > self.energy {
return ConsumptionResult::Insufficient {
requested: cost,
available: self.energy,
};
}
self.energy -= cost;
if self.energy == 0 {
self.is_dead = true;
return ConsumptionResult::Success { remaining: 0 };
}
ConsumptionResult::Success {
remaining: self.energy,
}
}
pub fn die(&mut self) {
self.energy = 0;
self.is_dead = true;
}
pub fn operations_remaining(&self, operation_cost: u64) -> u64 {
if self.is_dead || operation_cost == 0 {
return 0;
}
self.energy / operation_cost
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn metabolism_starts_with_initial_energy() {
let metabolism = Metabolism::new(1000);
assert_eq!(metabolism.energy(), 1000);
assert_eq!(metabolism.initial_energy(), 1000);
assert!(!metabolism.is_dead());
}
#[test]
fn energy_consumption_depletes_energy() {
let mut metabolism = Metabolism::new(1000);
let result = metabolism.consume(300);
assert_eq!(result, ConsumptionResult::Success { remaining: 700 });
assert_eq!(metabolism.energy(), 700);
let result = metabolism.consume(400);
assert_eq!(result, ConsumptionResult::Success { remaining: 300 });
assert_eq!(metabolism.energy(), 300);
}
#[test]
fn insufficient_energy_fails_operation() {
let mut metabolism = Metabolism::new(100);
let result = metabolism.consume(150);
assert_eq!(result, ConsumptionResult::Insufficient {
requested: 150,
available: 100,
});
assert_eq!(metabolism.energy(), 100);
assert!(!metabolism.is_dead());
}
#[test]
fn depleting_all_energy_causes_death() {
let mut metabolism = Metabolism::new(100);
metabolism.consume(50);
assert!(!metabolism.is_dead());
let result = metabolism.consume(50);
assert_eq!(result, ConsumptionResult::Success { remaining: 0 });
assert!(metabolism.is_dead());
assert_eq!(metabolism.energy(), 0);
}
#[test]
fn operations_after_death_are_impossible() {
let mut metabolism = Metabolism::new(100);
metabolism.die();
let result = metabolism.consume(10);
assert_eq!(result, ConsumptionResult::Dead);
assert_eq!(metabolism.energy(), 0);
}
#[test]
fn death_is_irreversible() {
let mut metabolism = Metabolism::new(100);
metabolism.die();
assert!(metabolism.is_dead());
assert!(metabolism.is_dead());
assert_eq!(metabolism.energy(), 0);
}
#[test]
fn energy_can_only_decrease() {
let mut metabolism = Metabolism::new(1000);
metabolism.consume(200);
let energy_after_first = metabolism.energy();
metabolism.consume(300);
let energy_after_second = metabolism.energy();
assert!(energy_after_second < energy_after_first);
assert!(energy_after_second < 1000);
}
#[test]
fn consumption_rate_tracks_depletion() {
let mut metabolism = Metabolism::new(1000);
assert_eq!(metabolism.consumption_rate(), 0.0);
metabolism.consume(250);
assert_eq!(metabolism.consumption_rate(), 0.25);
metabolism.consume(500);
assert_eq!(metabolism.consumption_rate(), 0.75);
metabolism.consume(250);
assert_eq!(metabolism.consumption_rate(), 1.0);
assert!(metabolism.is_dead());
}
#[test]
fn energy_never_increases() {
let mut metabolism = Metabolism::new(1000);
let energy0 = metabolism.energy();
metabolism.consume(100);
let energy1 = metabolism.energy();
metabolism.consume(200);
let energy2 = metabolism.energy();
metabolism.consume(50);
let energy3 = metabolism.energy();
assert!(energy1 <= energy0);
assert!(energy2 <= energy1);
assert!(energy3 <= energy2);
assert_eq!(energy1, 900);
assert_eq!(energy2, 700);
assert_eq!(energy3, 650);
}
#[test]
fn no_negative_cost_operations() {
let mut metabolism = Metabolism::new(1000);
metabolism.consume(0); assert_eq!(metabolism.energy(), 1000);
}
#[test]
fn death_state_is_permanent() {
let mut metabolism = Metabolism::new(100);
metabolism.die();
assert!(metabolism.is_dead());
assert!(metabolism.is_dead());
assert!(metabolism.is_dead());
assert!(metabolism.is_dead());
assert_eq!(metabolism.energy(), 0);
}
#[test]
fn death_at_zero_energy_is_mandatory() {
let mut metabolism = Metabolism::new(50);
metabolism.consume(50);
assert_eq!(metabolism.energy(), 0);
assert!(metabolism.is_dead());
}
}