授業/C言語基礎/計算ゲーム のバックアップ(No.4)


これまでに勉強したことを使って、次のような簡単な計算ゲームを作ります。

  1. コンピューターが足し算の問題を出題する
  2. ユーザーがそれに答える
  3. 正解か不正解かをコンピューターが判定する

乱数

無秩序に、かつ、すべて同じ確率で出現するように並べられた数字の列を乱数列といい、乱数列の中から取り出した数を乱数といいます。

ゲームを作るときには、乱数を良く使います。

計算ゲームでは、乱数を使わないと、コンピューターが出題する問題がいつも同じになってしまいます。

rand関数で乱数を生成する

C言語で乱数を生成する関数として、rand関数があります。

rand関数を使うには、stdlib.hという標準のライブラリーをインクルードする必要があります。

#include <stdio.h>

int main(void) {
  int i = 45, j = 76, a;

  printf("%d + %d = ", i, j);
  scanf("%d", &a);

  if (a == i + j) {
    printf("正解!\n");
  } else {
    printf("間違い!\n");
  }

  return 0;
}  

練習1

上のプログラムを作成し、何回か実行して結果を確認せよ。

srand関数で乱数のシードを初期化する

上のプログラムを実行すると、毎回、同じ結果になります。

rand()関数は、毎回同じやり方で擬似乱数を生成するので、スタート地点が同じだと毎回同じ擬似乱数が生成されるのです。

そこで、srand関数を使って擬似乱数を生成するスタート地点(これを乱数のシードといいます)を変更します。

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  int r;

  r = rand();
  printf("%d\n", r);

  return 0;
}

srand関数の()の中の数は、擬似乱数を生成するスタート地点を表していて、これを変えると生成される乱数が変わります。

練習2

上のプログラムを作成し、何回か実行して結果を確認せよ。

また、srand関数の()の中の数を変えてコンパイルし直し、何回か実行して結果を確認せよ。

time関数で時刻を取得して乱数のシードにする

乱数のシードを変更するたびにコンパイルし直すのは面倒なので、現在の時刻を取得して乱数のシードにします。

現在の時刻を取得するには、time関数を使います。 time関数を使うには、time.hという標準のライブラリーをインクルードする必要があります。

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  int r;

  srand(2);
  r = rand();
  printf("%d\n", r);

  return 0;
}

time関数の結果をunsigned int型にキャストしてsrand関数に渡しています。 ちょっとややこしいですが、今はこういうものだと思って進めてください。

練習3

上のプログラムを作成し、何回か実行して結果を確認せよ。

サイコロを作る

サイコロは、1から6までの整数が同じ確率で出ます。

サイコロを、乱数で作るには、次のようにします。

  1. rand関数で乱数を生成する
  2. 生成された値を6で割ったときの余りを求める(0から5)
  3. 最後に、1を加える(1から6)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
  int r;

  srand((unsigned int) time(NULL));
  r = rand();
  printf("%d\n", r);

  return 0;
}

なお、この作り方で作った乱数は、ゲームで使うくらいならあまり問題はありませんが、科学的なシミュレーションなど精密な計算が必要なときには問題があることがわかっていて、そのような場合には他の方法で乱数を作ります。

練習4

上のプログラムを作成し、何回か実行して結果を確認せよ。

計算ゲーム

おまけ:もうすこしマシなサイコロを作る

上の、剰余を使ったサイコロでは、生成された(2進数の)擬似乱数の下位ビットを使って乱数を作っています。 (10進数の擬似乱数に対して0から9までのサイコロを作るために擬似乱数を10で割ったときの余りを使うと、1の位だけを使っていることになるのと同じです。)

ところが、rand関数が生成する擬似乱数の下位ビットは乱数としてあまり良くない(周期性がある)ことがわかっています。 そこで、生成された擬似乱数の上位ビットを使って乱数を作ります。

rand関数が生成する最も小さな数は0、最も大きな数はstdlib.hの中でRAND_MAXとして定義されています。

これを使って1から6までの乱数を生成するには、次のようにします。

  1. 生成した乱数を RAND_MAX + 1.0 で割って0以上1未満の実数(0から0.999999999...)を作る
  2. これを6倍して0以上6未満の実数(0から5.999999999...)にする
  3. その整数部分(0から5)を取り出す
  4. 最後に、1を加える(1から6)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
  int r;

  srand((unsigned int) time(NULL));
  r = rand() % 6 + 1;
  printf("%d\n", r);

  return 0;
}

ちなみに、この作り方でもまだ問題があることがわかっています。 実は、問題はrand関数そのものにあるので、きちんとした乱数が必要なときにはrand関数を使ってはいけません。

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS