第2章24 疑似乱数で任意の範囲の整数を表示しよう@イチからゲーム作りで覚えるC言語

イチからゲーム作りで覚えるC言語
連載ページ一覧へ
2章25 疑似乱数のシードを時間にする
NEXT : 2章25 疑似乱数のシードを時間にする
2章23 疑似乱数を使おう : PREV

この記事でやること

前回はプロジェクト「0190_randomize」を新しく作り、疑似乱数についてお話ししました。
今回は、新しくプロジェクト「0195_generate_rand_with_range」を作り、前回からの続きで任意の幅で疑似乱数を生成する方法についてお話します。疑似乱数は、ランダムに生成する地形や、出現する敵など、いろいろなゲーム要素で使われる重要な概念です。

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

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

実際にプログラムを動かして結果を見てみましょう。プロジェクト「0195_generate_rand_with_range」を新しく作成し、ソースコード「RangedRandomize.c」を作成してみてください。

#include <stdio.h>
#include <stdlib.h>
int main() {
 srand(12345);
 printf("〇か×かを乱数で表示:");
 for (int i = 0; i < 10; i++) {
   int kisuuGuusuu = rand() % 2;
   if (kisuuGuusuu == 0) {
     printf("〇");
   } else {
     printf("×");
   }
 }
 printf("\n");
 printf("与えるダメージを乱数で表示:");
 int baseDamage = 10; // 必ず与える基礎ダメージ
 int habaDamage = 4; // 与えるダメージの幅
 for (int j = 0; j < 5; j++) {
   int damage = rand() % ( habaDamage + 1 ) + baseDamage;
   printf("%d, ", damage);
 }
 printf("\n");
}

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

〇か×かを乱数で表示:〇〇××〇×××〇〇
与えるダメージを乱数で表示:14, 10, 13, 12, 14,

〇か×かをそれぞれ確率50%で表示しています。(実際には rand() 関数のなかで使われている疑似乱数生成法によって偏りがあったりします。)
またダメージを10~14の幅でランダムに生成して表示しています。
実行するパソコンの環境によっては、違う結果が出てくるかもしれません。

指定の幅の数を乱数で生成する

前回お話した、乱数を生成する rand() 関数ですが、0 ~ RAND_MAX の間の整数を生成する機能を持っていました。
実際にゲームなどで乱数を使うのであれば、0~10だったり、200~220 など、目的によって指定した範囲の中で乱数を生成してほしいですよね。

0か1の乱数を生成する

rand() 関数を使って 0 か 1 の 2パターンを生成するには、下の計算で実現できます。

rand() % 2

ランダムに生成される rand() を 2 で割った余りですね。2で割った余りの結果は 0 か 1 のみとなるため、 0 か 1 の 2 パターンを生成することができました。

10~30 の範囲で乱数生成する

例えば 10から30の範囲で乱数を生成したい時は、さきほどの 0 か 1 の乱数を生成する式を少し応用してあげることで実現できます。

rand() % 21

という式で、まず 0~20 の範囲で乱数を取り出すことができますね。
これに対して、10 を足すと下の式になり、
0~20の範囲は 0+10 ~ 20+10  となります。

rand() % 21 + 10

結果として、20 ~ 30 の範囲が生成できました。もうすこし式を汎用的にしてみると、

rand() % ( 生成したい数値の幅 + 1 ) + 生成したい下限値

となります。

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

今回のソースコードの中を追ってみましょう。2行目ですが、rand関数、srand関数を使うためにはソースコードの頭に #include <stdlib.h> を書く必要があります。

#include <stdlib.h>

これで rand 関数とsrand 関数がつかえるようになりました。
4行目で srand 関数を使用しています。
前のページでお話しましたが、srand 関数は乱数の種を設定する関数です。設定した値に従って乱数の系列を生成します。
今回は適当に乱数の種は「12345」で設定しました。

 srand(12345);

〇×の2通りをランダムで表示してみる

次に、for 文と if~else文、rand文を組み合わせて、〇×をランダムで表示している箇所を見ていきましょう。

 printf("〇か×かを乱数で表示:");
 for (int i = 0; i < 10; i++) {
   int kisuuGuusuu = rand() % 2;
   if (kisuuGuusuu == 0) {
     printf("〇");
   } else {
     printf("×");
   }
 }
 printf("\n");

5行目で「〇か×かを乱数で表示:」という文字を表示しています。右側に改行は入っていません。

6行目~13行目は for 文でのループ処理となっています。
for ( int i = 0 ; i < 10 ; i++ ) {…} ですので、 i が 0~9の間繰り返し処理を実行する(つまり合計 10 回実行される)ことになります。

7行目で、整数 kisuuGuusuu に 「rand() % 2」 の結果を代入しています。
「rand() % 2 」は「rand() で生成された乱数を、2で割った余り」ですので、答えは 0 か 1 になりすね。kisuuGuusuu に 0 か 1 が代入されることになります。

8行目~12行目は if ~ else 文です。こちらのページでお話した、条件によって違う処理をさせたい時に使う文を使っています。
9行目は、「もし kisuuGuusuu が 0」のときのみ実行されます。
11行目は、「もし kisuuGuusuu が 0 でないとき」のみ実行されます。

このランダムに〇か×を表示する処理を 10 回ほど繰り返し、for 文が完了します。
14行目の printf(“\n”); で改行され、見た目をわかりやすく区切っています。

10~14の整数をランダムで表示してみる

ソースコードの続きで、今度は指定した範囲の整数をランダムで生成しています。

 printf("与えるダメージを乱数で表示:");
 int baseDamage = 10; // 必ず与える基礎ダメージ
 int habaDamage = 4; // 与えるダメージの幅
 for (int j = 0; j < 5; j++) {
   int damage = rand() % ( habaDamage + 1 ) + baseDamage;
   printf("%d, ", damage);
 }
 printf("\n");

15行目は「与えるダメージを乱数で表示:」を改行なしで表示しています。

16~17行目では変数を宣言&初期化しています。

18~21行目では for 文で繰り返し処理を行っています。
「for ( int j = 0 ; j < 5 ; j++ ) {…}」ですので、j が 0 から 4 の間、繰り返し処理を行うことになりますね。

19行目では下のような式になっています。

damage = rand() % ( habaDamage + 1 ) + baseDamage;

右辺の「rand() % ( habaDamage + 1 )」は「rand() % 5」と置き換えられます。乱数を 5 で割った余りですので、答えは 0~4  の乱数となります。
「0~4の乱数 + baseDamage」は「10~14の乱数」であり、変数 damage に結果が代入されます。

20行目では乱数の結果を表示しています。
「printf(“%d, “, damage);」で 「damage の中身」+「,」カンマ を表示して、一つ一つのダメージがわかりやすいようにしています。

for 文が終了した後、22行目で改行して結果を読みやすくしています。

あとがき

今回はゲームで非常によく使われる rand 関数の使い方をお話しました。実際にゲーム内で使うときは、ゲームの環境によって乱数の結果が変わらないように、自分で乱数を生成するプログラムを書いてしまうことが多いと思います。
(乱数で生成されるマップデータなどを考えた時、別のPC上にデータを持って行って遊ぼうとしたらまったく違うマップになってしまっては困るなどの場合があります。)

イチからゲーム作りで覚えるC言語
連載ページ一覧へ
2章25 疑似乱数のシードを時間にする
NEXT : 2章25 疑似乱数のシードを時間にする
2章23 疑似乱数を使おう : PREV

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

ページの更新履歴

更新日 更新内容
更新なし