mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Use cheap RNG in NPC AI code
This commit is contained in:
parent
2e047f6723
commit
b2627e2690
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -6952,6 +6952,7 @@ dependencies = [
|
|||||||
"hashbrown 0.12.3",
|
"hashbrown 0.12.3",
|
||||||
"itertools",
|
"itertools",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"rand_chacha 0.3.1",
|
||||||
"rayon",
|
"rayon",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"ron 0.8.0",
|
"ron 0.8.0",
|
||||||
|
@ -17,6 +17,7 @@ tracing = "0.1"
|
|||||||
atomic_refcell = "0.1"
|
atomic_refcell = "0.1"
|
||||||
slotmap = { version = "1.0.6", features = ["serde"] }
|
slotmap = { version = "1.0.6", features = ["serde"] }
|
||||||
rand = { version = "0.8", features = ["small_rng"] }
|
rand = { version = "0.8", features = ["small_rng"] }
|
||||||
|
rand_chacha = "0.3"
|
||||||
fxhash = "0.2.1"
|
fxhash = "0.2.1"
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.3"
|
||||||
rayon = "1.5"
|
rayon = "1.5"
|
||||||
|
@ -3,6 +3,7 @@ use crate::{
|
|||||||
RtState,
|
RtState,
|
||||||
};
|
};
|
||||||
use common::resources::{Time, TimeOfDay};
|
use common::resources::{Time, TimeOfDay};
|
||||||
|
use rand_chacha::ChaChaRng;
|
||||||
use std::{any::Any, marker::PhantomData, ops::ControlFlow};
|
use std::{any::Any, marker::PhantomData, ops::ControlFlow};
|
||||||
use world::{IndexRef, World};
|
use world::{IndexRef, World};
|
||||||
|
|
||||||
@ -20,6 +21,8 @@ pub struct NpcCtx<'a> {
|
|||||||
pub npc_id: NpcId,
|
pub npc_id: NpcId,
|
||||||
pub npc: &'a Npc,
|
pub npc: &'a Npc,
|
||||||
pub controller: &'a mut Controller,
|
pub controller: &'a mut Controller,
|
||||||
|
|
||||||
|
pub rng: ChaChaRng,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait that describes 'actions': long-running tasks performed by rtsim
|
/// A trait that describes 'actions': long-running tasks performed by rtsim
|
||||||
|
@ -22,6 +22,7 @@ use common::{
|
|||||||
use fxhash::FxHasher64;
|
use fxhash::FxHasher64;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
|
use rand_chacha::ChaChaRng;
|
||||||
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
use world::{
|
use world::{
|
||||||
@ -245,6 +246,7 @@ impl Rule for NpcAi {
|
|||||||
npc,
|
npc,
|
||||||
npc_id: *npc_id,
|
npc_id: *npc_id,
|
||||||
controller,
|
controller,
|
||||||
|
rng: ChaChaRng::from_seed(thread_rng().gen::<[u8; 32]>()),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -462,17 +464,16 @@ fn timeout(time: f64) -> impl FnMut(&mut NpcCtx) -> bool + Clone + Send + Sync {
|
|||||||
|
|
||||||
fn socialize() -> impl Action {
|
fn socialize() -> impl Action {
|
||||||
now(|ctx| {
|
now(|ctx| {
|
||||||
let mut rng = thread_rng();
|
|
||||||
// TODO: Bit odd, should wait for a while after greeting
|
// TODO: Bit odd, should wait for a while after greeting
|
||||||
if thread_rng().gen_bool(0.0003) && let Some(other) = ctx
|
if ctx.rng.gen_bool(0.0003) && let Some(other) = ctx
|
||||||
.state
|
.state
|
||||||
.data()
|
.data()
|
||||||
.npcs
|
.npcs
|
||||||
.nearby(ctx.npc.wpos.xy(), 8.0)
|
.nearby(ctx.npc.wpos.xy(), 8.0)
|
||||||
.choose(&mut rng)
|
.choose(&mut ctx.rng)
|
||||||
{
|
{
|
||||||
just(move |ctx| ctx.controller.greet(other)).boxed()
|
just(move |ctx| ctx.controller.greet(other)).boxed()
|
||||||
} else if thread_rng().gen_bool(0.0003) {
|
} else if ctx.rng.gen_bool(0.0003) {
|
||||||
just(|ctx| ctx.controller.do_dance())
|
just(|ctx| ctx.controller.do_dance())
|
||||||
.repeat()
|
.repeat()
|
||||||
.stop_if(timeout(6.0))
|
.stop_if(timeout(6.0))
|
||||||
@ -504,7 +505,7 @@ fn adventure() -> impl Action {
|
|||||||
| SiteKind::DesertCity(_)
|
| SiteKind::DesertCity(_)
|
||||||
),
|
),
|
||||||
) && ctx.npc.current_site.map_or(true, |cs| *site_id != cs)
|
) && ctx.npc.current_site.map_or(true, |cs| *site_id != cs)
|
||||||
&& thread_rng().gen_bool(0.25)
|
&& ctx.rng.gen_bool(0.25)
|
||||||
})
|
})
|
||||||
.min_by_key(|(_, site)| site.wpos.as_().distance(ctx.npc.wpos.xy()) as i32)
|
.min_by_key(|(_, site)| site.wpos.as_().distance(ctx.npc.wpos.xy()) as i32)
|
||||||
.map(|(site_id, _)| site_id)
|
.map(|(site_id, _)| site_id)
|
||||||
@ -549,10 +550,10 @@ fn hunt_animals() -> impl Action {
|
|||||||
just(|ctx| ctx.controller.do_hunt_animals()).debug(|| "hunt_animals")
|
just(|ctx| ctx.controller.do_hunt_animals()).debug(|| "hunt_animals")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_forest(ctx: &NpcCtx) -> Option<Vec2<f32>> {
|
fn find_forest(ctx: &mut NpcCtx) -> Option<Vec2<f32>> {
|
||||||
let chunk_pos = ctx.npc.wpos.xy().as_() / TerrainChunkSize::RECT_SIZE.as_();
|
let chunk_pos = ctx.npc.wpos.xy().as_() / TerrainChunkSize::RECT_SIZE.as_();
|
||||||
Spiral2d::new()
|
Spiral2d::new()
|
||||||
.skip(thread_rng().gen_range(1..=8))
|
.skip(ctx.rng.gen_range(1..=8))
|
||||||
.take(49)
|
.take(49)
|
||||||
.map(|rpos| chunk_pos + rpos)
|
.map(|rpos| chunk_pos + rpos)
|
||||||
.find(|cpos| {
|
.find(|cpos| {
|
||||||
@ -604,7 +605,7 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
let house = site2
|
let house = site2
|
||||||
.plots()
|
.plots()
|
||||||
.filter(|p| matches!(p.kind(), PlotKind::House(_)))
|
.filter(|p| matches!(p.kind(), PlotKind::House(_)))
|
||||||
.choose(&mut thread_rng())?;
|
.choose(&mut ctx.rng)?;
|
||||||
Some(site2.tile_center_wpos(house.root_tile()).as_())
|
Some(site2.tile_center_wpos(house.root_tile()).as_())
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@ -623,37 +624,33 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
.debug(|| "find somewhere to sleep"),
|
.debug(|| "find somewhere to sleep"),
|
||||||
);
|
);
|
||||||
// Villagers with roles should perform those roles
|
// Villagers with roles should perform those roles
|
||||||
} else if matches!(ctx.npc.profession, Some(Profession::Herbalist))
|
} else if matches!(ctx.npc.profession, Some(Profession::Herbalist)) && ctx.rng.gen_bool(0.8)
|
||||||
&& thread_rng().gen_bool(0.8)
|
|
||||||
{
|
{
|
||||||
if let Some(forest_wpos) = find_forest(ctx) {
|
if let Some(forest_wpos) = find_forest(ctx) {
|
||||||
return casual(
|
return casual(
|
||||||
travel_to_point(forest_wpos, 0.5)
|
travel_to_point(forest_wpos, 0.5)
|
||||||
.debug(|| "walk to forest")
|
.debug(|| "walk to forest")
|
||||||
.then({
|
.then({
|
||||||
let wait_time = thread_rng().gen_range(10.0..30.0);
|
let wait_time = ctx.rng.gen_range(10.0..30.0);
|
||||||
gather_ingredients().repeat().stop_if(timeout(wait_time))
|
gather_ingredients().repeat().stop_if(timeout(wait_time))
|
||||||
})
|
})
|
||||||
.map(|_| ()),
|
.map(|_| ()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if matches!(ctx.npc.profession, Some(Profession::Hunter))
|
} else if matches!(ctx.npc.profession, Some(Profession::Hunter)) && ctx.rng.gen_bool(0.8) {
|
||||||
&& thread_rng().gen_bool(0.8)
|
|
||||||
{
|
|
||||||
if let Some(forest_wpos) = find_forest(ctx) {
|
if let Some(forest_wpos) = find_forest(ctx) {
|
||||||
return casual(
|
return casual(
|
||||||
just(|ctx| ctx.controller.say("Time to go hunting!"))
|
just(|ctx| ctx.controller.say("Time to go hunting!"))
|
||||||
.then(travel_to_point(forest_wpos, 0.75))
|
.then(travel_to_point(forest_wpos, 0.75))
|
||||||
.debug(|| "walk to forest")
|
.debug(|| "walk to forest")
|
||||||
.then({
|
.then({
|
||||||
let wait_time = thread_rng().gen_range(30.0..60.0);
|
let wait_time = ctx.rng.gen_range(30.0..60.0);
|
||||||
hunt_animals().repeat().stop_if(timeout(wait_time))
|
hunt_animals().repeat().stop_if(timeout(wait_time))
|
||||||
})
|
})
|
||||||
.map(|_| ()),
|
.map(|_| ()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if matches!(ctx.npc.profession, Some(Profession::Merchant))
|
} else if matches!(ctx.npc.profession, Some(Profession::Merchant)) && ctx.rng.gen_bool(0.8)
|
||||||
&& thread_rng().gen_bool(0.8)
|
|
||||||
{
|
{
|
||||||
return casual(
|
return casual(
|
||||||
just(|ctx| {
|
just(|ctx| {
|
||||||
@ -665,7 +662,7 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
"Looking for supplies? I've got you covered.",
|
"Looking for supplies? I've got you covered.",
|
||||||
]
|
]
|
||||||
.iter()
|
.iter()
|
||||||
.choose(&mut thread_rng())
|
.choose(&mut ctx.rng)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
) // Can't fail
|
) // Can't fail
|
||||||
})
|
})
|
||||||
@ -687,7 +684,7 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
.get(visiting_site)
|
.get(visiting_site)
|
||||||
.and_then(|site| ctx.index.sites.get(site.world_site?).site2())
|
.and_then(|site| ctx.index.sites.get(site.world_site?).site2())
|
||||||
.and_then(|site2| {
|
.and_then(|site2| {
|
||||||
let plaza = &site2.plots[site2.plazas().choose(&mut thread_rng())?];
|
let plaza = &site2.plots[site2.plazas().choose(&mut ctx.rng)?];
|
||||||
Some(site2.tile_center_wpos(plaza.root_tile()).as_())
|
Some(site2.tile_center_wpos(plaza.root_tile()).as_())
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@ -696,7 +693,7 @@ fn villager(visiting_site: SiteId) -> impl Action {
|
|||||||
.debug(|| "walk to plaza")
|
.debug(|| "walk to plaza")
|
||||||
// ...then wait for some time before moving on
|
// ...then wait for some time before moving on
|
||||||
.then({
|
.then({
|
||||||
let wait_time = thread_rng().gen_range(30.0..90.0);
|
let wait_time = ctx.rng.gen_range(30.0..90.0);
|
||||||
socialize().repeat().stop_if(timeout(wait_time))
|
socialize().repeat().stop_if(timeout(wait_time))
|
||||||
.debug(|| "wait at plaza")
|
.debug(|| "wait at plaza")
|
||||||
})
|
})
|
||||||
@ -794,7 +791,7 @@ fn pilot() -> impl Action {
|
|||||||
.and_then(|site| ctx.index.sites.get(site).kind.convert_to_meta())
|
.and_then(|site| ctx.index.sites.get(site).kind.convert_to_meta())
|
||||||
.map_or(false, |meta| matches!(meta, SiteKindMeta::Settlement(_)))
|
.map_or(false, |meta| matches!(meta, SiteKindMeta::Settlement(_)))
|
||||||
})
|
})
|
||||||
.choose(&mut thread_rng());
|
.choose(&mut ctx.rng);
|
||||||
if let Some((_id, site)) = site {
|
if let Some((_id, site)) = site {
|
||||||
let start_chunk =
|
let start_chunk =
|
||||||
ctx.npc.wpos.xy().as_::<i32>() / TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
ctx.npc.wpos.xy().as_::<i32>() / TerrainChunkSize::RECT_SIZE.as_::<i32>();
|
||||||
@ -826,7 +823,7 @@ fn captain() -> impl Action {
|
|||||||
.get(*neighbor)
|
.get(*neighbor)
|
||||||
.map_or(false, |c| c.river.river_kind.is_some())
|
.map_or(false, |c| c.river.river_kind.is_some())
|
||||||
})
|
})
|
||||||
.choose(&mut thread_rng())
|
.choose(&mut ctx.rng)
|
||||||
{
|
{
|
||||||
let wpos = TerrainChunkSize::center_wpos(chunk);
|
let wpos = TerrainChunkSize::center_wpos(chunk);
|
||||||
let wpos = wpos.as_().with_z(
|
let wpos = wpos.as_().with_z(
|
||||||
@ -891,7 +888,7 @@ fn bird_large() -> impl Action {
|
|||||||
matches!(ctx.index.sites.get(site).kind, SiteKind::Dungeon(_))
|
matches!(ctx.index.sites.get(site).kind, SiteKind::Dungeon(_))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.choose(&mut thread_rng())
|
.choose(&mut ctx.rng)
|
||||||
{
|
{
|
||||||
casual(goto(
|
casual(goto(
|
||||||
site.wpos.as_::<f32>().with_z(
|
site.wpos.as_::<f32>().with_z(
|
||||||
|
Loading…
Reference in New Issue
Block a user