rand_xorshift crate を使う
Rust の rand クレートはゲームなどでは遅く、他の疑似乱数生成器を使います。
rand_xorshift は Xorshift を実装しており、生成速度や周期の点で大抵のゲームでは十分です。
最近は rand_pcg など PCG も人気のようです、使い方は同じです。
クレート依存性を記述しておきます。
# Cargo.toml [dependencies] rand = "0.7" rand_xorshift = "0.2"
rand::Rng トレイトのメソッドを使いたいときには明示的に use してください。
use rand_xorshift; use rand::{Rng, SeedableRng}; fn main() { // ゲームなどでは時刻などで初期化することが多い // デバッグなどでシードを固定すると楽になる let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(1234567890); // use rand::Rng してあるので gen_range が呼べる let mut rand: i32 = rng.gen_range(0, 100); }
攻撃時の命中判定を行うには、Rng::gen_bool メソッドが便利です。
// 80% 確率で命中 if rand.gen_bool(0.8) { // 命中 }
gen_bool はベルヌーイ分布を生成して利用しています[1]。同じ確率判定を繰り返して使うには、分布を保持しておくと分布を毎回生成するリソース分だけ節約できます。
fn by_sample() { let player_attack_prob = 0.95; let player_attack_distr = rand::distributions::Bernoulli::new(player_attack_prob).unwrap(); let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(123456); let mut hit = 0; for _ in 0..1_000_000_000 { if rng.sample(player_attack_distr) { // 又は // if player_attack_distr.sample(rng) { hit += 1; } } println!("{}", hit + 100); } // イテレータにして利用する方法、シンプルに見える fn by_sample_iter() { let player_attack_prob = 0.95; let player_attack_distr = rand::distributions::Bernoulli::new(player_attack_prob).unwrap(); let rng = rand_xorshift::XorShiftRng::seed_from_u64(123456); // sample_iter は rng と player_attack_distr が move let mut player_attack_prob_iter = rng.sample_iter(player_attack_distr); let mut hit = 0; for _ in 0..1000000000 { if player_attack_prob_iter.next().unwrap() { hit += 1; } } println!("{}", hit + 100); }
イテレータを生成する sample_iter メソッドは乱数生成器と分布を take するので、乱数生成器を他でも使いまわす場合にはこの方法が利用できません。
イテレータにしても10億回程度の実行回数では誤差程度の時間しか変わりません。
Rng::gen_range は以下と同じです[2]。分布を毎回生成する分だけ節約できます。
use rand::distributions::{Distribution, Uniform}; let mut rng = rand_xorshift::XorShiftRng::seed_from_u64(1234567890); let uniform_samples = Uniform::from(0..total_prob); let mut rand: i32 = uniform_samples.sample(&mut rng);