こんにちは、コン(@pippi_kon)です。
この記事では『C言語でのクイズゲームの作り方』をご紹介しています。
前回は、構造体にデータを登録する関数を作成してプログラムをスッキリさせる方法をご紹介しました。
初回のプログラムと比較するとだいぶ見栄えがよくなってきましたね!
ここらへんで機能を一つ追加してみようと思います。
今回は、クイズのスコアの記録と結果の表示をさせてみます。
今回の目標
今回作成するプログラムの出力結果です。
クイズが全問終了した後、各問題の解答結果と正答率を表示します。
解答結果の○×は、問題に解答した際に記録しています。
正答率は結果を表示する際に計算しています。
プログラム全文
今回作成したプログラムのご紹介です。
のちほど、プログラムの解説を行います。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#define QUIZ_NUM 3
typedef struct {
char mondai[100];
char sentaku[3][100];
int answer_no;
}quiz_t;
void set_quiz(quiz_t* t, char* m, char* s1, char* s2, char* s3, int a)
{
strncpy(t->mondai, m, sizeof(t->mondai));
strncpy(t->sentaku[0], s1, sizeof(t->sentaku[0]));
strncpy(t->sentaku[1], s2, sizeof(t->sentaku[1]));
strncpy(t->sentaku[2], s3, sizeof(t->sentaku[2]));
t->answer_no = a;
}
int main(void)
{
int ans;
char result[QUIZ_NUM][10];
int ok_cnt = 0;
int i;
quiz_t quiz[QUIZ_NUM];
set_quiz(&quiz[0], "リンゴは英語で何と言う?", "apple", "orange", "banana", 1);
set_quiz(&quiz[1], "大正->昭和->○○->令和\n○○に入る年号は?", "慶応", "明治", "平成", 3);
set_quiz(&quiz[2], "世界三大珍味はどれ?", "イカスミ", "キャビア", "チーズ", 2);
for (i = 0; i < QUIZ_NUM; i++) {
printf("[第%d問]\n", i + 1);
printf("%s\n", quiz[i].mondai);
printf("1:%s 2:%s 3:%s\n", quiz[i].sentaku[0], quiz[i].sentaku[1], quiz[i].sentaku[2]);
printf(">>> ");
scanf("%d", &ans);
if (ans == quiz[i].answer_no) {
printf("正解!\n\n");
strncpy(result[i], "○", sizeof(result[i]));
ok_cnt++;
}
else {
printf("不正解...\n");
printf("正解は %d:%s です。\n\n", quiz[i].answer_no, quiz[i].sentaku[quiz[i].answer_no - 1]);
strncpy(result[i], "×", sizeof(result[i]));
}
}
printf("--クイズ終了--\n");
for (i = 0; i < QUIZ_NUM; i++) {
printf("[第%d問]...%s\n", i + 1, result[i]);
}
printf("あなたは %d 問中 %d 問正解でした (正答率:%d %%)\n", QUIZ_NUM, ok_cnt, (ok_cnt * 100) / QUIZ_NUM);
return 0;
}
プログラムの解説
クイズ数を定数化
#define QUIZ_NUM 3
スコア処理の前に、今まで「3」と直接指定していたクイズの問題数を定数(#define)にしました。
プログラム内で数字を直接指定しているものはマジックナンバーと呼ばれています。
数字だけでは意味がわかりにくいし、数字を変えたくなったときに複数箇所で指定していた場合に修正が大変です。
なのでなるべく使わない(直接数字を指定しない)ようにしましょう。
このような意味を持つ数字は定数・変数で扱うのが一般的。
定数名・変数名で数字の意味がわかりやすくなるし、数字を変えたいときは定数値・変数値を変えるだけでOK。(もちろん、数字を変えたことによる影響調査は必要)
前回のプログラムから以下の箇所を定数に変えました。(3→QUIZ_NUM)
quiz_t quiz[QUIZ_NUM];
for (i = 0; i < QUIZ_NUM; i++) {
解答結果の保存
char result[QUIZ_NUM][10];
解答結果を記録(保存)するための変数を宣言します。
今回は"○"か"×"を保存するので文字列型の配列を用意しました。
配列の要素数はクイズの問題数と同じにすればOKなので、先ほど定義した定数(QUIZ_NUM)を指定します。
実際に解答結果を記録している箇所は以下です。
strncpy(result[i], "○", sizeof(result[i]));
strncpy(result[i], "×", sizeof(result[i]));
解答結果が正解なら"○"を、不正解なら"×"を配列に格納しています。
ループカウンターを配列の添え字に使用しているので、配列の先頭から順に『第1問目→第2問目→…』の結果となります。
正解数の記録
int ok_cnt = 0;
正答率を求めるために、問題に正解した回数を記録する変数を宣言します。
この変数は以下のように使用します。
- 問題に正解 → カウントアップする(+1)
- 問題に不正解 → なにもしない
明示的に値を代入するわけではないので、事前に初期化(0を代入)しておく必要があります。(C言語では変数宣言時にゴミデータが入っている)
今回は変数宣言時に初期化していますが、33行目あたりで初期化してもOKです。
実際にカウントアップしている箇所は以下です。
ok_cnt++;
繰り返しになりますが、正解したときのみ値をカウントアップ(+1)しています。
不正解のときは何もしていません。
スコアの表示
printf("--クイズ終了--\n");
for (i = 0; i < QUIZ_NUM; i++) {
printf("[第%d問]...%s\n", i + 1, result[i]);
}
printf("あなたは %d 問中 %d 問正解でした (正答率:%d %%)\n", QUIZ_NUM, ok_cnt, (ok_cnt * 100) / QUIZ_NUM);
クイズが終わったら結果を表示します。
結果の表示は以下の2種類。
- 各問題の解答結果
- 正答率
まずは記録していた各問題の解答結果を表示します。
for (i = 0; i < QUIZ_NUM; i++) {
printf("[第%d問]...%s\n", i + 1, result[i]);
}
解答結果は配列resultに格納しているので、forループで先頭から順に表示します。
何問目の結果なのかはforループのカウンターiを利用。
iは0からカウントアップされるので、表示する際は+1しています。
解答結果の表示が終わったら「全問題数」「正解数」「正答率」を表示します。
参照する値はそれぞれ以下の通り。
内容 | 値 |
---|---|
全問題数 | QUIZ_NUM |
正解数 | ok_cnt |
正答率 | ok_cnt×100÷QUIZ_NUM |