【C言語】数当てゲームの入力回数は”指数不等式”で設定せよ

C言語

ようこそ、KiRiOのブログへ!

本記事では「新・明解C言語中級編」の自由課題について解説します。


自由課題のとおり、この参考書には解答が載っておりません。
そこで、本記事で私なりの解答を共有しますので、ぜひ解答の参考にしてみてください。

今回解説する課題は「演習1-3」で、課題内容は以下のとおりです。

演習1-3当てさせる数を$-$999以上999以下の整数とした《数当てゲーム》を作成せよ。
プレーヤが入力できる最大の回数が、どのくらいであれば適当であるのかも考察すること。

今回の課題内容を整理しますと、以下のようになります。

  1. 《数当てゲーム》を作成
  2. $-$999以上999以下の範囲内で乱数を生成(《数当てゲーム》の答え)
  3. プレーヤの最大入力回数を設定
  4. プレーヤに整数を入力させる
  5. 入力値と答えを比較して結果を表示

それでは、これらを順番に設計していきます。

設計

《数当てゲーム》を作成

ここでは、ゲームの大枠を設計します。

《おみくじ》プログラムでも実装しましたが、「ゲームを開始するか否か」を選択する機能です。

【C言語】乱数を生成するためには”種”が必要
ようこそ、KiRiOのブログへ! 本記事では、「新・明解C言語中級編」の自由課題について解説します。 自由課題のとおり、この参考書には解答が載っておりません。 そこで、本記事で私なりの解答を共有しますので、ぜひ解答の参考にして...
  • プログラム実行後に「《数当てゲーム》を開始しますか?(はい/いいえ)」というメッセージを表示
  • 「はい」を選択するとおみくじを引くことができる
  • 「いいえ」を選択するとプログラムが終了する
  • それ以外の値が入力されると再度メッセージが表示される

《数当てゲーム》の答えを設定

次に、ゲームの答えを設定します。

今回は、課題内容にもありますとおり「$-$999以上999以下の整数」となります。
すなわち、プログラム実行時に「$-$999以上999以下の乱数」を生成すればよいのです。

プレーヤの最大入力回数を設定

本問のカギとなる部分が、この「最大入力回数」だと思います。
多すぎると当たりやすくなりますし、少なすぎると当てにくくなります。
また、整数範囲によっても回数は変わってきます。
最大入力回数の適正値は、どのように設定すればよいのでしょうか。

わかりやすくするために、「1〜10の整数を当てるゲーム」で考えてみましょう。
範囲が狭い場合ですと、適当に入力しても当たる可能性はあります。
しかし確実に当てたい場合、みなさんどのように入力しますか。

まずは「答えが5より小さいのか・大きいのか」を調べるために、「5」すなわち「範囲の中央値」を入力するのではないでしょうか。
そして、答えが「5より小さい」とわかった場合は、今度は1〜4の中央値にあたる「2」(もしくは「3」)を入力するはずです。
こうして、1回の入力ごとに範囲を半分に狭めていけば、自ずと答えに辿り着きます。

これを踏まえて「1〜10の整数を当てるゲーム」で答えが「1」だった場合、以下のようになります。

  • プレイヤー「5を入力(1〜10の中央値)」←1回目
  • プログラム「5より小さいです」
  • プレイヤー「3を入力(1〜4の中央値)」←2回目
  • プログラム「3より小さいです」
  • プレイヤー「2を入力(1〜2の中央値)」←3回目
  • プログラム「2より小さいです(←1で確定)」
  • プレイヤー「1を入力」←4回目
  • プログラム「正解です!」

結果として、最大4回の入力で数を当てることができました。
この例から以下のことがわかります。

10を2で割り続けると4回までは正の数である

今度は逆に、数の昇順方向に見ていきますと、上記内容は以下のように言い換えられます。

2を4回掛け続けると10を超える

すなわち、4という数字は以下のように求められます。

(範囲内の整数の数を超えない最大の2の累乗の指数)+1

今回の範囲内の整数の数は「0〜10の間の整数の数」ですので「10」になります。
そして「10」を超えない最大の2の累乗は「8」であり、その指数である「3」に1を加えたものが「4」となります。

それでは、上記内容を数式として表します。求める数(最大入力回数)を$n$とすると、以下のようになります。

$$2^{n – 1} < 10 < 2^{n}$$

このように、最大入力回数は「指数不等式」で求められることがわかりました。

