- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- ソース を表示
- 授業/C言語基礎/じゃんけんゲーム へ行く。
これまでに勉強したことを使って、次のような簡単なじゃんけんゲームを作ります。
プレイヤーがコンピューターと勝負がつくまでじゃんけんをする。
実行結果をイメージする †
数当てゲームのところでも書いたように、プログラムを作り始める前に、まず、プログラムの実行結果をイメージします。
じゃんけんでは、グーかチョキかパーを出しますが、プレイヤーが選ぶときには、グーを 1、チョキを 2、パーを 3 として入力させることにしましょう。
プレイヤーが入力した後にコンピューターが自分の手を選ぶのはズルいので、プレイヤーに入力を要求する前にコンピューターが自分の手を選ぶことにしましょう。 とりあえず、コンピューターがグーを選んだとして話を進めます。
プレイヤーに入力を要求するメッセージは、じゃんけんらしく、次のような感じにしましょう。
じゃんけんぽん!(1: グー、2: チョキ、3: パー):
プレイヤーが手を選んで入力すると、プレイヤーが選んだ手とコンピューターが選んだ手を出力します。 ここでは、プレイヤーがパーを選んで 3 を入力したとします。
じゃんけんぽん! (1: グー、2: チョキ、3: パー): 3 あなた: 3、コンピューター: 1(1: グー、2: チョキ、3: パー)
プレイヤーがパーを選んだ場合は、プレイヤーの勝ちなので、「あなたの勝ち」というメッセージを表示し、プログラムを終了します。
じゃんけんぽん! (1: グー、2: チョキ、3: パー): 3 あなた: 3、コンピューター: 1(1: グー、2: チョキ、3: パー) あなたの勝ち!
プレイヤーがチョキを選んで 2 を入力したとすると、プレイヤーの負けなので、「あなたの負け」というメッセージを表示し、プログラムを終了します。
じゃんけんぽん! (1: グー、2: チョキ、3: パー): 2 あなた: 2、コンピューター: 1(1: グー、2: チョキ、3: パー) あなたの負け!
プレイヤーがグーを選んで 1 を入力したとすると、あいこなので、もう一度コンピューターが自分の手を選び、プレイヤーに入力を要求するメッセージを表示します。
じゃんけんぽん! (1: グー、2: チョキ、3: パー): 1 あなた: 1、コンピューター: 1(1: グー、2: チョキ、3: パー) あいこでしょ! (1: グー、2: チョキ、3: パー):
これを、勝負がつくまで繰り返します。
このように、実行結果をイメージすることで、プログラムの中で、どんな処理が行われるのか、どんな処理が繰り返されるのかが、ある程度わかります。
フローチャートを作る †
次に、イメージした実行結果になるように、プログラムのフローチャート(またはアルゴリズム)を考えます。
このプログラムも、数当てゲームと同じように、二つの部分に分けることができます。
- コンピューターが自分の手を選ぶ
- プレイヤーが自分の手を選ぶ
ただし、数当てゲームとは違って、1と2の両方を(勝負がつくまで)繰り返し実行します。
プレイヤーの手はキーボードから入力してもらうので、繰り返し行われる処理は次のようになります。
- 「じゃんけんぽん」と出力する
- rand関数で自分の手を選ぶ
- scanf関数でキーボードから入力する
- 引き分けなら、「あいこでしょ」と出力し、2へ戻る
- そうでないなら、結果を出力し、プログラムを終了する
この流れを、そのままフローチャートにすると、次のようになります(フローチャート1)。
繰り返し条件の前置判定を行う方がwhile文を作りやすいので、これを次のように書き換えます(フローチャート2)。
ただし、こうすると、繰り返し処理の前に重複する処理ができてしまいます。
プログラムを作る †
フローチャートができたら、プログラムを作ります。
無限ループとbreak文の組み合わせでプログラムを作る †
まず初めに、「じゃんけんぽん」と出力して、無限ループを用意します。
#include <stdio.h> int main(void) { printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); while (1) { } return 0; }
無限ループに入ると、乱数を使って自分の手を選びます。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r; srand((unsigned int) time(NULL)); // 乱数のシード printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); while (1) { r = rand() % 3 + 1; } return 0; }
ここでは、1から3までの整数を乱数で生成し、1ならグー、2ならチョキ、3ならパーとします。 rand関数、srand関数、time関数の使い方については、計算ゲームを復習しましょう。
その次に、キーボードから入力する部分を加えます。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r, h; srand((unsigned int) time(NULL)); // 乱数のシード printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); while (1) { r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); } return 0; }
ここで、h はプレイヤーが入力した数を記憶しておくための変数です。 (同時に、新しく登場した変数 a の宣言も追加しています。) また、入力された手とコンピューターが選んだ手を画面に出力しています。
ここで、引き分けでない場合は、無限ループを中断します。 そうでない場合(引き分けの場合)は、「あいこでしょ」と出力して、無限ループの先頭に戻ります。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r, h; srand((unsigned int) time(NULL)); // 乱数のシード printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); while (1) { r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); if (h != r) { break; } printf("あいこでしょ!(1: グー、2: チョキ、3: パー):\n"); } return 0; }
無限ループが中断されたら(勝負がついたら)、結果を判定して、出力します。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r, h; srand((unsigned int) time(NULL)); // 乱数のシード printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); while (1) { r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); if (h != r) { break; } printf("あいこでしょ!(1: グー、2: チョキ、3: パー):\n"); } if (r == 1 && h == 3 || r == 2 && h == 1 || r == 3 && h == 2) { printf("あなたの勝ち!\n"); } else { printf("あなたの負け!\n"); } return 0; }
結果を判定するif文の条件は、勝ちのパターン3通りをすべて列挙して論理和 || でつないでも作ることができます。
これで完成です(プログラム1)。
演習1 †
プログラム1を作成し、実行結果を確認せよ。
前置判定while文でプログラムを作る †
まず初めに、「じゃんけんぽん」と出力して、乱数を使って自分の手を選び、キーボードからプレイヤーの手を入力します。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r, h; srand((unsigned int) time(NULL)); // 乱数のシード printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); return 0; }
次に、繰り返し処理を行う前置判定のwhile文の形を用意します。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r, h; srand((unsigned int) time(NULL)); // 乱数のシード printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); while (h == r) { } return 0; }
ここでは、繰り返し条件を先に考えることができるので、先に入れてあります。 繰り返し条件は、引き分け、つまり、コンピューターの手とプレイヤーの手が等しいことです。
つぎに、繰り返し処理の中だけを考えると、次のようになります。
printf("あいこでしょ!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r);
そこで、これをwhile文の波括弧 { } の中に入れます。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r, h; srand((unsigned int) time(NULL)); // 乱数のシード printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); while (h == r) { printf("あいこでしょ!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); } return 0; }
繰り返しが終了したら(勝負がついたら)、結果を判定して、出力します。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r, h; srand((unsigned int) time(NULL)); // 乱数のシード printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); while (h == r) { printf("あいこでしょ!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); } if (r == 1 && h == 3 || r == 2 && h == 1 || r == 3 && h == 2) { printf("あなたの勝ち!\n"); } else { printf("あなたの負け!\n"); } return 0; }
これで完成です(プログラム2)。
演習2 †
プログラム1をプログラム2に変更し、実行結果を確認せよ。
プログラムをテストする(おまけ) †
数当てゲームと同様に、まず、プログラムをモジュールに分割し、単体テストを行います。
プログラム2は、次の四つのモジュールに分割できます。
- コンピューターが自分の手を選ぶ
- プレイヤーが手を入力する
- あいこのときにもう一度勝負する
- 勝敗を判定する
そこで、まず、コンピューターが自分の手を正しく選べているかどうかをテストします。 数当てゲームと同様に、乱数を生成した後にprintf関数を置いて生成された数を表示し、これを何回も繰り返し実行して生成された数が1から3までの数であることを確認します。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r, h; int i; // テスト用 srand((unsigned int) time(NULL)); // 乱数のシード // テスト用 for (i = 1; i <= 1000; i++) { r = rand() % 3 + 1; printf("# r = %d\n", r); if (r < 1 || r > 3) { exit(1); } } /* printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); */ while (h == r) { printf("あいこでしょ!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); } if (r == 1 && h == 3 || r == 2 && h == 1 || r == 3 && h == 2) { printf("あなたの勝ち!\n"); } else { printf("あなたの負け!\n"); } return 0; }
つぎに、コンピューターの手をグー、チョキ、パーのそれぞれに固定して、残りのモジュールのテストを行います。
ここでは、テスト・ケースが限定されているので、ホワイト・ボックス・テストを用いて、すべてのケースをカバーするようにテスト・ケースを作成します。 つまり、r が 1, 2, 3 のそれぞれについて、h が 1, 2, 3 のときの計9通りについて、実行結果を確認します。
コンピューターの手をグーに固定するには、乱数を生成した直後に、r に 1 を代入します。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { int r, h; srand((unsigned int) time(NULL)); // 乱数のシード printf("じゃんけんぽん!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; r = 1; // テスト用 scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); while (h == r) { printf("あいこでしょ!(1: グー、2: チョキ、3: パー):\n"); r = rand() % 3 + 1; r = 1; // テスト用 scanf("%d", &h); printf("あなた: %d、コンピューター: %d(1: グー、2: チョキ、3: パー)\n", h, r); } if (r == 1 && h == 3 || r == 2 && h == 1 || r == 3 && h == 2) { printf("あなたの勝ち!\n"); } else { printf("あなたの負け!\n"); } return 0; }
このテスト用プログラムに対して、1, 2, 3 とすべてのパターンを入力して、実行結果を確認します。
luna% a.out じゃんけんぽん!(1: グー、2: チョキ、3: パー): 1 あなた: 1、コンピューター: 1(1: グー、2: チョキ、3: パー) あいこでしょ!(1: グー、2: チョキ、3: パー): 2 あなた: 2、コンピューター: 1(1: グー、2: チョキ、3: パー) あなたの負け! luna% a.out じゃんけんぽん!(1: グー、2: チョキ、3: パー): 3 あなた: 3、コンピューター: 1(1: グー、2: チョキ、3: パー) あなたの勝ち!
演習3 †
プログラム2のテストを行え。
まとめ †
プログラム作成は、設計、コーディング、テストの順に行います。
まず、出力結果のイメージを作成し、プログラムの流れをフローチャートで表します(設計)。
フローチャートができたら、フローチャートに基づいてプログラムを作成します(コーディング)。
プログラムができたら、正しく動くことを確認します(テスト)。