レトロRPGを作る-乱数を実装する

乱数テーブル

ゲームで乱数を算出する場合、乱数テーブルを使用するのが定番です(パチスロでも使用されていますし)。
よって多くは語りませんが、理由とし上げられるのは主に以下の2点ではないでしょうか。

  1. 値のばらつき具合をコントロール出来る
  2. 環境に依存しない


例えば計算式を使用して乱数を得る場合、発生する乱数の値や偏りはプラットフォームや、時間帯によって変化するかも知れません。
乱数なのだからそれで良いように思うかも知れませんがゲームバランスを調整する上で、それでは困ります。
また、乱数の使いどころは「1/16の確率で○○が発生する」等の当たり判定に使用する事が多いわけですが、確率が1/16なら16回に1回が中央値であって欲しいのが人情でしょう(どうもそのようです)。
そういったゲームバランスが取りやすいのが乱数テーブルです。

と言うことで、まずは乱数テーブルを用意します。

Random.table = [
    7, 182, 240, 31, 85, 91, 55, 227, 174, 79, 178, 94, 153, 246, 119, 203,
    96, 143, 67, 62, 167, 76, 45, 136, 199, 104, 215, 209, 194, 242, 193, 221,
    170, 147, 22, 247, 38, 4, 54, 161, 70, 78, 86, 190, 108, 110, 128, 213,
    181, 142, 164, 158, 231, 202, 206, 33, 255, 15, 212, 140, 230, 211, 152, 71,
    244, 13, 21, 237, 196, 228, 53, 120, 186, 218, 39, 97, 171, 185, 195, 125,
    133, 252, 149, 107, 48, 173, 134, 0, 141, 205, 126, 159, 229, 239, 219, 89,
    235, 5, 20, 201, 36, 44, 160, 60, 68, 105, 64, 113, 100, 58, 116, 124,
    132, 19, 148, 156, 150, 172, 180, 188, 3, 222, 84, 220, 197, 216, 12, 183,
    37, 11, 1, 28, 35, 43, 51, 59, 151, 27, 98, 47, 176, 224, 115, 204,
    2, 74, 254, 155, 163, 109, 25, 56, 117, 189, 102, 135, 63, 175, 243, 251,
    131, 10, 18, 26, 34, 83, 144, 207, 122, 139, 82, 90, 73, 106, 114, 40,
    88, 138, 191, 14, 6, 162, 253, 250, 65, 101, 210, 77, 226, 92, 29, 69,
    30, 9, 17, 179, 95, 41, 121, 57, 46, 42, 81, 217, 93, 166, 234, 49,
    129, 137, 16, 103, 245, 169, 66, 130, 112, 157, 146, 87, 225, 61, 241, 249,
    238, 8, 145, 24, 32, 177, 165, 187, 198, 72, 80, 154, 214, 127, 123, 233,
    118, 223, 50, 111, 52, 168, 208, 184, 99, 200, 192, 236, 75, 232, 23, 248
];

今回用意したのは256のテーブルですが、最近は65536のテーブルを使用する事が多いんじゃないでしょうか。

ポインター

値そのものは現在のポインターが示す値を使用するのですが、ポインターの進め方にいくつかの手法があるようです。
一つ目は、値を参照する毎にポインターを一つ進める方法です。昔の_ではこの方法だった様ですがエンカウント飛ばしなど攻略されてしまうため、今はあまり使われないようです。
二つ目は、画面周期等を使用して常にポインターを進めて置く方法です。
で、ここで提示する方法は次のとおりです…

    Random.pos = Math.floor(Random.pos + Math.random() * 255) % 256;

先に説明した方法の折衷案です。ポインターを一つずつ進める代わりにランダムに進めるようにしています。
この方法にしたのは、周期的にポインターを進める負荷がもったいなかったためです。

値を取り出す

乱数の使い道は、エンカウント、エンカウント時の敵グループの選択やアイテムのドロップ判定あたりでしょうか。
ここはわかり易くアイテムのドロップについて考えてみます。

例えば、1/4の確率でアイテムをドロップする場合、0~255の値の内、0~63の値なら当たりとみなします。

Random.judge(range) {
    var v = Random.table[Random.pos];
    Random.pos = Math.floor(Random.pos + Math.random() * 255) % 256;
    return range.l <= v && v < range.g;
}

if(Random.judge({l: 0, g: 64})) {
    //アイテムドロップ
}

次にドロップするアイテムを選択します。ドロップするアイテムは4種類あり、それぞれ出現する確率が異なるとします。
その場合、256の値の配分によって、それぞれの確率を表現します。
0~127ならA、128~200ならB、201~250ならC、251~255ならDとか。

Random.getValue = function (range) {
    /// 乱数の値を取得します。レンジ配列が指定されていればどの要素に
    /// マッチしたかを返します。

    var v = Random.table[Random.pos];
    Random.pos = Math.floor(Random.pos + Math.random() * 255) % 256;
    if (range == null) {
        //範囲指定がなければ、そのままかえす(256等分)
        return v;
    }
    for (var i = 0; i < range.length; i++) {
        if (range[i].l <= v && v < range[i].g) {
            return i;
        }
    }

    //マッチしない
    return i - 1;
}
var Items = ["A","B","C","D"];
var pos = Random.getValue([
    {l:0, g: 128},
    {l:128, g: 201},
    {l:201, g: 251},
    {l:251, g: 256}]);

//Items[pos]がドロップしたアイテム。

後は、0~3の値を均等に得たい場合でしょうか。この時もRandom.table[Random.pos] % 4の様にしてはいけません。
下位nビットを取得する手法は乱数としては優れた方法ではなくせっかくの乱数テーブルを台無しにしてしまいます。
以下のようにするべきです。

var value = Random.getValue([
    {l:0, g: 64},
    {l:64, g: 128},
    {l:128, g: 192},
    {l:192, g: 256}]);

ソース

dq-random.js

M. K. の紹介

IT屋さんです。プログラミングが大好きで今はJavascriptがお気に入りです。
カテゴリー: dq, JavaScript, ゲーム作成, プログラミング   パーマリンク