本問では「$-$999〜999の間の整数」ですので、$n$は以下のようにして求められます。

$$2^{n – 1} < 1999 < 2^{n}$$

高校数学が懐かしいですね。各辺に2を底とする対数をとります。

$$\log_{2}2^{n – 1} < \log_{2}1999 < \log_{2}2^{n}$$ $$n - 1 < \log_{2}1999 < n$$ $$n - 1 < 10.96 < n$$

以上より、本文での最大入力回数は「11回」に設定することにします。

プレーヤに整数を入力させる

次に、プレーヤによる整数入力の設計です。

まず、プレーヤがゲーム開始を選択した後は、ゲームの内容と最大入力回数を表示します。
そして、プレーヤが入力する際は、現在が何回目の入力であるかもあわせて表示します。

  • $-$999〜999の整数を当ててください(制限回数:11回)
  • ◯回目:(プレーヤが整数を入力)

さらに、本文で重要となってくるのが入力回数の管理です。
プレーヤは、最大入力回数分しか整数を入力できません。
そこで、はじめに入力回数を最大回数に設定し、プレーヤの入力が完了するたびに入力回数を1だけ減らしていきます(これをデクリメントといいます)。
反対に、値を1だけ増やすことをインクリメントといいます。
プレーヤの入力は、数を当てる、もしくは入力回数が0になるまで行えます。

  • 入力回数を最大入力回数に設定
  • プレーヤが整数を入力するたびに入力回数を1つ減らす
  • プレーヤの入力は、数を当てる、もしくは入力回数が0になるまで行えるものとする

入力値と答えを比較して結果を表示

最後に、入力値の比較結果をもとに、対応するメッセージを返します。

まずは、入力値と答えが一致しない場合です。
入力が制限回数内である場合、答えが入力値と比べて大きいのか・小さいのかを、残り入力回数とあわせて表示します。
そして、最大入力回数時に一致しなかった場合は、答えとともに不正解である旨のメッセージを表示します。

  • 入力値と答えが一致しない場合
    • 入力が入力回数内であるとき
      • 答えと入力値との差分を残り回数とあわせて表示
      • 例1:250よりも大きいです(残り8回)
      • 例2:386よりも小さいです(残り4回)
    • 入力が最大入力回数に達したとき
      • 結果とともに正解を表示
      • 例:残念!正解は385でした。
  • 入力値と答えが一致する場合
    • 結果とともに正解に達するまでの入力回数を表示
    • 例:正解です!(入力回数:10回)

設計は以上となります。

実装

それでは、設計内容をもとに実装していきます。
以下の順番で実装していきます。

  • 《数当てゲーム》の開始選択
  • ゲームの答えを設定
  • 最大入力回数を設定
  • プレーヤによる整数入力
  • 入力値を答えと比較して結果を表示

《数当てゲーム》の開始選択

《おみくじ》プログラム同様、開始の選択はdo-while文を用いて行います。

【C言語】乱数を生成するためには”種”が必要
ようこそ、KiRiOのブログへ! 本記事では、「新・明解C言語中級編」の自由課題について解説します。 自由課題のとおり、この参考書には解答が載っておりません。 そこで、本記事で私なりの解答を共有しますので、ぜひ解答の参考にして...

以上を実装したものが下記プログラムです。

#include <stdio.h>

int main(void)
{
	int try;

	do
	{
		printf("《数当てゲーム》を開始しますか?【はい…1/いいえ…0】:");
		scanf("%d", &try);

		if (try == 0)
		{
			return 0;
		}
	} while (try != 0 && try != 1);

	return 0;
}

ゲームの答えを設定

設計時にも述べたとおり、$-$999以上999以下の乱数を生成します。
乱数生成の詳細については、下記ブログをご覧ください▼

【C言語】乱数を生成するためには”種”が必要
ようこそ、KiRiOのブログへ! 本記事では、「新・明解C言語中級編」の自由課題について解説します。 自由課題のとおり、この参考書には解答が載っておりません。 そこで、本記事で私なりの解答を共有しますので、ぜひ解答の参考にして...

本問の場合は「-999 + rand() % 1999;」となります。
以上を実装したものが下記プログラムです。

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

int main(void)
{
	int try;
	int ans;

	do
	{
		printf("《数当てゲーム》を開始しますか?【はい…1/いいえ…0】:");
		scanf("%d", &try);

		if (try == 0)
		{
			return 0;
		}
	} while (try != 0 && try != 1);

	srand(time{NULL});
	ans = -999 + rand() % 1999;

	return 0;
}

