terminals_core/substrate/
world.rs1use serde::{Deserialize, Serialize};
7
8pub const ZONE_COUNT: usize = 41;
10
11pub const ZONE_ATOM_OFFSET: usize = 23;
13
14pub const SHELL_SIZES: [usize; 4] = [1, 6, 12, 22];
16
17pub const SHELL_BASE_RESONANCE: [f32; 4] = [1.0, 0.8, 0.5, 0.2];
19
20pub const SHELL_STABILITY_SCALE: [f32; 4] = [1.0, 0.9, 0.6, 0.3];
22
23pub const SHELL_RADII: [f32; 4] = [0.0, 8.0, 18.0, 30.0];
25
26#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
28pub struct WorldAtom {
29 pub zone_id: u8,
30 pub shell: u8,
31 pub topology: u8,
33 pub active_ops: u64,
35 pub resonance: f32,
37 pub stability: f32,
39 pub owner_hash: u32,
41 pub angle: f32,
43}
44
45impl Default for WorldAtom {
46 fn default() -> Self {
47 Self {
48 zone_id: 0,
49 shell: 0,
50 topology: 0,
51 active_ops: 0,
52 resonance: 0.0,
53 stability: 0.0,
54 owner_hash: 0,
55 angle: 0.0,
56 }
57 }
58}
59
60pub struct WorldGrid {
62 pub zones: [WorldAtom; ZONE_COUNT],
63}
64
65impl WorldGrid {
66 pub fn new() -> Self {
68 let mut zones = [WorldAtom::default(); ZONE_COUNT];
69 let mut zone_id: u8 = 0;
70
71 for (shell, &count) in SHELL_SIZES.iter().enumerate() {
72 let shell_u8 = shell as u8;
73 for i in 0..count {
74 let angle = if count > 1 {
75 2.0 * std::f32::consts::PI * (i as f32) / (count as f32)
76 } else {
77 0.0
78 };
79
80 zones[zone_id as usize] = WorldAtom {
81 zone_id,
82 shell: shell_u8,
83 topology: (i % 6) as u8,
84 active_ops: 0,
85 resonance: SHELL_BASE_RESONANCE[shell],
86 stability: SHELL_STABILITY_SCALE[shell],
87 owner_hash: 0,
88 angle,
89 };
90
91 zone_id += 1;
92 }
93 }
94
95 Self { zones }
96 }
97
98 pub fn apply_op(&mut self, zone_id: u32, op_id: u32) -> bool {
100 if zone_id as usize >= ZONE_COUNT || op_id >= 55 {
101 return false;
102 }
103 let zone = &mut self.zones[zone_id as usize];
104 let mask = 1u64 << op_id;
105 let was_new = zone.active_ops & mask == 0;
106 zone.active_ops |= mask;
107 was_new
108 }
109
110 pub fn has_op(&self, zone_id: u32, op_id: u32) -> bool {
112 if zone_id as usize >= ZONE_COUNT || op_id >= 55 {
113 return false;
114 }
115 self.zones[zone_id as usize].active_ops & (1u64 << op_id) != 0
116 }
117
118 pub fn op_count(&self, zone_id: u32) -> u32 {
120 if zone_id as usize >= ZONE_COUNT {
121 return 0;
122 }
123 self.zones[zone_id as usize].active_ops.count_ones()
124 }
125
126 pub fn active_ops(&self, zone_id: u32) -> Vec<u32> {
128 if zone_id as usize >= ZONE_COUNT {
129 return vec![];
130 }
131 let bits = self.zones[zone_id as usize].active_ops;
132 (0..55).filter(|&i| bits & (1u64 << i) != 0).collect()
133 }
134
135 pub fn update_stability(&mut self, substrate_r: f32) {
137 for zone in &mut self.zones {
138 let shell = zone.shell as usize;
139 if shell < 4 {
140 zone.stability = substrate_r * SHELL_STABILITY_SCALE[shell];
141 zone.resonance = SHELL_BASE_RESONANCE[shell] * (0.5 + substrate_r * 0.5);
142 }
143 }
144 }
145
146 pub fn apply_shockwave(&mut self, bass_peak: f32) {
148 if bass_peak < 0.3 {
149 return;
150 }
151 for zone in &mut self.zones {
152 let shell = zone.shell as usize;
153 if shell < 4 {
154 let intensity = bass_peak * (1.0 - shell as f32 * 0.2);
155 zone.resonance = (zone.resonance + intensity * 0.3).min(1.0);
156 }
157 }
158 }
159
160 pub fn tick(&mut self, dt: f32) {
162 let decay = (-dt * 2.0).exp(); for zone in &mut self.zones {
164 let shell = zone.shell as usize;
165 if shell < 4 {
166 let base = SHELL_BASE_RESONANCE[shell];
167 zone.resonance = base + (zone.resonance - base) * decay;
168 }
169 }
170 }
171
172 pub fn witness_flat(&self) -> Vec<f32> {
175 let mut out = Vec::with_capacity(ZONE_COUNT * 4);
176 for zone in &self.zones {
177 out.push(zone.resonance);
178 out.push(zone.stability);
179 out.push(zone.active_ops.count_ones() as f32 / 55.0); out.push(zone.topology as f32);
181 }
182 out
183 }
184
185 pub fn zone_shell(zone_id: u32) -> u8 {
187 if zone_id == 0 { return 0; }
188 if zone_id <= 6 { return 1; }
189 if zone_id <= 18 { return 2; }
190 if zone_id <= 40 { return 3; }
191 3 }
193}
194
195impl Default for WorldGrid {
196 fn default() -> Self {
197 Self::new()
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_world_creation() {
207 let world = WorldGrid::new();
208 assert_eq!(world.zones.len(), ZONE_COUNT);
209 assert_eq!(world.zones[0].shell, 0); assert_eq!(world.zones[1].shell, 1); assert_eq!(world.zones[7].shell, 2); assert_eq!(world.zones[19].shell, 3); }
214
215 #[test]
216 fn test_apply_op() {
217 let mut world = WorldGrid::new();
218 assert!(world.apply_op(0, 5)); assert!(!world.apply_op(0, 5)); assert!(world.has_op(0, 5));
221 assert!(!world.has_op(0, 6));
222 assert_eq!(world.op_count(0), 1);
223 }
224
225 #[test]
226 fn test_apply_op_bounds() {
227 let mut world = WorldGrid::new();
228 assert!(!world.apply_op(50, 0)); assert!(!world.apply_op(0, 55)); assert!(!world.apply_op(0, 60)); }
232
233 #[test]
234 fn test_active_ops() {
235 let mut world = WorldGrid::new();
236 world.apply_op(5, 0);
237 world.apply_op(5, 10);
238 world.apply_op(5, 54);
239 let ops = world.active_ops(5);
240 assert_eq!(ops, vec![0, 10, 54]);
241 }
242
243 #[test]
244 fn test_update_stability() {
245 let mut world = WorldGrid::new();
246 world.update_stability(0.5);
247 assert!((world.zones[0].stability - 0.5).abs() < 1e-6); assert!((world.zones[1].stability - 0.45).abs() < 1e-6); }
250
251 #[test]
252 fn test_shockwave() {
253 let mut world = WorldGrid::new();
254 let zone_idx = 19; let pre_res = world.zones[zone_idx].resonance;
257 world.apply_shockwave(0.8);
258 assert!(world.zones[zone_idx].resonance > pre_res,
259 "resonance {} should exceed pre {}", world.zones[zone_idx].resonance, pre_res);
260 }
261
262 #[test]
263 fn test_shockwave_below_threshold() {
264 let mut world = WorldGrid::new();
265 let pre_res = world.zones[0].resonance;
266 world.apply_shockwave(0.2); assert_eq!(world.zones[0].resonance, pre_res);
268 }
269
270 #[test]
271 fn test_tick_decay() {
272 let mut world = WorldGrid::new();
273 world.zones[1].resonance = 1.0; world.tick(1.0); assert!(world.zones[1].resonance < 1.0); assert!(world.zones[1].resonance > 0.8); }
278
279 #[test]
280 fn test_witness_flat() {
281 let world = WorldGrid::new();
282 let flat = world.witness_flat();
283 assert_eq!(flat.len(), ZONE_COUNT * 4);
284 }
285
286 #[test]
287 fn test_zone_shell() {
288 assert_eq!(WorldGrid::zone_shell(0), 0);
289 assert_eq!(WorldGrid::zone_shell(1), 1);
290 assert_eq!(WorldGrid::zone_shell(6), 1);
291 assert_eq!(WorldGrid::zone_shell(7), 2);
292 assert_eq!(WorldGrid::zone_shell(18), 2);
293 assert_eq!(WorldGrid::zone_shell(19), 3);
294 assert_eq!(WorldGrid::zone_shell(40), 3);
295 }
296}