1use std::collections::{HashMap, HashSet, VecDeque};
7
8pub type AgentId = u32;
9
10#[derive(Debug, Clone)]
13pub struct Message {
14 pub from: AgentId,
15 pub to: AgentId,
16 pub payload: String,
17 pub nutrient: f32, pub hop_count: u32,
19 pub max_hops: u32,
20}
21
22impl Message {
23 pub fn new(from: AgentId, to: AgentId, payload: &str) -> Self {
24 Self {
25 from,
26 to,
27 payload: payload.to_string(),
28 nutrient: 0.0,
29 hop_count: 0,
30 max_hops: 4,
31 }
32 }
33
34 pub fn with_max_hops(mut self, max: u32) -> Self {
35 self.max_hops = max;
36 self
37 }
38}
39
40#[derive(Debug, Clone)]
43pub struct DeliveryResult {
44 pub delivered: bool,
45 pub from: AgentId,
46 pub to: AgentId,
47 pub hops: u32,
48 pub cost: f32,
49 pub path: Vec<AgentId>,
50 pub reason: String,
51}
52
53#[derive(Debug, Clone)]
56pub struct Agent {
57 pub id: AgentId,
58 pub energy: f32,
59 pub alive: bool,
60}
61
62impl Agent {
63 pub fn new(id: AgentId, energy: f32) -> Self {
64 Self {
65 id,
66 energy,
67 alive: true,
68 }
69 }
70}
71
72pub struct RelayNetwork {
75 agents: HashMap<AgentId, Agent>,
76 trust: HashMap<(AgentId, AgentId), f32>, hop_cost: f32,
78 nutrient_per_hop: f32,
79 trust_boost: f32,
80 trust_degrade: f32,
81 trust_halflife_secs: u64,
82}
83
84impl RelayNetwork {
85 pub fn new() -> Self {
86 Self {
87 agents: HashMap::new(),
88 trust: HashMap::new(),
89 hop_cost: 0.1,
90 nutrient_per_hop: 0.02,
91 trust_boost: 0.01,
92 trust_degrade: 0.02,
93 trust_halflife_secs: 3600, }
95 }
96
97 pub fn with_config(
98 hop_cost: f32,
99 nutrient_per_hop: f32,
100 trust_boost: f32,
101 trust_degrade: f32,
102 trust_halflife_secs: u64,
103 ) -> Self {
104 Self {
105 hop_cost,
106 nutrient_per_hop,
107 trust_boost,
108 trust_degrade,
109 trust_halflife_secs,
110 ..Self::new()
111 }
112 }
113
114 pub fn add_agent(&mut self, id: AgentId, energy: f32) {
117 self.agents.insert(id, Agent::new(id, energy));
118 }
119
120 pub fn remove_agent(&mut self, id: AgentId) {
121 self.agents.remove(&id);
122 self.trust.retain(|(a, b), _| *a != id && *b != id);
124 }
125
126 pub fn agent(&self, id: AgentId) -> Option<&Agent> {
127 self.agents.get(&id)
128 }
129
130 pub fn agent_count(&self) -> usize {
131 self.agents.len()
132 }
133
134 pub fn alive_agents(&self) -> Vec<AgentId> {
135 self.agents.values().filter(|a| a.alive).map(|a| a.id).collect()
136 }
137
138 pub fn set_trust(&mut self, from: AgentId, to: AgentId, level: f32) {
141 let clamped = level.max(0.0).min(1.0);
142 self.trust.insert((from, to), clamped);
143 }
144
145 pub fn trust(&self, from: AgentId, to: AgentId) -> f32 {
146 *self.trust.get(&(from, to)).unwrap_or(&0.0)
147 }
148
149 pub fn boost_trust(&mut self, from: AgentId, to: AgentId) {
150 let current = self.trust(from, to);
151 self.set_trust(from, to, (current + self.trust_boost).min(1.0));
152 }
153
154 pub fn degrade_trust(&mut self, from: AgentId, to: AgentId) {
155 let current = self.trust(from, to);
156 self.set_trust(from, to, (current - self.trust_degrade).max(0.0));
157 }
158
159 pub fn decay_all_trust(&mut self, elapsed_secs: u64) {
161 if elapsed_secs == 0 { return; }
162 let factor = 0.5_f32.powi(elapsed_secs as i32 / self.trust_halflife_secs.max(1) as i32);
163 for t in self.trust.values_mut() {
164 *t *= factor;
165 }
166 }
167
168 pub fn find_path(&self, from: AgentId, to: AgentId, max_hops: u32) -> Option<Vec<AgentId>> {
173 if from == to { return Some(vec![from]); }
174 if !self.agents.contains_key(&from) || !self.agents.contains_key(&to) { return None; }
175
176 let mut queue: VecDeque<(AgentId, Vec<AgentId>, f32)> = VecDeque::new();
179 let mut visited: HashSet<AgentId> = HashSet::new();
180
181 queue.push_back((from, vec![from], 1.0));
182 visited.insert(from);
183
184 while let Some((current, path, path_trust)) = queue.pop_front() {
185 if path.len() as u32 > max_hops + 1 { continue; }
186
187 let mut neighbors: Vec<(AgentId, f32)> = Vec::new();
189 for (id, _agent) in &self.agents {
190 if *id == current || visited.contains(id) { continue; }
191 let forward = self.trust(current, *id);
192 let backward = self.trust(*id, current);
193 if forward > 0.0 || backward > 0.0 {
194 let edge_trust = (forward + backward) / 2.0;
195 neighbors.push((*id, edge_trust));
196 }
197 }
198
199 neighbors.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
201
202 for (neighbor, edge_trust) in neighbors {
203 let mut new_path = path.clone();
204 new_path.push(neighbor);
205
206 if neighbor == to {
207 return Some(new_path);
208 }
209
210 visited.insert(neighbor);
211 queue.push_back((neighbor, new_path, path_trust * edge_trust));
212 }
213 }
214
215 None }
217
218 pub fn send(&mut self, msg: Message) -> DeliveryResult {
222 if !self.agents.contains_key(&msg.from) {
224 return DeliveryResult {
225 delivered: false,
226 from: msg.from,
227 to: msg.to,
228 hops: 0,
229 cost: 0.0,
230 path: vec![],
231 reason: "source agent not found".to_string(),
232 };
233 }
234 if !self.agents.contains_key(&msg.to) {
235 return DeliveryResult {
236 delivered: false,
237 from: msg.from,
238 to: msg.to,
239 hops: 0,
240 cost: 0.0,
241 path: vec![],
242 reason: "destination agent not found".to_string(),
243 };
244 }
245
246 let path = match self.find_path(msg.from, msg.to, msg.max_hops) {
248 Some(p) => p,
249 None => {
250 if self.trust(msg.from, msg.to) > 0.0 {
252 vec![msg.from, msg.to]
253 } else {
254 return DeliveryResult {
255 delivered: false,
256 from: msg.from,
257 to: msg.to,
258 hops: 0,
259 cost: 0.0,
260 path: vec![],
261 reason: "no path found within max hops".to_string(),
262 };
263 }
264 }
265 };
266
267 let hops = (path.len() - 1) as u32;
268 let cost = hops as f32 * self.hop_cost;
269
270 if let Some(agent) = self.agents.get_mut(&msg.from) {
272 if agent.energy < cost {
273 return DeliveryResult {
274 delivered: false,
275 from: msg.from,
276 to: msg.to,
277 hops: 0,
278 cost: 0.0,
279 path: vec![],
280 reason: format!("insufficient energy: need {:.2}, have {:.2}", cost, agent.energy),
281 };
282 }
283 agent.energy -= cost;
284 }
285
286 for i in 0..path.len().saturating_sub(1) {
288 self.boost_trust(path[i], path[i + 1]);
289 }
290
291 DeliveryResult {
292 delivered: true,
293 from: msg.from,
294 to: msg.to,
295 hops,
296 cost,
297 path: path.clone(),
298 reason: format!("delivered via {} hops, cost {:.2}", hops, cost),
299 }
300 }
301
302 pub fn spore_probe(&self, source: AgentId, max_hops: u32) -> Vec<AgentId> {
307 let mut reachable = Vec::new();
308 for (id, _) in &self.agents {
309 if *id == source { continue; }
310 if self.find_path(source, *id, max_hops).is_some() {
311 reachable.push(*id);
312 }
313 }
314 reachable
315 }
316
317 pub fn connection_count(&self) -> usize {
321 self.trust.iter().filter(|(_, &t)| t > 0.0).count()
322 }
323
324 pub fn average_trust(&self) -> f32 {
326 let count = self.trust.len();
327 if count == 0 { return 0.0; }
328 let sum: f32 = self.trust.values().sum();
329 sum / count as f32
330 }
331
332 pub fn strongest_connection(&self) -> Option<(AgentId, AgentId, f32)> {
334 self.trust.iter()
335 .filter(|(_, &t)| t > 0.0)
336 .max_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
337 .map(|(&(a, b), &t)| (a, b, t))
338 }
339
340 pub fn weakest_connection(&self) -> Option<(AgentId, AgentId, f32)> {
342 self.trust.iter()
343 .filter(|(_, &t)| t > 0.0)
344 .min_by(|a, b| a.1.partial_cmp(b.1).unwrap_or(std::cmp::Ordering::Equal))
345 .map(|(&(a, b), &t)| (a, b, t))
346 }
347}
348
349impl Default for RelayNetwork {
350 fn default() -> Self {
351 Self::new()
352 }
353}
354
355#[cfg(test)]
358mod tests {
359 use super::*;
360
361 fn triangle_network() -> RelayNetwork {
362 let mut net = RelayNetwork::new();
363 net.add_agent(0, 10.0);
364 net.add_agent(1, 10.0);
365 net.add_agent(2, 10.0);
366 net.set_trust(0, 1, 0.8);
367 net.set_trust(1, 0, 0.7);
368 net.set_trust(1, 2, 0.6);
369 net.set_trust(2, 1, 0.5);
370 net.set_trust(0, 2, 0.3);
371 net.set_trust(2, 0, 0.3);
372 net
373 }
374
375 #[test]
376 fn test_add_agents() {
377 let mut net = RelayNetwork::new();
378 net.add_agent(0, 10.0);
379 net.add_agent(1, 5.0);
380 assert_eq!(net.agent_count(), 2);
381 assert!(net.agent(0).is_some());
382 assert_eq!(net.agent(0).unwrap().energy, 10.0);
383 }
384
385 #[test]
386 fn test_remove_agent() {
387 let mut net = triangle_network();
388 net.remove_agent(1);
389 assert_eq!(net.agent_count(), 2);
390 assert!(net.agent(1).is_none());
391 }
392
393 #[test]
394 fn test_trust_set_get() {
395 let mut net = RelayNetwork::new();
396 net.set_trust(0, 1, 0.8);
397 assert_eq!(net.trust(0, 1), 0.8);
398 assert_eq!(net.trust(1, 0), 0.0); }
400
401 #[test]
402 fn test_trust_clamping() {
403 let mut net = RelayNetwork::new();
404 net.set_trust(0, 1, 1.5);
405 assert_eq!(net.trust(0, 1), 1.0);
406 net.set_trust(0, 1, -0.5);
407 assert_eq!(net.trust(0, 1), 0.0);
408 }
409
410 #[test]
411 fn test_trust_boost_degrade() {
412 let mut net = RelayNetwork::new();
413 net.set_trust(0, 1, 0.5);
414 let boosted = net.trust(0, 1);
415 net.boost_trust(0, 1);
416 assert!(net.trust(0, 1) > boosted);
417 let after_boost = net.trust(0, 1);
418 net.degrade_trust(0, 1);
419 assert!(net.trust(0, 1) < after_boost);
420 }
421
422 #[test]
423 fn test_trust_decay() {
424 let mut net = RelayNetwork::with_config(0.1, 0.02, 0.01, 0.02, 100);
425 net.set_trust(0, 1, 0.8);
426 net.decay_all_trust(100); assert!((net.trust(0, 1) - 0.4).abs() < 0.01);
428 }
429
430 #[test]
431 fn test_direct_path() {
432 let net = triangle_network();
433 let path = net.find_path(0, 1, 4).unwrap();
434 assert_eq!(path, vec![0, 1]);
435 }
436
437 #[test]
438 fn test_indirect_path() {
439 let mut net = RelayNetwork::new();
440 net.add_agent(0, 10.0);
441 net.add_agent(1, 10.0);
442 net.add_agent(2, 10.0);
443 net.set_trust(0, 1, 0.5);
444 net.set_trust(1, 0, 0.5);
445 net.set_trust(1, 2, 0.5);
446 net.set_trust(2, 1, 0.5);
447 let path = net.find_path(0, 2, 4).unwrap();
449 assert_eq!(path, vec![0, 1, 2]);
450 }
451
452 #[test]
453 fn test_same_agent_path() {
454 let net = triangle_network();
455 let path = net.find_path(0, 0, 4).unwrap();
456 assert_eq!(path, vec![0]);
457 }
458
459 #[test]
460 fn test_no_path_nonexistent() {
461 let mut net = RelayNetwork::new();
462 net.add_agent(0, 10.0);
463 net.add_agent(1, 10.0);
464 let path = net.find_path(0, 99, 4);
465 assert!(path.is_none());
466 }
467
468 #[test]
469 fn test_no_path_disconnected() {
470 let mut net = RelayNetwork::new();
471 net.add_agent(0, 10.0);
472 net.add_agent(1, 10.0);
473 let path = net.find_path(0, 1, 4);
475 assert!(path.is_none());
476 }
477
478 #[test]
479 fn test_send_direct() {
480 let mut net = triangle_network();
481 let msg = Message::new(0, 1, "hello");
482 let result = net.send(msg);
483 assert!(result.delivered);
484 assert_eq!(result.hops, 1);
485 assert_eq!(result.path, vec![0, 1]);
486 }
487
488 #[test]
489 fn test_send_indirect() {
490 let mut net = triangle_network();
491 let msg = Message::new(0, 2, "hello");
492 let result = net.send(msg);
493 assert!(result.delivered);
494 assert!(result.hops >= 1);
495 assert_eq!(*result.path.last().unwrap(), 2);
496 }
497
498 #[test]
499 fn test_send_nonexistent_source() {
500 let mut net = triangle_network();
501
502 let msg = Message::new(99, 0, "hello");
503 let result = net.send(msg);
504 assert!(!result.delivered);
505 }
506
507 #[test]
508 fn test_send_nonexistent_dest() {
509 let mut net = triangle_network();
510
511 let msg = Message::new(0, 99, "hello");
512 let result = net.send(msg);
513 assert!(!result.delivered);
514 }
515
516 #[test]
517 fn test_send_insufficient_energy() {
518 let mut net = RelayNetwork::new();
519 net.add_agent(0, 0.01);
520 net.add_agent(1, 10.0);
521 net.set_trust(0, 1, 0.9);
522 net.set_trust(1, 0, 0.9);
523 let msg = Message::new(0, 1, "hello");
524 let result = net.send(msg);
525 assert!(!result.delivered);
526 assert!(result.reason.contains("insufficient energy"));
527 }
528
529 #[test]
530 fn test_energy_deduction() {
531 let mut net = RelayNetwork::new();
532 net.add_agent(0, 10.0);
533 net.add_agent(1, 10.0);
534 net.set_trust(0, 1, 0.9);
535 net.set_trust(1, 0, 0.9);
536 let msg = Message::new(0, 1, "hello");
537 let _ = net.send(msg);
538 assert!(net.agent(0).unwrap().energy < 10.0);
540 }
541
542 #[test]
543 fn test_trust_boost_on_delivery() {
544 let mut net = RelayNetwork::new();
545 net.add_agent(0, 10.0);
546 net.add_agent(1, 10.0);
547 net.set_trust(0, 1, 0.5);
548 net.set_trust(1, 0, 0.5);
549 let before = net.trust(0, 1);
550 let msg = Message::new(0, 1, "hello");
551 let _ = net.send(msg);
552 assert!(net.trust(0, 1) > before);
553 }
554
555 #[test]
556 fn test_spore_probe() {
557 let net = triangle_network();
558 let reachable = net.spore_probe(0, 4);
559 assert!(reachable.contains(&1));
560 assert!(reachable.contains(&2));
561 }
562
563 #[test]
564 fn test_connection_count() {
565 let net = triangle_network();
566 assert_eq!(net.connection_count(), 6);
567 }
568
569 #[test]
570 fn test_average_trust() {
571 let net = triangle_network();
572 let avg = net.average_trust();
573 assert!((avg - 0.533).abs() < 0.01);
575 }
576
577 #[test]
578 fn test_strongest_weakest() {
579 let net = triangle_network();
580 let strongest = net.strongest_connection().unwrap();
581 assert_eq!(strongest.2, 0.8); let weakest = net.weakest_connection().unwrap();
583 assert_eq!(weakest.2, 0.3); }
585
586 #[test]
587 fn test_max_hops_limit() {
588 let mut net = RelayNetwork::new();
589 net.add_agent(0, 10.0);
590 net.add_agent(1, 10.0);
591 net.add_agent(2, 10.0);
592 net.add_agent(3, 10.0);
593 net.set_trust(0, 1, 0.5);
594 net.set_trust(1, 0, 0.5);
595 net.set_trust(1, 2, 0.5);
596 net.set_trust(2, 1, 0.5);
597 net.set_trust(2, 3, 0.5);
598 net.set_trust(3, 2, 0.5);
599 let path = net.find_path(0, 3, 1);
601 assert!(path.is_none());
602 let path = net.find_path(0, 3, 3);
604 assert!(path.is_some());
605 }
606
607 #[test]
608 fn test_dead_agent_pruning() {
609 let mut net = triangle_network();
610 net.remove_agent(1);
611 let path = net.find_path(0, 2, 4);
613 assert_eq!(path.unwrap(), vec![0, 2]);
614 }
615
616 #[test]
617 fn test_custom_config() {
618 let net = RelayNetwork::with_config(0.5, 0.1, 0.05, 0.1, 600);
619 assert_eq!(net.agent_count(), 0);
620 }
621
622 #[test]
623 fn test_alive_agents() {
624 let net = triangle_network();
625 let alive = net.alive_agents();
626 assert_eq!(alive.len(), 3);
627 }
628
629 #[test]
630 fn test_message_with_max_hops() {
631 let msg = Message::new(0, 1, "hello").with_max_hops(2);
632 assert_eq!(msg.max_hops, 2);
633 }
634}