#[derive(Debug, Clone)]
pub struct ValueArgumentInput {
pub attacker: String,
pub defender: String,
pub value_at_stake: String,
pub attacker_conviction: f64,
pub defender_conviction: f64,
pub defender_openness: f64,
}
#[derive(Debug, Clone)]
pub struct ValueArgumentResult {
pub winner: String,
pub loser: String,
pub value_at_stake: String,
pub loser_value_shift: f64,
pub winner_value_shift: f64,
}
pub fn resolve_value_argument(input: &ValueArgumentInput) -> ValueArgumentResult {
let attacker_wins = input.attacker_conviction >= input.defender_conviction;
let (winner, loser) = if attacker_wins {
(input.attacker.clone(), input.defender.clone())
} else {
(input.defender.clone(), input.attacker.clone())
};
let conviction_gap = (input.attacker_conviction - input.defender_conviction).abs();
let loser_value_shift = (conviction_gap * input.defender_openness).clamp(0.0, 1.0);
let winner_value_shift = (conviction_gap * 0.1).clamp(0.0, 0.1);
ValueArgumentResult {
winner,
loser,
value_at_stake: input.value_at_stake.clone(),
loser_value_shift,
winner_value_shift,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn winner_shifts_loser_value() {
let input = ValueArgumentInput {
attacker: "Alice".to_string(),
defender: "Bob".to_string(),
value_at_stake: "Benevolence".to_string(),
attacker_conviction: 0.8,
defender_conviction: 0.4,
defender_openness: 0.6,
};
let result = resolve_value_argument(&input);
assert_eq!(result.winner, "Alice");
assert!(result.loser_value_shift > 0.0);
assert!(result.loser_value_shift <= 1.0);
}
#[test]
fn defender_wins_when_more_convinced() {
let input = ValueArgumentInput {
attacker: "Alice".to_string(),
defender: "Bob".to_string(),
value_at_stake: "Power".to_string(),
attacker_conviction: 0.3,
defender_conviction: 0.9,
defender_openness: 0.5,
};
let result = resolve_value_argument(&input);
assert_eq!(result.winner, "Bob");
}
#[test]
fn winner_gets_small_self_reinforcement() {
let input = ValueArgumentInput {
attacker: "Alice".to_string(),
defender: "Bob".to_string(),
value_at_stake: "Security".to_string(),
attacker_conviction: 0.8,
defender_conviction: 0.4,
defender_openness: 0.6,
};
let result = resolve_value_argument(&input);
assert!(result.winner_value_shift > 0.0);
assert!(result.winner_value_shift < result.loser_value_shift);
}
#[test]
fn equal_conviction_favors_attacker() {
let input = ValueArgumentInput {
attacker: "Alice".to_string(),
defender: "Bob".to_string(),
value_at_stake: "Tradition".to_string(),
attacker_conviction: 0.5,
defender_conviction: 0.5,
defender_openness: 0.8,
};
let result = resolve_value_argument(&input);
assert_eq!(result.winner, "Alice");
assert_eq!(result.loser_value_shift, 0.0);
}
}