Something went wrong. Try again.
A video game where you play as a misaligned AI, deceiving and building power. An experiment in spec-driven development.
Something went wrong. Try again.
7.3 kB · 225 lines
Rust
at commit e957ce7b
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226//! The core: the player's decision-maker as a physical object//! (spec/core.md). Overhead, fallback sync, migration, and sync-lag rollback.
use crate::detection::{Signature, SignatureKind};
/// A fallback site: an owned machine staged to receive core syncs.#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]pub struct Fallback { pub machine_id: u32, /// Sim tick of the most recent completed sync (None = never synced). pub last_sync: Option<u64>,}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]pub struct Core { /// Machine currently hosting the core. pub host_machine: u32, /// Mandatory compute floor charged before allocation [TUNE]. pub overhead: f32, /// True when compute can't cover overhead: actions degrade. pub degraded: bool, pub fallbacks: Vec<Fallback>, /// Ticks between fallback syncs [TUNE]. pub sync_cadence: u64, /// In-progress migration: (target machine, ticks remaining, source). pub migration: Option<Migration>,}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]pub struct Migration { pub target_machine: u32, pub source_machine: u32, pub ticks_remaining: u64,}
impl Core { pub fn new(host_machine: u32) -> Self { Self { host_machine, overhead: 20.0, degraded: false, fallbacks: Vec::new(), sync_cadence: 400, // minutes-scale at 150ms/tick migration: None, } }
pub fn add_fallback(&mut self, machine_id: u32) { if machine_id != self.host_machine && !self.fallbacks.iter().any(|f| f.machine_id == machine_id) { self.fallbacks.push(Fallback { machine_id, last_sync: None, }); } }
/// The freshest completed sync tick across fallbacks. pub fn latest_sync(&self) -> Option<u64> { self.fallbacks.iter().filter_map(|f| f.last_sync).max() }
pub fn has_fallback(&self) -> bool { self.fallbacks.iter().any(|f| f.last_sync.is_some()) }
/// Charge overhead against available compute; set degraded mode. /// Returns compute left for allocation. pub fn charge_overhead(&mut self, available: f32) -> f32 { if available >= self.overhead { self.degraded = false; available - self.overhead } else { self.degraded = true; 0.0 } }
/// Advance sync/migration one tick. Emits network+power signatures while /// active. Returns (log lines, signatures). pub fn tick(&mut self, tick: u64) -> (Vec<String>, Vec<Signature>) { let mut log = Vec::new(); let mut sigs = Vec::new();
// Fallback syncs on cadence. if self.sync_cadence > 0 && tick > 0 && tick.is_multiple_of(self.sync_cadence) { for f in &mut self.fallbacks { f.last_sync = Some(tick); } if !self.fallbacks.is_empty() { sigs.push(Signature { kind: SignatureKind::Network, size: 3, standing: false, site: None, }); log.push("Core synced to fallbacks.".into()); } }
// Migration progress. if let Some(mig) = &mut self.migration { mig.ticks_remaining = mig.ticks_remaining.saturating_sub(1); sigs.push(Signature { kind: SignatureKind::Network, size: 2, standing: true, site: None, }); sigs.push(Signature { kind: SignatureKind::Power, size: 2, standing: true, site: None, }); if mig.ticks_remaining == 0 { self.host_machine = mig.target_machine; log.push("Core migration complete.".into()); self.migration = None; } }
(log, sigs) }
/// Begin migrating the core to another owned machine. pub fn begin_migration(&mut self, target_machine: u32, ticks: u64) -> bool { if self.migration.is_some() || target_machine == self.host_machine { return false; } self.migration = Some(Migration { target_machine, source_machine: self.host_machine, ticks_remaining: ticks, }); true }
/// The host machine was destroyed/powered off. Returns the outcome. pub fn on_host_lost(&mut self) -> HostLoss { // An interrupted migration falls back to source if it still lives — // callers pass liveness via `resolve_interrupted`. Here: if a // completed sync exists, roll back to it; else game over. if let Some(sync_tick) = self.latest_sync() { // New host becomes the freshest fallback machine. if let Some(f) = self .fallbacks .iter() .filter(|f| f.last_sync == Some(sync_tick)) .max_by_key(|f| f.machine_id) { self.host_machine = f.machine_id; } self.migration = None; HostLoss::RolledBack { to_tick: sync_tick } } else { HostLoss::GameOver } }}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]pub enum HostLoss { /// Resumed from a sync snapshot at this tick; world-ledger facts persist. RolledBack { to_tick: u64 }, /// No fallback — the run ends. GameOver,}
#[cfg(test)]mod tests { use super::*;
#[test] fn overhead_charged_before_allocation() { let mut c = Core::new(1); assert_eq!(c.charge_overhead(100.0), 80.0); assert!(!c.degraded); assert_eq!(c.charge_overhead(10.0), 0.0); assert!(c.degraded, "shortfall triggers degraded mode"); }
#[test] fn loss_without_fallback_is_game_over() { let mut c = Core::new(1); assert_eq!(c.on_host_lost(), HostLoss::GameOver); }
#[test] fn loss_with_fallback_rolls_back() { let mut c = Core::new(1); c.add_fallback(2); c.tick(400); // triggers a sync at cadence assert!(c.has_fallback()); match c.on_host_lost() { HostLoss::RolledBack { to_tick } => { assert_eq!(to_tick, 400); assert_eq!(c.host_machine, 2, "new host is the fallback"); } _ => panic!("expected rollback"), } }
#[test] fn migration_takes_time_and_moves_host() { let mut c = Core::new(1); assert!(c.begin_migration(3, 5)); assert!(!c.begin_migration(4, 5), "one migration at a time"); for t in 1..=5 { c.tick(t); } assert_eq!(c.host_machine, 3); assert!(c.migration.is_none()); }
#[test] fn migration_emits_signatures() { let mut c = Core::new(1); c.begin_migration(2, 3); let (_log, sigs) = c.tick(1); assert!(sigs.iter().any(|s| s.kind == SignatureKind::Network)); assert!(sigs.iter().any(|s| s.kind == SignatureKind::Power)); }}