The completion of the world-model thread: N actors co-driving one generative world converge on the identical byte-exact world + session hash — the generative analog of the multiplayer-worlds convergence, and the thing the float world-model field (streaming non-reproducible pixels) cannot offer. The convergence machinery already existed: an action is a signed CausalOp, the op log replays under canonical total order, so a MERGED multi-actor log replays the same on every peer regardless of arrival order. Rendering that merged log through the integer world model yields the same frames everywhere. - int_world.rs: step_delta() / decode_deltas() — the transition driven by raw (dx,dy) deltas, so a merged multi-actor action log (from world_model::replay) renders without an action-index detour. step()/decode() now wrap these. - tests/int_world_shared.rs (never-skip, gated all(world_model, world_int)): composes world_model::replay ∘ int_world::decode_deltas — two actors interleave signed action ops; a peer that received them in a different order renders the IDENTICAL world (same session hash c61a6595…, same clip hash b7039d73a08de906, same frame bytes). No tolerance, byte-equal. - wai-web: WorldModel::stepDelta, signWorldAction (sign one action op → bytes), replayWorldOps (parse a merged CausalOp log → convergent session hash + canonical delta sequence). The shared session decodes the same in every tab. Live (two BroadcastChannel tabs, telepresence-style): each tab is an actor, each keystroke a signed action op broadcast to the other; both replay the merged log and converge on the same session hash + byte-exact world. Co-Authored-By: Claude Opus 4.8 <[email protected]>
76 lines
3.9 KiB
Rust
76 lines
3.9 KiB
Rust
//! `wai.world.model` SHARED / multi-agent — **convergence on a generated world**.
|
||
//! Two actors interleave signed action ops over one world; the merged log replays
|
||
//! (canonical total order, ANY arrival) to one session hash, and the integer world
|
||
//! model renders that to one BYTE-EXACT frame sequence. So N participants driving a
|
||
//! *generative* interactive world converge on the identical world + hash — the
|
||
//! generative analog of the multiplayer-worlds convergence, the thing the float
|
||
//! world-model field (streaming non-reproducible pixels) cannot offer. Composes
|
||
//! `world_model::replay` ∘ `int_world::decode_deltas`. Never skips (committed model).
|
||
#![cfg(all(feature = "world_model", feature = "world_int"))]
|
||
|
||
use std::path::PathBuf;
|
||
|
||
use ed25519_dalek::SigningKey;
|
||
use wai::int_hyper_synth::IntModel;
|
||
use wai::int_world::decode_deltas;
|
||
use wai::world::CausalOp;
|
||
use wai::world_model::{hex32, replay, sign_action, ModelHeader};
|
||
|
||
fn key(s: u8) -> SigningKey {
|
||
SigningKey::from_bytes(&[s; 32])
|
||
}
|
||
fn vdir() -> PathBuf {
|
||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/vectors/int_world")
|
||
}
|
||
|
||
fn header() -> ModelHeader {
|
||
ModelHeader { model_id: [7u8; 32], seed: 0x00C0FFEE, width: 64, height: 64, fps: 24 }
|
||
}
|
||
|
||
/// Replay a (possibly out-of-order) multi-actor op set and render the generated
|
||
/// world; returns (session_hash hex, clip_hash hex, per-frame bytes).
|
||
fn run(ops: &[CausalOp], t: &IntModel, gs: &IntModel, z0: &[i64], c: usize, lat: usize) -> (String, String, Vec<Vec<u8>>) {
|
||
let s = replay(&header(), ops, true).expect("replay verifies + orders");
|
||
let deltas: Vec<(i64, i64)> = s.actions.iter().map(|a| (a.dx as i64, a.dy as i64)).collect();
|
||
let clip = decode_deltas(t, gs, z0, &deltas, c, lat);
|
||
(hex32(&s.session_hash), format!("{:016x}", clip.clip_hash()), clip.frames)
|
||
}
|
||
|
||
#[test]
|
||
fn two_actors_converge_on_one_byte_exact_generated_world() {
|
||
let t = IntModel::from_bytes(&std::fs::read(vdir().join("world_T.bin")).unwrap()).unwrap();
|
||
let gs = IntModel::from_bytes(&std::fs::read(vdir().join("world_gs.bin")).unwrap()).unwrap();
|
||
let j: serde_json::Value = serde_json::from_slice(&std::fs::read(vdir().join("world.json")).unwrap()).unwrap();
|
||
let c = j["c"].as_u64().unwrap() as usize;
|
||
let lat = j["lat"].as_u64().unwrap() as usize;
|
||
let z0: Vec<i64> = j["z0"].as_array().unwrap().iter().map(|v| v.as_i64().unwrap()).collect();
|
||
|
||
// Two actors co-drive one world; their action ops interleave by effect tick.
|
||
let (a, b) = (key(1), key(2));
|
||
let ops = vec![
|
||
sign_action(&a, 1, 1, 1, 0, 0, b""), // A: +x
|
||
sign_action(&b, 1, 1, 0, 1, 0, b""), // B: +y (same tick → serialized by actor)
|
||
sign_action(&a, 2, 2, 1, 0, 0, b""), // A: +x
|
||
sign_action(&b, 2, 3, -1, 0, 0, b""), // B: −x
|
||
sign_action(&a, 3, 4, 0, 1, 0, b""), // A: +y
|
||
];
|
||
|
||
let (sh, ch, frames) = run(&ops, &t, &gs, &z0, c, lat);
|
||
assert_eq!(frames.len(), ops.len() + 1, "one frame per merged action + initial");
|
||
|
||
// CONVERGENCE: a peer that received the ops in a different order renders the
|
||
// identical world — same session hash, same clip hash, same frame bytes. No
|
||
// tolerance, no "eventually": the generated world is byte-equal.
|
||
let mut shuffled = ops.clone();
|
||
shuffled.rotate_left(2);
|
||
shuffled.swap(0, 3);
|
||
let (sh2, ch2, frames2) = run(&shuffled, &t, &gs, &z0, c, lat);
|
||
assert_eq!(sh, sh2, "session hash converges regardless of arrival order");
|
||
assert_eq!(ch, ch2, "the GENERATED world converges byte-exact");
|
||
assert_eq!(frames, frames2, "every generated frame is byte-identical across peers");
|
||
|
||
// Portable pins (deterministic Ed25519 + canonical order + integer render).
|
||
println!("shared world: session {sh} clip {ch}");
|
||
assert_eq!(sh, "c61a6595d23980656d8ff326f3d1f327654f4afb364c233946e0f49fde78c4c0", "shared session hash");
|
||
assert_eq!(ch, "b7039d73a08de906", "shared world clip hash");
|
||
}
|