From c4032ee0243f02c5c7391383fa2e550c256d0d40 Mon Sep 17 00:00:00 2001 From: Joshua Barretto Date: Thu, 5 Jan 2023 21:31:20 +0000 Subject: [PATCH] Parallelised rtsim NPC AI --- Cargo.lock | 1 + rtsim/Cargo.toml | 1 + rtsim/src/gen/mod.rs | 4 +- rtsim/src/rule/npc_ai.rs | 154 ++++++++++++++++++++------------------- 4 files changed, 85 insertions(+), 75 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 25bb316998..7b04dd4df1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6952,6 +6952,7 @@ dependencies = [ "hashbrown 0.12.3", "itertools", "rand 0.8.5", + "rayon", "rmp-serde", "ron 0.8.0", "serde", diff --git a/rtsim/Cargo.toml b/rtsim/Cargo.toml index 321558b7b3..fa2fd17800 100644 --- a/rtsim/Cargo.toml +++ b/rtsim/Cargo.toml @@ -19,3 +19,4 @@ slotmap = { version = "1.0.6", features = ["serde"] } rand = { version = "0.8", features = ["small_rng"] } fxhash = "0.2.1" itertools = "0.10.3" +rayon = "1.5" diff --git a/rtsim/src/gen/mod.rs b/rtsim/src/gen/mod.rs index e54a396fee..a62f9c9ec6 100644 --- a/rtsim/src/gen/mod.rs +++ b/rtsim/src/gen/mod.rs @@ -89,7 +89,7 @@ impl Data { .with_z(world.sim().get_alt_approx(wpos2d).unwrap_or(0.0)) }; if good_or_evil { - for _ in 0..32 { + for _ in 0..250 { this.npcs.create( Npc::new(rng.gen(), rand_wpos(&mut rng)) .with_faction(site.faction) @@ -107,7 +107,7 @@ impl Data { ); } } else { - for _ in 0..5 { + for _ in 0..15 { this.npcs.create( Npc::new(rng.gen(), rand_wpos(&mut rng)) .with_faction(site.faction) diff --git a/rtsim/src/rule/npc_ai.rs b/rtsim/src/rule/npc_ai.rs index 9fd1e5d2c3..1e64132a47 100644 --- a/rtsim/src/rule/npc_ai.rs +++ b/rtsim/src/rule/npc_ai.rs @@ -21,6 +21,7 @@ use common::{ use fxhash::FxHasher64; use itertools::Itertools; use rand::prelude::*; +use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; use std::{ any::{Any, TypeId}, marker::PhantomData, @@ -230,84 +231,91 @@ const MAX_STEP: f32 = 32.0; impl Rule for NpcAi { fn start(rtstate: &mut RtState) -> Result { rtstate.bind::(|mut ctx| { - let npc_ids = ctx.state.data().npcs.keys().collect::>(); - - for npc_id in npc_ids { - let mut brain = ctx.state.data_mut().npcs[npc_id] - .brain - .take() - .unwrap_or_else(|| Brain { - action: Box::new(think().repeat()), - }); - - let controller = { - let data = &*ctx.state.data(); - let npc = &data.npcs[npc_id]; - - let mut controller = Controller { goto: npc.goto }; - - brain.action.tick(&mut NpcCtx { - state: ctx.state, - world: ctx.world, - index: ctx.index, - time_of_day: ctx.event.time_of_day, - time: ctx.event.time, - npc, - npc_id, - controller: &mut controller, - }); - - /* - let action: ControlFlow<()> = try { - brain.tick(&mut NpcData { - ctx: &ctx, - npc, - npc_id, - controller: &mut controller, + let mut npc_data = { + let mut data = ctx.state.data_mut(); + data.npcs + .iter_mut() + .map(|(npc_id, npc)| { + let controller = Controller { goto: npc.goto }; + let brain = npc.brain.take().unwrap_or_else(|| Brain { + action: Box::new(think().repeat()), }); - /* - // // Choose a random plaza in the npcs home site (which should be the - // // current here) to go to. - let task = - generate(move |(_, npc, ctx): &(NpcId, &Npc, &EventCtx<_, _>)| { - let data = ctx.state.data(); - let site2 = - npc.home.and_then(|home| data.sites.get(home)).and_then( - |home| match &ctx.index.sites.get(home.world_site?).kind - { - SiteKind::Refactor(site2) - | SiteKind::CliffTown(site2) - | SiteKind::DesertCity(site2) => Some(site2), - _ => None, - }, - ); + (npc_id, controller, brain) + }) + .collect::>() + }; - let wpos = site2 - .and_then(|site2| { - let plaza = &site2.plots - [site2.plazas().choose(&mut thread_rng())?]; - Some(site2.tile_center_wpos(plaza.root_tile()).as_()) - }) - .unwrap_or(npc.wpos.xy()); + { + let data = &*ctx.state.data(); - TravelTo { - wpos, - use_paths: true, - } - }) - .repeat(); + npc_data + .par_iter_mut() + .for_each(|(npc_id, controller, brain)| { + let npc = &data.npcs[*npc_id]; - task_state.perform(task, &(npc_id, &*npc, &ctx), &mut controller)?; - */ - }; - */ - - controller - }; - - ctx.state.data_mut().npcs[npc_id].goto = controller.goto; - ctx.state.data_mut().npcs[npc_id].brain = Some(brain); + brain.action.tick(&mut NpcCtx { + state: ctx.state, + world: ctx.world, + index: ctx.index, + time_of_day: ctx.event.time_of_day, + time: ctx.event.time, + npc, + npc_id: *npc_id, + controller, + }); + }); } + + let mut data = ctx.state.data_mut(); + for (npc_id, controller, brain) in npc_data { + data.npcs[npc_id].goto = controller.goto; + data.npcs[npc_id].brain = Some(brain); + } + + /* + let action: ControlFlow<()> = try { + brain.tick(&mut NpcData { + ctx: &ctx, + npc, + npc_id, + controller: &mut controller, + }); + /* + // // Choose a random plaza in the npcs home site (which should be the + // // current here) to go to. + let task = + generate(move |(_, npc, ctx): &(NpcId, &Npc, &EventCtx<_, _>)| { + let data = ctx.state.data(); + let site2 = + npc.home.and_then(|home| data.sites.get(home)).and_then( + |home| match &ctx.index.sites.get(home.world_site?).kind + { + SiteKind::Refactor(site2) + | SiteKind::CliffTown(site2) + | SiteKind::DesertCity(site2) => Some(site2), + _ => None, + }, + ); + + let wpos = site2 + .and_then(|site2| { + let plaza = &site2.plots + [site2.plazas().choose(&mut thread_rng())?]; + Some(site2.tile_center_wpos(plaza.root_tile()).as_()) + }) + .unwrap_or(npc.wpos.xy()); + + TravelTo { + wpos, + use_paths: true, + } + }) + .repeat(); + + task_state.perform(task, &(npc_id, &*npc, &ctx), &mut controller)?; + */ + }; + */ }); Ok(Self)