fix(simulation): address #843 review — seeding, atomics, API consistency
Review fixes from Hoshe + Tyre: - EntityRng seeding: splitmix64(seed) ^ splitmix64(id) instead of splitmix64(seed + id) — eliminates collision class where adjacent seeds produce identical streams - AtomicBool ordering: Relaxed → SeqCst for shutdown flag (correct on weakly-ordered architectures) - Worker Drop: join handles instead of detaching threads - Normalize stub API: remove ChunkGenWorker convenience wrappers, use .pool consistently across all 3 workers - trigger_monologue: downgrade &mut to shared refs (no-op anchor was blocking parallel systems) - Remove dead SimRng inserts from migrated monologue tests - Document determinism gap on poll_worker_results - Document bevy_tasks/rayon dep rationale in Cargo.toml Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -52,7 +52,9 @@ impl EntityRng {
|
||||
/// Uses splitmix64 mixing to combine the two seeds with good avalanche
|
||||
/// properties — avoids correlated sequences for entities with similar IDs.
|
||||
pub fn from_seed_and_id(world_seed: u64, stable_id: u64) -> Self {
|
||||
let mixed = splitmix64(world_seed.wrapping_add(stable_id));
|
||||
// Mix each input independently then XOR — avoids the collision class
|
||||
// where (seed=0, id=N) == (seed=1, id=N-1) that wrapping_add creates.
|
||||
let mixed = splitmix64(world_seed) ^ splitmix64(stable_id);
|
||||
Self {
|
||||
rng: ChaCha20Rng::seed_from_u64(mixed),
|
||||
}
|
||||
@@ -91,4 +93,33 @@ mod tests {
|
||||
let val2: u32 = rng2.rng.random();
|
||||
assert_ne!(val1, val2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entity_rng_same_seed_same_id_same_sequence() {
|
||||
let mut a = EntityRng::from_seed_and_id(42, 7);
|
||||
let mut b = EntityRng::from_seed_and_id(42, 7);
|
||||
let va: Vec<u32> = (0..100).map(|_| a.rng.random()).collect();
|
||||
let vb: Vec<u32> = (0..100).map(|_| b.rng.random()).collect();
|
||||
assert_eq!(va, vb);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entity_rng_same_seed_different_id_different_sequence() {
|
||||
let mut a = EntityRng::from_seed_and_id(42, 0);
|
||||
let mut b = EntityRng::from_seed_and_id(42, 1);
|
||||
let va: u32 = a.rng.random();
|
||||
let vb: u32 = b.rng.random();
|
||||
assert_ne!(va, vb);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn entity_rng_adjacent_seeds_no_collision() {
|
||||
// Verify (seed=0, id=N) != (seed=1, id=N-1) — the collision class
|
||||
// that wrapping_add would create.
|
||||
let mut a = EntityRng::from_seed_and_id(0, 5);
|
||||
let mut b = EntityRng::from_seed_and_id(1, 4);
|
||||
let va: u32 = a.rng.random();
|
||||
let vb: u32 = b.rng.random();
|
||||
assert_ne!(va, vb);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user