第2章23 疑似乱数を使おう@イチからゲーム作りで覚えるC言語

イチからゲーム作りで覚えるC言語
連載ページ一覧へ
2章24 疑似乱数で任意の範囲の整数を表示しよう
NEXT : 2章24 疑似乱数で任意の範囲の整数を表示しよう
2章22 繰り返し処理のネスト構造と break、continue などのジャンプ文 : PREV

この記事でやること

前回はプロジェクト「0180_nested_loop」を新しく作り、繰り返し処理の入れ子(ネスト構造)についてお話ししました。
今回は、新しくプロジェクト「0190_randomize」を作り、疑似乱数についてお話します。疑似乱数は、ランダムに生成する地形や、出現する敵など、いろいろなゲーム要素で使われる重要な概念です。

この記事はゆる~くC言語でコンソールゲームを作りながらプログラミングを学ぶための連載記事です。シリーズものですので記事の一覧はこちらを参照してくださいね。

疑似乱数を行うプログラム

疑似乱数を使ったソースコードを実際にプログラムを動かして結果を見てみましょう。プロジェクト「0190_randomize」を新しく作成し、ソースコード「DispRandom.c」を作成してみてください。

#include <stdio.h>
#include <stdlib.h>
int main() {
 unsigned int randomSeed = 123;
 srand( randomSeed );
 for (int i = 1; i < 4; i++) {
   int gold = rand();
   printf("%d 個目の宝箱の中身は金貨が %d 枚でした。\n", i, gold);
 }
}

このコードを実行してみるとこんな結果になったかと思います。

1 個目の宝箱の中身は金貨が 440 枚でした。
2 個目の宝箱の中身は金貨が 19053 枚でした。
3 個目の宝箱の中身は金貨が 23075 枚でした。

宝箱の中身(金貨)がランダムにとれます。実行するパソコンの環境によっては、違う枚数が出てくるかもしれません。

疑似乱数とは

疑似乱数(pseudo-random numbersとは、完全なランダムではなく何かの規則に従って出力される(一見)ランダムな数値のことです。今回のプログラムも一見ランダムに金貨の枚数が出ているように見えますが、このプログラムをもう一度実行してみた結果を確認してもらうと、まったく同じ順番で同じ数(金貨の枚数)がでます。

疑似乱数をつくる関数やシステムのことを疑似疑似乱数列生成器と呼びます。
様々なゲームで乱数は使われています。挙げたらきりがないですが、例えば、

  • 自動生成されるマップ
  • 適当な距離を歩くとランダムで出現するモンスター
  • カジノゲーム

などです。疑似乱数列生成器は、キーとなる値を指定すると、キーとなる値から計算した同じ乱数の結果(数列)を返してくれるように作られているものが多くあります。このキーとなる値を乱数の種(Random Seed)とよく呼びます。サンドボックス系のゲームではマップのシード値とか呼ばれてたりします。

C言語であらかじめ用意されている疑似乱数の関数を使うには、
#include <stdlib.h> を書いてあげる必要があります。

ソースコードを追ってみよう

今回のプログラムのソースコードを一行づつ追ってみましょう。

int main() {
 unsigned int randomSeed = 123;
 srand( randomSeed );

4行目では、unsigned int 型(負の値をとらない int 型)で変数 randomSeed に 123 を代入しています。
5行目の srand関数乱数の種を設定する関数です。
関数 srand の1つ目の引数(第1引数)に randomSeed を設定して関数を呼び出しています。

srand の s は seed for a new sequence of pseudo-random numbers (疑似乱数が生成する新しい系列のシード値)の略っぽい気がしますが、正解はわからずです。

この乱数の種(今回なら変数 randomSeed )が違う整数なら、違う乱数の数値が違う順序で吐き出されることになります。

6行目~9行目は for 文によるループとなっています。
ループするとき変数 i が 1,2,3,4 と増えていき、4となったとき、ループが終了します。
ですので、3回、for 文の繰り返し処理が実行されることになりますね。

 for (int i = 1; i < 4; i++) {
   int gold = rand();
   printf("%d 個目の宝箱の中身は金貨が %d 枚でした。\n", i, gold);
 }

7 行目は、int型の変数 gold に rand() の結果を代入しています。
この rand() 関数は、疑似乱数を生成して整数を返します。呼び出すたびに、異なる整数が疑似乱数のアルゴリズムによって生み出され、返ってきます。

8 行目で、変数 i と gold の中身を表示しています。

N1570 の仕様によるとsrand 関数を使わずに rand 関数を使った場合、自動的に最初に srand( 1 ) と設定したものと同じ乱数が生成されるようです。

rand() 関数が生成する乱数の幅

rand() 関数は呼び出すたびに、C言語側で用意されたアルゴリズムに従って整数を生成します。この乱数は 0以上、少なくとも 32767 以下の範囲で数値を生成します。
乱数の生成する値の最大は「少なくとも 32767」ということまでは仕様で決まっていますが、実際にはもっと大きな数をかえすようなアルゴリズムが使われている可能性があります。

自身のビルド環境で、rand()が返す最大値を確認するには、

printf("rand()が返す最大の整数 = %d\n", RAND_MAX);&nbsp;

として、RAND_MAX の中身を見ることで表示できます。

自分で乱数生成(次の章で書こう!)

C言語の rand() ではなく、自分で乱数生成を行うプログラムも作ることができます。
自分で作ることにどのようなメリットがあるでしょうか。

例えば、いろいろなスマホやPC、OS上で実行されるプログラムを書いたとき、実行される環境によって、異なる乱数列が生成されると困ることがあります。

「プレイヤーに入力してもらった文字列」をシード値にして、マップを自動生成するようなケースなど、シード値が同じであれば、同じ疑似乱数の結果が得られる、というようなケースでは

あとがき

乱数を使えるようになったことで、いままで同じ結果しか得られなかったアプリが、だいぶゲーム性らしさが出てくるようになると思います。
実行するたびに、異なる結果がでてくるようなプログラムについては次回お話しようと思います。

今回はここまで。おつかれさまでした。

イチからゲーム作りで覚えるC言語
連載ページ一覧へ
2章24 疑似乱数で任意の範囲の整数を表示しよう
NEXT : 2章24 疑似乱数で任意の範囲の整数を表示しよう
2章22 繰り返し処理のネスト構造と break、continue などのジャンプ文 : PREV

非常に参考になったサイトさまや、参考文献など

ページの更新履歴

更新日 更新内容
更新なし