エンジニアとしてデビューしよう。

Column

ハイ・アンド・ローを作る 第3回

ハイ・アンド・ローを作る 第3回

今回はハイ・アンド・ロー制作の最終回です。 第1回でコンピューターに無作為な数値を選ばせ、第2回でプレイヤーが入力した数値と比較し、正解/不正解の判定まで行いました。

残りの作業としては、正解するまでプレイヤーに繰り返し数値を入力させる部分になります。 同じ処理を繰り返すので、プレイヤーが数値を入力して答えと比較する部分をコピーすれば良さそうです。

#include
#include
#include
int main(void)
{
int inputNum;
int answer;
srand( time(NULL) );
answer = (rand() % 100) + 1;
printf("answer = %d\n" , answer);
printf("input:");
scanf("%d",&inputNum);
// 1回目の入力
printf("inputNum = %d\n" , inputNum);
if( answer == inputNum )
{
printf("正解!\n");
}
else
{
if( answer > inputNum )
{
printf("答えの方が 大きい です\n");
}
else
{
printf("答えの方が 小さい です\n");
}
}
// 2回目の入力
printf("input:");
scanf("%d",&inputNum);
printf("inputNum = %d\n" , inputNum);
if( answer == inputNum )
{
printf("正解!\n");
}
else
{
if( answer > inputNum )
{
printf("答えの方が 大きい です\n");
}
else
{
printf("答えの方が 小さい です\n");
}
}
return 0;
}

とりあえず、2回分コピーしてみました。 確かに2回入力するチャンスがありますが、全く同じ処理をコピーしているだけなので、1回目で正解しても2回目の入力を促されてしまいます。

ということは「1回目で不正解であれば、2回目の入力を促す」ようにしなければいけませんね。 以下のように書き換えてみました。

#include
#include
#include
int main(void)
{
int inputNum;
int answer;
srand( time(NULL) );
answer = (rand() % 100) + 1;
printf("answer = %d\n" , answer);
printf("input:");
scanf("%d",&inputNum);
// 1回目の入力
printf("inputNum = %d\n" , inputNum);
if( answer == inputNum )
{
printf("正解!\n");
}
else
{
if( answer > inputNum )
{
printf("答えの方が 大きい です\n");
}
else
{
printf("答えの方が 小さい です\n");
}
// 1回目は不正解だったので、2回目の入力
printf("input:");
scanf("%d",&inputNum);
printf("inputNum = %d\n" , inputNum);
if( answer == inputNum )
{
printf("正解!\n");
}
else
{
if( answer > inputNum )
{
printf("答えの方が 大きい です\n");
}
else
{
printf("答えの方が 小さい です\n");
}
}
}
return 0;
}

最初のソースコードに比べて、分岐の入れ子が増えてしまいました。 これでは3回、4回と入力できる回数とともに、入れ子が複雑になってしまいますね。

また、根本的な問題として「最大何回入力させればよいのか?」もあります。 1回目で成功する人もいれば、10回間違える人もいるでしょう。

ここで、反復が存在する理由がはっきりしてきます。 敢えて極端な言い方をすれば、反復は「無くてもよいもの」です。 反復を使わなくても、上記の様に延々とコピーしていけばプログラムとしては動かせるでしょう。

しかし、それは現実的ではありません。 プログラムというのは何万回も繰り返して処理する必要も出てきます。 とても人間が対応できるレベルではありません。

そこで反復の登場です。 反復は人間が面倒なことをコンピュータに代わりにさせることができます。 「○○の間は××をしておくように」と命令するイメージです。

反復の記述方法は幾つかありますが、今回は「while」を採用します。 whileは続く丸括弧の中に反復の継続条件を記述します。

#include
#include
#include
int main(void)
{
int i = 0;
while( i < 10 )
{
printf("loop...\n");
i++;
}
}

まずは簡単な反復を練習してみました。 iの値が「10よりも小さい間」は、whileの直後にあるブロックが実行され続けます。 ポイントは反復対象の中でiの値を加算しているところ(i++)です。 ここで反復の継続条件を変化させ、無限ループにならないように制御しています。

では、先ほどコピーしていた処理を、どのように反復化すべきでしょうか? 方法は沢山ありますが、今回は最もシンプルと思われる方法を考えてみました。

whileは、続く丸括弧の中の値が 0 であれば反復を終了し、0以外であれば継続します。 ※ プログラム言語によってはtrue/falseが相当します

よって、whileに継続条件を与えなくても while( 1 ) と記述すれば、とりあえず反復を続けてくれます。(故意に無限ループさせるイメージ) そのままでは無限ループになってしまうため、反復処理中にある条件を満たしたタイミングで「break」によって反復を終了させます。

先ほどの反復練習をbreakを使った制御に書き換えると、以下のようになります。

#include
#include
#include
int main(void)
{
int i = 0;
while( 1 )
{
if( i >= 10 )
{
break;
}
printf("loop...\n");
i++;
}
}

今回のハイ・アンド・ローにも、breakを使えばスマートに反復を導入できそうですね。

#include
#include
#include
int main(void)
{
int inputNum;
int answer;
srand( time(NULL) );
answer = (rand() % 100) + 1;
printf("answer = %d\n" , answer);
while( 1 )
{
printf("input: ");
scanf("%d" , &inputNum);
printf("inputNum = %d\n" , inputNum);
if( answer == inputNum )
{
printf("正解!");
break;
}
else
{
if( answer > inputNum )
{
printf("答えの方が 大きい です\n");
}
else
{
printf("答えの方が 小さい です\n");
}
}
}
return 0;
}

繰り返し対象をwhileのブロックで囲み、正解した場合にbreakで反復を終了しています。 これまで作った処理の形を崩さずに反復を導入することができました。

さて、いよいよ仕上げです。 不要なprintfを削除して、数値判定の部分を短縮した記述に書き換えましょう。

#include
#include
#include
int main(void)
{
int inputNum;
int answer;
srand( time(NULL) );
answer = (rand() % 100) + 1;
printf("*** ハイ・アンド・ロー ゲーム ***\n");
while( 1 )
{
printf("input: ");
scanf("%d" , &inputNum);
if( answer == inputNum )
{
printf("正解!");
break;
}
else if( answer > inputNum )
{
printf("答えの方が 大きい です\n");
}
else
{
printf("答えの方が 小さい です\n");
}
}
return 0;
}

これでハイ・アンド・ローの完成です。

もちろん他の作り方も沢山ありますが、今回はできるだけ簡潔に、短く記述できる方針で作ってみました。 今回のハイ・アンド・ローを元にして、自分なりのアレンジを加えても面白いと思います。

皆さまのプログラミングライフに、少しでも役立てば幸いです。