Skip to main content

ternary_spreadsheet/
cell.rs

1/// A ternary value: negative, neutral, or positive.
2#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
3pub enum TernaryValue {
4    Negative = -1,
5    Neutral = 0,
6    Positive = 1,
7}
8
9impl TernaryValue {
10    /// Create from an i8. Clamps to the nearest ternary value.
11    pub fn from_i8(v: i8) -> Self {
12        match v {
13            i8::MIN..=-1 => TernaryValue::Negative,
14            0 => TernaryValue::Neutral,
15            1..=i8::MAX => TernaryValue::Positive,
16        }
17    }
18
19    /// Convert to i8.
20    pub fn as_i8(self) -> i8 {
21        self as i8
22    }
23
24    /// Flip the sign: Negative ↔ Positive, Neutral stays Neutral.
25    pub fn flip(self) -> Self {
26        match self {
27            TernaryValue::Negative => TernaryValue::Positive,
28            TernaryValue::Neutral => TernaryValue::Neutral,
29            TernaryValue::Positive => TernaryValue::Negative,
30        }
31    }
32
33    /// Get all three values.
34    pub fn all() -> [TernaryValue; 3] {
35        [TernaryValue::Negative, TernaryValue::Neutral, TernaryValue::Positive]
36    }
37
38    /// Random value given a u32 seed (simple deterministic).
39    pub fn from_seed(seed: u32) -> Self {
40        match seed % 3 {
41            0 => TernaryValue::Negative,
42            1 => TernaryValue::Neutral,
43            _ => TernaryValue::Positive,
44        }
45    }
46}
47
48impl std::fmt::Display for TernaryValue {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        match self {
51            TernaryValue::Negative => write!(f, "-1"),
52            TernaryValue::Neutral => write!(f, "0"),
53            TernaryValue::Positive => write!(f, "+1"),
54        }
55    }
56}
57
58/// A single ternary cell with value, fitness, and history.
59#[derive(Debug, Clone)]
60pub struct Cell {
61    pub value: TernaryValue,
62    pub fitness: f64,
63    pub history: Vec<TernaryValue>,
64    pub generation: u64,
65}
66
67impl Cell {
68    /// Create a new cell with the given value and zero fitness.
69    pub fn new(value: TernaryValue) -> Self {
70        Cell {
71            value,
72            fitness: 0.0,
73            history: vec![],
74            generation: 0,
75        }
76    }
77
78    /// Create a neutral cell.
79    pub fn neutral() -> Self {
80        Self::new(TernaryValue::Neutral)
81    }
82
83    /// Set the value, recording the previous value in history.
84    pub fn set_value(&mut self, value: TernaryValue) {
85        self.history.push(self.value);
86        self.value = value;
87        self.generation += 1;
88    }
89
90    /// Set the fitness score.
91    pub fn set_fitness(&mut self, fitness: f64) {
92        self.fitness = fitness;
93    }
94
95    /// Compute a simple fitness: value magnitude scaled by generation diversity.
96    pub fn compute_default_fitness(&self) -> f64 {
97        let base = self.value.as_i8() as f64;
98        let unique_count = {
99            let mut vals = self.history.clone();
100            vals.sort();
101            vals.dedup();
102            vals.len() as f64
103        };
104        base * (1.0 + unique_count * 0.1)
105    }
106
107    /// How many times has this cell changed value?
108    pub fn mutation_count(&self) -> usize {
109        self.history.len()
110    }
111
112    /// Reset the cell to neutral with a new generation.
113    pub fn reset(&mut self) {
114        self.history.push(self.value);
115        self.value = TernaryValue::Neutral;
116        self.fitness = 0.0;
117        self.generation += 1;
118    }
119}
120
121impl Default for Cell {
122    fn default() -> Self {
123        Self::neutral()
124    }
125}