最大入力回数を設定

設定方法については設計部分で述べてきました。
最大入力回数は定数となりますので、変数宣言前に「const」を追加します。
そして、プレーヤの入力回数を管理するために「残り回数」用の変数を用意し、入力前に定数の値を代入します。
以上を実装したものが下記プログラムです。

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

int main(void)
{
	int try;
	int ans;
	const int max = 11;
	int remain = max;

	do
	{
		printf("《数当てゲーム》を開始しますか?【はい…1/いいえ…0】:");
		scanf("%d", &try);

		if (try == 0)
		{
			return 0;
		}
	} while (try != 0 && try != 1);

	srand(time{NULL});
	ans = -999 + rand() % 1999;

	return 0;
}

プレーヤによる整数入力

プレーヤの入力前に、まずは最大入力回数を含むゲーム内容を表示します。
その後、現在の入力回数とともにプレーヤが入力します。
そして設計部分でも述べたとおり、プレーヤの入力後に入力回数(remain)をデクリメントして入力回数を変更します。
そして、この入力は最大入力回数分または答えを当てられるまで行えます。
すなわち、整数入力の継続条件は「入力回数が正の数」または「入力値が答えと一致しない」となります。
以上を実装したものが下記プログラムとなります。

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

int main(void)
{
	int try;
	int num, ans;
	const int max = 11;
	int remain = max;

	do
	{
		printf("《数当てゲーム》を開始しますか?【はい…1/いいえ…0】:");
		scanf("%d", &try);

		if (try == 0)
		{
			return 0;
		}
	} while (try != 0 && try != 1);

	srand(time{NULL});
	ans = -999 + rand() % 1999;

	printf("\n-999〜999の整数を当ててください(制限回数:%d回)\n", max);

	do
	{
		printf("%2d回目:", (max - remain) + 1");
		scanf("%d", &num);
		remain--;
	} while (remain > 0 && num != ans);

	return 0;
}

入力値を答えと比較して結果を表示

最後に、プレーヤの入力値を答えと比較して結果を表示します。
結果にもとづく条件分岐は、設計で述べた内容に則ります。
これらを実装したものが下記プログラムとなります。

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

int main(void)
{
	int try;
	int num, ans;
	const int max = 11;
	int remain = max;

	do
	{
		printf("《数当てゲーム》を開始しますか?【はい…1/いいえ…0】:");
		scanf("%d", &try);

		if (try == 0)
		{
			return 0;
		}
	} while (try != 0 && try != 1);

	srand(time{NULL});
	ans = -999 + rand() % 1999;

	printf("\n-999〜999の整数を当ててください(制限回数:%d回)\n", max);

	do
	{
		printf("%2d回目:", (max - remain) + 1");
		scanf("%d", &num);
		remain--;

		if (remain == 0)
		{
			break;
		}
		
		if (num > ans)
		{
			printf("\a%3dよりも小さいです(残り%d回)\n", num, remain);
		}
		if (num < ans)
		{
			printf("\a%3dよりも大きいです(残り%d回)\n", num, remain);
		}
	} while (num != ans);

	if (num == ans)
	{
		printf("正解です!(入力回数:%d回)\n", max - remain);
	}
	if (num < ans)
	{
		printf("残念!正解は%dでした。\n", ans);
	}

	return 0;
}

以上で完了となります。
プログラムを実行して、《数当てゲーム》として機能しているか確認してみてください。

まとめ

それでは、本記事で説明した内容を以下にまとめます。

  1. 数当てゲームの最大入力回数は指数不等式をもとに設定
    • 連続する整数の個数:$N$、最大入力回数:$x$
    • $2^{x - 1} < N < 2^{x}$
  2. プレーヤの入力後は入力回数を更新
    1. 最大入力回数を定数として設定
    2. 定数の値を入力回数用変数に代入
    3. プレーヤの入力完了後に変数の値をデクリメント

ここまで、記事を読んでいただき、ありがとうございました。

最後に、今回取り上げた課題が記載されている本を紹介します。
今回扱った課題以外にも、様々なプログラム開発について記載されているため、この記事とあわせて読んでいただくと、より理解が深まると思います。


また、参考として以下の本もご紹介しておきます。

こちらもオススメです。2021年12月14日に最新版が発売されました!

それでは、また別の記事でお会いしましょう!

コメント

タイトルとURLをコピーしました