授業/C言語基礎/数当てゲーム のバックアップ差分(No.4)


  • 追加された行はこの色です。
  • 削除された行はこの色です。
*数当てゲーム [#j1e784ae]

これまでに勉強したことを使って、次のような簡単な数当てゲームを作ります。
  コンピューターがある数を一つ決め、プレイヤーがその数を当てます。
  コンピューターは、プレイヤーが答えた数が正解よりも大きいか小さいかだけを教えます。


*実行結果をイメージする [#c224395d]

プログラムを作るときは、まず、プログラムの実行結果をイメージします。

このプログラムを実行すると、まずはじめに、コンピューターが正解の数を決めます。
とりあえず、正解の数を33として話を進めます。

次に、コンピューターが、プレイヤーに数の入力を要求します。
入力を要求するメッセージは次のような感じにしましょう。
 整数を入力してください:

ここで、プレイヤーが99と入力したとすると、99は正解の33より大きいので、「大きすぎる」というメッセージを出力し、もう一度、プレイヤーに入力を要求するメッセージを表示します。
 整数を入力してください:
 99
 大きすぎます
 整数を入力してください:


今度は、プレイヤーが11と入力したとすると、11は正解の33より小さいので、「小さすぎる」というメッセージを出力し、もう一度、プレイヤーに入力を要求するメッセージを表示します。
 整数を入力してください:
 99
 大きすぎます
 整数を入力してください:
 11
 小さすぎます
 整数を入力してください:


プレイヤーが正解を入力すると、「正解」というメッセージを出力し、プログラムを終了します。
 整数を入力してください:
 99
 大きすぎます
 整数を入力してください:
 11
 小さすぎます
 整数を入力してください:
 33
 正解!


このように、実行結果をイメージすることで、プログラムの中で、どんな処理が行われるのか、どんな処理が繰り返されるのかが、ある程度わかります。



*フローチャートを作る [#e134d110]

次に、イメージした実行結果になるように、プログラムのフローチャート(またはアルゴリズム)を考えます。

このプログラムは、二つの部分に分けることができます。
+コンピューターがある数(正解)を一つ決める
+プレイヤーが答える

最初の、コンピューターがある数(正解)を一つ決めるところは、[[計算ゲーム>授業/C言語基礎/計算ゲーム]]と同じように、乱数を使えばできます。
乱数の使い方がわからない場合は、自分で正解の値を決めてもかまいません。

その次の、プレイヤーが答えるところは、これまでに勉強した制御構造(条件分岐と繰り返し)を組み合わせて作ります。

この部分の中心は、ユーザーが答えて、それが正解よりも大きいか小さいかを教えるというものですから、キーボードからの入力と条件分岐を組み合わせると、次のようになります。
+scanf関数でキーボードから入力する
+if文で入力された数と正解を比べる
--入力された数が正解よりも大きいならば、「大きすぎる」と表示する
--入力された数が正解よりも小さいならば、「小さすぎる」と表示する

これを、正解が入力されるまで繰り返します。

この流れを、そのままフローチャートにすると、次のようになります。
#ref(./number1.png,50%)

ところが、これだと、繰り返しの部分がC言語の繰り返し処理のフローチャートと対応していないので、プログラムとして書きにくいです。

そこで、繰り返しの文をC言語の繰り返し処理のフローチャートに対応するように、フローチャートを書き直します。
#ref(./number2.png,50%);




*プログラムを作る [#k81d93b9]

フローチャートができたら、プログラムを作ります。

乱数を使って正解の数を決める部分だけを作ると、次のようになります。
#geshi(c){{
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
  int r;

  srand((unsigned int) time(NULL));
  r = rand() % 100 + 1;

  return 0;
}
}}
ここでは、正解の数は、1から100までの整数としています。
rand関数、srand関数、time関数の使い方については、[[計算ゲーム>授業/C言語基礎/計算ゲーム]]を復習しましょう。
(ここで、一度コンパイルして、エラーがないか確認します。)

次に、その後の繰り返し処理を形だけ作ります。
#geshi(c){{
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
  int r;

  srand((unsigned int) time(NULL));
  r = rand() % 100 + 1;

  while (    ) {
  while (1) {


  }
  printf("正解!\n");

  return 0;
}
}}
ただし、これだとコンパイル時にwhile文の繰り返し条件でエラーになりますので、実際には、条件を 1 にしておくといいです。(忘れないように注意が必要です。)
while文の繰り返し条件が1になっているのは、コンパイル時にエラーにならないようにするためです。
条件は後で作り直します。
忘れないように注意しましょう。
(ここで、もう一度コンパイルして、エラーがないか確認します。)

while文の形ができたら、先に、繰り返し処理の中を考えましょう。
繰り返し処理の中だけを考えると、次の3つの処理が必要になります。
+プレイヤーに入力を要求するprintf関数
+プレイヤーの答をキーボードから入力するscanf関数
+大きいか小さいかを判定するif文

