#![cfg(feature = "deterministic")]
use amari::deterministic::ga2d::{DetRotor2, DetVector2};
use amari::deterministic::DetF32;
#[derive(Clone, Debug)]
struct Entity {
position: DetVector2,
velocity: DetVector2,
rotation: DetRotor2,
angular_velocity: DetF32,
}
impl Entity {
fn new(x: f32, y: f32) -> Self {
Self {
position: DetVector2::from_f32(x, y),
velocity: DetVector2::ZERO,
rotation: DetRotor2::IDENTITY,
angular_velocity: DetF32::ZERO,
}
}
fn step(&mut self, dt: DetF32, gravity: DetVector2) {
self.velocity = self.velocity + gravity * dt;
self.position = self.position + self.velocity * dt;
let delta_angle = self.angular_velocity * dt;
self.rotation = self.rotation * DetRotor2::from_angle(delta_angle);
}
fn to_bits(&self) -> EntityBits {
EntityBits {
pos_x: self.position.x.to_bits(),
pos_y: self.position.y.to_bits(),
vel_x: self.velocity.x.to_bits(),
vel_y: self.velocity.y.to_bits(),
rot_s: self.rotation.s.to_bits(),
rot_b: self.rotation.b.to_bits(),
ang_vel: self.angular_velocity.to_bits(),
}
}
fn from_bits(bits: EntityBits) -> Self {
Self {
position: DetVector2::new(DetF32::from_bits(bits.pos_x), DetF32::from_bits(bits.pos_y)),
velocity: DetVector2::new(DetF32::from_bits(bits.vel_x), DetF32::from_bits(bits.vel_y)),
rotation: DetRotor2::new(DetF32::from_bits(bits.rot_s), DetF32::from_bits(bits.rot_b)),
angular_velocity: DetF32::from_bits(bits.ang_vel),
}
}
fn bit_identical(&self, other: &Self) -> bool {
self.to_bits() == other.to_bits()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
struct EntityBits {
pos_x: u32,
pos_y: u32,
vel_x: u32,
vel_y: u32,
rot_s: u32,
rot_b: u32,
ang_vel: u32,
}
#[derive(Clone, Copy, Debug)]
struct Input {
thrust: DetVector2,
torque: DetF32,
}
impl Input {
fn none() -> Self {
Self {
thrust: DetVector2::ZERO,
torque: DetF32::ZERO,
}
}
}
#[derive(Clone)]
struct GameState {
entities: Vec<Entity>,
frame: u32,
}
impl GameState {
fn new() -> Self {
Self {
entities: Vec::new(),
frame: 0,
}
}
fn add_entity(&mut self, entity: Entity) {
self.entities.push(entity);
}
fn step(&mut self, inputs: &[Input], dt: DetF32, gravity: DetVector2) {
for (entity, input) in self.entities.iter_mut().zip(inputs.iter()) {
entity.velocity = entity.velocity + input.thrust;
entity.angular_velocity = entity.angular_velocity + input.torque;
}
for entity in &mut self.entities {
entity.step(dt, gravity);
}
self.frame += 1;
}
fn to_bits(&self) -> Vec<EntityBits> {
self.entities.iter().map(|e| e.to_bits()).collect()
}
fn bit_identical(&self, other: &Self) -> bool {
if self.frame != other.frame || self.entities.len() != other.entities.len() {
return false;
}
self.entities
.iter()
.zip(other.entities.iter())
.all(|(a, b)| a.bit_identical(b))
}
}
fn demo_lockstep() {
println!("\n=== Lockstep Networking Demo ===\n");
let dt = DetF32::from_f32(1.0 / 60.0); let gravity = DetVector2::from_f32(0.0, -9.8);
let mut client_a = GameState::new();
let mut client_b = GameState::new();
client_a.add_entity(Entity::new(0.0, 10.0));
client_b.add_entity(Entity::new(0.0, 10.0));
println!("Initial state (both clients):");
println!(" Position: ({:.2}, {:.2})", 0.0, 10.0);
println!(" Frame: 0\n");
let input_sequence = [
Input {
thrust: DetVector2::from_f32(5.0, 0.0),
torque: DetF32::from_f32(0.1),
},
Input::none(),
Input {
thrust: DetVector2::from_f32(0.0, 5.0),
torque: DetF32::from_f32(-0.05),
},
];
for frame in 0..100 {
let input = input_sequence[frame % input_sequence.len()];
let inputs = vec![input];
client_a.step(&inputs, dt, gravity);
client_b.step(&inputs, dt, gravity);
}
println!("After 100 frames:");
let entity_a = &client_a.entities[0];
let entity_b = &client_b.entities[0];
println!(
"Client A position: ({:.6}, {:.6})",
entity_a.position.x.to_f32(),
entity_a.position.y.to_f32()
);
println!(
"Client B position: ({:.6}, {:.6})",
entity_b.position.x.to_f32(),
entity_b.position.y.to_f32()
);
if client_a.bit_identical(&client_b) {
println!("\n✓ SUCCESS: States are bit-identical!");
println!(" No desync occurred despite 100 frames of simulation.");
} else {
println!("\n✗ FAILURE: States diverged!");
}
let bits_a = entity_a.position.x.to_bits();
let bits_b = entity_b.position.x.to_bits();
println!("\nBit patterns for position.x:");
println!(" Client A: 0x{:08x}", bits_a);
println!(" Client B: 0x{:08x}", bits_b);
println!(" Match: {}", bits_a == bits_b);
}
fn demo_rollback() {
println!("\n=== Rollback Netcode Demo ===\n");
let dt = DetF32::from_f32(1.0 / 60.0);
let gravity = DetVector2::from_f32(0.0, -9.8);
let mut state = GameState::new();
state.add_entity(Entity::new(0.0, 10.0));
let predicted_input = vec![Input {
thrust: DetVector2::from_f32(5.0, 0.0),
torque: DetF32::ZERO,
}];
println!("Simulating frames 0-10 with predicted input...");
for _ in 0..10 {
state.step(&predicted_input, dt, gravity);
}
let frame_10_state = state.clone();
let frame_10_bits = state.to_bits();
println!(
" Frame 10 position: ({:.6}, {:.6})",
state.entities[0].position.x.to_f32(),
state.entities[0].position.y.to_f32()
);
for _ in 0..5 {
state.step(&predicted_input, dt, gravity);
}
println!(
" Frame 15 position: ({:.6}, {:.6})",
state.entities[0].position.x.to_f32(),
state.entities[0].position.y.to_f32()
);
let actual_input = vec![Input {
thrust: DetVector2::from_f32(0.0, 10.0), torque: DetF32::from_f32(0.2),
}];
println!("\n✗ Misprediction detected at frame 10!");
println!(" Rolling back to frame 10...");
let mut restored_state = GameState {
entities: frame_10_bits
.iter()
.map(|bits| Entity::from_bits(*bits))
.collect(),
frame: 10,
};
if restored_state.bit_identical(&frame_10_state) {
println!(" ✓ State restored bit-exactly from frame 10");
}
println!(" Re-simulating frames 10-15 with actual input...");
for _ in 0..5 {
restored_state.step(&actual_input, dt, gravity);
}
println!(
" New frame 15 position: ({:.6}, {:.6})",
restored_state.entities[0].position.x.to_f32(),
restored_state.entities[0].position.y.to_f32()
);
println!("\n✓ Rollback complete! Game state is now correct.");
}
fn demo_replay() {
println!("\n=== Replay Verification Demo ===\n");
let dt = DetF32::from_f32(1.0 / 60.0);
let gravity = DetVector2::from_f32(0.0, -9.8);
println!("Recording gameplay session (50 frames)...");
let mut recorder = GameState::new();
recorder.add_entity(Entity::new(5.0, 15.0));
let mut input_log = Vec::new();
for frame in 0..50 {
let input = if frame % 20 < 10 {
Input {
thrust: DetVector2::from_f32(3.0, 0.0),
torque: DetF32::from_f32(0.15),
}
} else {
Input {
thrust: DetVector2::from_f32(-2.0, 5.0),
torque: DetF32::from_f32(-0.1),
}
};
input_log.push(input);
recorder.step(&[input], dt, gravity);
}
let recorded_bits = recorder.to_bits();
println!(
" Final position: ({:.6}, {:.6})",
recorder.entities[0].position.x.to_f32(),
recorder.entities[0].position.y.to_f32()
);
println!("\nReplaying recorded session...");
let mut replay = GameState::new();
replay.add_entity(Entity::new(5.0, 15.0));
for input in &input_log {
replay.step(&[*input], dt, gravity);
}
println!(
" Final position: ({:.6}, {:.6})",
replay.entities[0].position.x.to_f32(),
replay.entities[0].position.y.to_f32()
);
let replay_bits = replay.to_bits();
if recorded_bits == replay_bits {
println!("\n✓ SUCCESS: Replay matches recording bit-exactly!");
println!(" This works across platforms and compiler optimizations.");
} else {
println!("\n✗ FAILURE: Replay diverged from recording!");
}
println!("\nBit pattern verification:");
for (i, (rec, rep)) in recorded_bits[0..1]
.iter()
.zip(&replay_bits[0..1])
.enumerate()
{
println!(" Entity {}: match={}", i, rec == rep);
if rec != rep {
println!(" Recorded: 0x{:08x}", rec.pos_x);
println!(" Replayed: 0x{:08x}", rep.pos_x);
}
}
}
fn demo_collision() {
println!("\n=== Deterministic Collision Demo ===\n");
let dt = DetF32::from_f32(1.0 / 60.0);
let gravity = DetVector2::from_f32(0.0, -9.8);
let mut entity1 = Entity::new(-5.0, 10.0);
let mut entity2 = Entity::new(5.0, 10.0);
entity1.velocity = DetVector2::from_f32(10.0, 0.0); entity2.velocity = DetVector2::from_f32(-8.0, 0.0);
println!("Initial setup:");
println!(
" Entity 1: pos=({:.2}, {:.2}), vel=({:.2}, {:.2})",
-5.0, 10.0, 10.0, 0.0
);
println!(
" Entity 2: pos=({:.2}, {:.2}), vel=({:.2}, {:.2})",
5.0, 10.0, -8.0, 0.0
);
let collision_radius = DetF32::from_f32(1.0);
let mut frame = 0;
let mut collision_frame = None;
for _ in 0..60 {
entity1.step(dt, gravity);
entity2.step(dt, gravity);
frame += 1;
let diff = entity2.position - entity1.position;
let distance = diff.magnitude();
if distance < collision_radius * DetF32::TWO && collision_frame.is_none() {
collision_frame = Some(frame);
println!("\n✓ Collision detected at frame {}!", frame);
println!(
" Entity 1 position: ({:.6}, {:.6})",
entity1.position.x.to_f32(),
entity1.position.y.to_f32()
);
println!(
" Entity 2 position: ({:.6}, {:.6})",
entity2.position.x.to_f32(),
entity2.position.y.to_f32()
);
println!(" Distance: {:.6}", distance.to_f32());
let normal = diff.normalize();
let relative_velocity = entity2.velocity - entity1.velocity;
let velocity_along_normal = relative_velocity.dot(normal);
if velocity_along_normal < DetF32::ZERO {
let impulse = normal * velocity_along_normal;
entity1.velocity = entity1.velocity + impulse;
entity2.velocity = entity2.velocity - impulse;
}
break;
}
}
println!("\nRepeating simulation to verify determinism...");
let mut test1 = Entity::new(-5.0, 10.0);
let mut test2 = Entity::new(5.0, 10.0);
test1.velocity = DetVector2::from_f32(10.0, 0.0);
test2.velocity = DetVector2::from_f32(-8.0, 0.0);
for _ in 0..collision_frame.unwrap_or(0) {
test1.step(dt, gravity);
test2.step(dt, gravity);
}
if test1.bit_identical(&entity1) && test2.bit_identical(&entity2) {
println!("✓ Collision occurs at identical frame on replay!");
} else {
println!("✗ Collision frame differs on replay!");
}
}
fn main() {
println!("╔════════════════════════════════════════════════════════════╗");
println!("║ Deterministic Physics for Networked Games ║");
println!("║ Amari v0.9.9 - Bit-Exact Cross-Platform Reproducibility ║");
println!("╚════════════════════════════════════════════════════════════╝");
demo_lockstep();
demo_rollback();
demo_replay();
demo_collision();
println!("\n╔════════════════════════════════════════════════════════════╗");
println!("║ Key Takeaways ║");
println!("╠════════════════════════════════════════════════════════════╣");
println!("║ • All operations produce identical bit patterns ║");
println!("║ • Lockstep networking stays synchronized ║");
println!("║ • Rollback works perfectly (no accumulating errors) ║");
println!("║ • Replays work across platforms and years later ║");
println!("║ • ~10-20%% performance overhead vs native f32 ║");
println!("╚════════════════════════════════════════════════════════════╝\n");
}