お久しぶりです。あと、あけましておめでとうございました。
どうも僕は連続更新→停滞→連続更新→停滞→(ryを繰り返してしまうようです。次からは更新→停滞になるように頑張ろうと思います^^
そんなわけで乱数です。ゲーム内でサイコロとかアイテムドロップとかで使う確率の乱数です。といってもゲーム用だから本当の乱数じゃなくて、数式で計算して求める疑似乱数です。
昔は乱数テーブルなんかも使ったりしていたそうですが、今の時代のPCスペックだったらわざわざ乱数テーブル用意する意味もないですし、計算で求めたいところです。そんな疑似乱数を求める計算式と言ったら……
そうだね、線形合同法だね!!!(
線形合同法といえば精度に問題があることで有名です。使い方を間違えると、サイコロを振ると奇数と偶数が交互にでるようになったりします。いわゆるカルドなんとかサーガ現象ですね!
計算式がすごく簡単で実装も楽なので使っちゃいたい気分ですね。一応、細かく手を加えてあげると少しまともになるみたいですが、手を加えたら簡単じゃないので使いたくないですね(
なんかいいものはないものか……
そうだね、メルセンヌ・ツイスタだね!!!
メルセンヌ・ツイスタはもう精度が神です。説明のページによると、2^19937-1の周期を持って、623次元超立方体の中で均等に分布するらしいです。623次元超立方体とかカッコよすぎです。
しかもライセンスがMITライセンス。ライセンス表示すれば自作プログラムに組み込んでOK!商用だってOK!というわけで、メルセンヌ・ツイスタを使わない理由なんてないですね。
というわけで、メルセンヌ・ツイスタを(僕が)使いやすくするために、ちょっといじって組み込んでみる。
class Random { private: static const unsigned int N = 624; static const unsigned int M = 397; static const unsigned long MATRIX_A = 0x9908b0dfUL; static const unsigned long UPPER_MASK = 0x80000000UL; static const unsigned long LOWER_MASK = 0x7fffffffUL; unsigned long mt[N]; int mti; public: Random( // 本家の初期化処理をする unsigned long seed ); ~Random( ); unsigned long uRand( // 本家の生成処理をする ); long rand( ){return (long)(uRand()>>1);}; long rand( unsigned long max ){return rand()%max;}; unsigned long uRand( unsigned long max ){return uRand()%max;}; };
実装の部分は元のやつのままなので、ヘッダファイルだけ。範囲付き乱数の取得(0〜10とか)をやるために剰余しちゃってるので精度がだいぶ落ちると思いますがゲームなのでそこまで気にしないことにします。
クラス化すると何がいいかって、対戦ものとか作るときに各プレイヤーごとに乱数オブジェクト持たせられることですね。格ゲーとかだと別に各自で持つ必要はないですが、落ちゲーなんかだと、どのプレイヤーも同じようにブロック降ってこないと不公平です。その分メモリは食べちゃいますが、最近のPCはスペックがすごいらしいので大丈夫です多分。
あとはXorshiftっていうのがあります。これは精度が結構あってなおかつ計算が早くて実装も簡単っていうゲームに使うにはすごく便利な子です。
一部で「XorshiftはライセンスがGPL」っていう噂がありますが、論文が載ってる元のページにはソースコード(Code)は無くて、論文(Paper)だけなので多分一番下に書いてあるクリエイティブコモンズでいいんじゃないかなと思うんですが、英語が苦手で確証が持てないのでRuたんは使えないでいます……大丈夫なんだよね?*1 神書籍「ゲームプログラマになる前に覚えておきたい技術」*2にも載ってたし……うん……
つまり、今回のお話は「英語の勉強をしよう」というお話でしたとさ。おしまい。