条件を考えるのは後にして、先に、繰り返し処理の中を考えましょう。
繰り返し処理の中だけを考えると、次のようになります。
したがって、繰り返し処理の中は、次のようになります。
#geshi(c){{
  printf("1から100までの整数を入力してください:\n");
  scanf("%d", &a);
  if (a > r) {
    printf("大きすぎます\n");
  } else if (a < r) {
    printf("小さすぎます\n");
  }
}}
ここで、a はプレイヤーが入力した数を記憶しておくための変数です。

そこで、これをwhile文の波括弧 { } の中にいれて、新しく登場した変数 a の宣言を追加します。
#geshi(c){{
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
  int r, a;

  srand((unsigned int) time(NULL));
  r = rand() % 100 + 1;

  while (    ) {
  while (1) {
    printf("1から100までの整数を入力してください:\n");
    scanf("%d", &a);
    if (a > r) {
      printf("大きすぎます\n");
    } else if (a < r) {
      printf("小さすぎます\n");
    }
  }
  printf("正解!\n");

  return 0;
}
}}
(ここで、もう一度コンパイルして、エラーがないか確認します。)

正解でない間、繰り返すので、繰り返し条件は「a と r が等しくない」という式になります。
また、最初は、繰り返し条件を必ず満たすようにしなければならないので、a の値を1から100まででない値に初期化します。
#geshi(c){{
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
  int r, a;

  srand((unsigned int) time(NULL));
  r = rand() % 100 + 1;

  a = -1;
  while (a != r) {
    printf("1から100までの整数を入力してください:\n");
    scanf("%d", &a);
    if (a > r) {
      printf("大きすぎます\n");
    } else if (a < r) {
      printf("小さすぎます\n");
    }
  }
  printf("正解!\n");

  return 0;
}
}}
これで完成です(プログラム1)。

プログラムができたら、コンパイルして、エラーを直します。
途中で何度かコンパイルしてそのたびにエラーを直しておくと、一度にたくさんのエラーがでないのでエラーを直しやすいです。

*演習1 [#sd1ca6c5]

**演習1 [#sd1ca6c5]

プログラム1を作成し、実行結果を確認せよ。


*テスト [#s201611e]

プログラムが完成したら、何度か実行して設計図(フローチャート)と同じ動きをするかどうかを確認します。
これを''テスト''といいます。

テストのやり方はいくつかありますが、その中に、ブラックボックス・テストとホワイトボックス・テストがあります。

''ブラックボックス・テスト''は、プログラムの中身をわからないものと考えて、仕様に決められたとおりの動きをするかどうかを確認します。

まず初めに、コンピューターがきちんと問題を生成できているかどうかを確認します。
そのため、乱数を生成した後にprintf関数を置いて、生成された数を表示して確認します。
#geshi(c){{
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
  int r, a;

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

  a = -1;
  while (a != r) {
    printf("1から100までの整数を入力してください:\n");
    scanf("%d", &a);
    if (a > r) {
      printf("大きすぎます\n");
    } else if (a < r) {
      printf("小さすぎます\n");
    }
  }
  printf("正解!\n");

  return 0;
}
}}
printf関数の直後に、プログラムの実行を終了する''exit関数''を置いています。
これは、効率良くテストを行うためです。

これを何度か実行し、生成される数が1から100までに収まっていることを確認します。

コンピューターがきちんと問題を生成できていることを確認できたら、次に、プレイヤーの答を正しく判定できるかどうかを確認します。

そのため、正解を真ん中くらいの適当な数に固定します。
ここでは、実行結果をイメージするときに使った33とします。
#geshi(c){{
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
  int r, a;

  srand((unsigned int) time(NULL));
  r = rand() % 100 + 1;
  r = 33;

  a = -1;
  while (a != r) {
    printf("1から100までの整数を入力してください:\n");
    scanf("%d", &a);
    if (a > r) {
      printf("大きすぎます\n");
    } else if (a < r) {
      printf("小さすぎます\n");
    }
  }
  printf("正解!\n");

  return 0;
}
}}
問題作成の確認用に追加したprintf関数とexit関数を削除し、その代わりに、問題を33に固定する命令文を追加します。

ブラックボックス・テストを行うときにテスト・ケースを作成する方法として、''同値分割法''と''境界値分析''があります。

''同値分割法''では、入力データを仕様に基づいてグループに分割します。

ここでは、正解よりも大きい数、正解よりも小さい数、正解と同じ数という3つグループが考えられます。
そこで、正解よりも大きい数の代表として99、正解よりも小さい数の代表として11、正解と同じ数(の代表)として33を選び、これらをテスト・ケースとします。

テスト・ケースを選んだら、テスト・ケースを順番に入力し、実行結果を確認します。

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