//! # Basic simulation //! A really basic simulation to demonstrate //! some features of Djinn: //! //! - how to write a basic simulation //! - how to create a websocket server and publish to it //! - how to publish and listen to events //! - how to register a simulation reporter //! - how to run a simulation extern crate djinn; extern crate redis; extern crate rustc_serialize; use std::thread; use redis::{Client, Commands}; use djinn::{Agent, Manager, Simulation, Population, Updates, Redis, WebSocketServer, run}; const HEALTH_START: usize = 10; const HEALTH_CHANGE: usize = 10; #[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)] pub struct State { health: usize, } #[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)] pub struct World { weather: String, } #[derive(RustcDecodable, RustcEncodable, Debug, PartialEq, Clone)] pub enum Update { ChangeHealth(usize), } #[derive(Clone)] pub struct BasicSim; impl Simulation for BasicSim { type State = State; type Update = Update; type World = World; fn decide(&self, agent: &Agent, world: &Self::World, population: &Population, updates: &mut Updates) -> () { updates.queue(agent.id, Update::ChangeHealth(HEALTH_CHANGE)); } fn update(&self, mut state: &mut Self::State, updates: Vec) -> bool { let updated = updates.len() > 0; for update in updates { match update { Update::ChangeHealth(health) => { state.health += health; } } } updated } } fn main() { let sim = BasicSim {}; let world = World { weather: "sunny".to_string() }; // Setup the manager let addr = "redis://127.0.0.1/"; let client = Client::open(addr).unwrap(); let mut manager = Manager::new(addr, client, sim.clone()); // Spawn the population manager.spawn(State { health: 0 }); let id = manager.spawn(State { health: HEALTH_START }); // Create a websocket server to pass messages to frontend clients let mut ws = WebSocketServer::new("127.0.0.1:3012", addr); ws.start(); // Give the frontend some time to connect thread::sleep_ms(2000); let n_steps = 10; // Create a client to listen to events let log_t = thread::spawn(move || { let client = Client::open(addr).unwrap(); let mut pubsub = client.get_pubsub().unwrap(); pubsub.subscribe("weather").unwrap(); for step in 0..n_steps { let msg = pubsub.get_message().unwrap(); let payload: String = msg.get_payload().unwrap(); println!("[{:02}] This step's weather is {}", step, payload); } }); // Register a really simple reporter manager.register_reporter(1, |step, pop, conn| { let world = pop.world(); let _: () = conn.publish("weather", world.weather.clone()).unwrap(); let _: () = conn.publish("ws", world.weather.clone()).unwrap(); }); manager = run(sim, world, manager, 4, n_steps); log_t.join().unwrap(); // Check that things are working let agent = match manager.population.get_agent(id) { Some(a) => a, None => panic!("Couldn't find the agent"), }; println!("{:?}", agent); assert_eq!(agent.state.health, HEALTH_START + (HEALTH_CHANGE * n_steps)); // Shutdown the websocket server ws.shutdown(); }