[前へ]   [目次へ]   [次へ]

CUIミニゲーム(高低当て)(C/C++)

では、実際に作っていきましょう。

まず、雛形+終了待ち。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
#include <stdio.h>

int main(void){
   
//終了待ち
   
getchar();
   
return 0;

手順1の内容を追加します。
これは準備段階なので単体で記述します。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
   
int now;//現在値
   //randを初期化
   
srand(time(NULL));
   
//初期値を生成
   
now=rand()%10;
   
//終了待ち
   
getchar();
   
return 0;

手順2〜5は、全部組み合わせて考えなければいけません。
まず、ループの範囲を考えます。
繰り返し実行すべきなのは手順2〜5なので、ここにループを張ります。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
   
int now;//現在値
   //randを初期化
   
srand(time(NULL));
   
//初期値を生成
   
now=rand()%10;
   
//ループ
   
do{
   }
while();//(1)条件式は?
   //終了待ち
   
getchar();
   
return 0;
(1)13行の条件式は手順4、5の
「「次回値」と「現在値」を比較し、「選択結果」と合致するか検証します。」
「手順4の結果が合致だった場合、「次回値」を「現在値」とし、手順2に戻ります。
   非合致だった場合、手順6に進みます。」
から、「選択結果」との比較結果が合致するかどうかを判断しなければいけません。
「選択結果」は「次回値」<「現在値」、「次回値」==「現在値」、「次回値」>「現在値」の
三通り考えられるわけですが、まず「選択結果」の表現を決めないと話が進みません。

というわけで、とりあえず条件式は保留にして「選択結果」を入力します。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
   
int now;//現在値
   
int sel=0;//プレイヤーの選択(選択結果)
   
char buf[256]="";//入力バッファ
   //randを初期化
   
srand(time(NULL));
   
//初期値を生成
   
now=rand()%10;
   
//ループ
   
do{
      printf(
"現在の値:%d\n"
         "1:現在値<次回値 2:現在値==次回値 3:現在値>次回値\n"
         "次回はどうなると思いますか?:"
,now);//(1)
      
fgets(buf,255,stdin);
      
if((buf[0]>='1')&&(buf[0]<='3')){//(2)数字の1,2,3
         
sel=buf[0]-'0';//(3)数値化して格納
      }
   }
while();//条件式は保留。
   //終了待ち
   
getchar();
   
return 0;
(1)15〜17行は3行に渡る長いprintfの呼び出しです。
"現在の値:%d\n" のような文字列定数は、 "a" "b" "c" のように連続して記述すると、
コンパイル時に連結して "abc" と書いたものとして扱われます。
この時、間に挟んでも無視されるのは改行、スペース、タブ文字です。
上のように改行したところでソース中でも改行しておくと、長い文字列を表示する時に読みやすいです。

"abc\
def"
 のように行末に \ をおいて改行することでも文字列定数を改行できますが、
この方法だとインデントができない(文字列定数に含まれてしまう)ので、読みにくくなり私は嫌いです。
ちなみに、行末の \ を置かないと「文字列定数が終了していない」というエラーが出ます。

(2)19行は入力された一文字目が1〜3の範囲であることをチェックしています。
(3)20行は以前紹介した、文字コードから数値への変換です。
文字としての1〜3を数値としての1〜3に変換し、「選択結果」の sel に代入します。

さて、16行目の出力にあるように、 sel と選択した条件の対応は
1:現在値<次回値 2:現在値==次回値 3:現在値>次回値
としました。


次は、手順3、「0〜9のいずれかの数値をランダムに生成し、これを「次回値」とします。」ですが、
初期値の生成とあまり変わらないので現在値と次回値の比較までを一気にやります。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
   
int now;//現在値
   
int sel=0;//プレイヤーの選択(選択結果)
   
int next;//次回値
   
char buf[256]="";//入力バッファ
   
int cmpres;//現在値と次回値の比較結果
   //randを初期化
   
srand(time(NULL));
   
//初期値を生成
   
now=rand()%10;
   
//ループ
   
do{
      printf(
"現在の値:%d\n"
         "1:現在値<次回値 2:現在値==次回値 3:現在値>次回値\n"
         "次回はどうなると思いますか?:"
,now);
      fgets(buf,255,stdin);
      
if((buf[0]>='1')&&(buf[0]<='3')){
         sel=buf[0]-
'0';
      }
      next=rand()%10;
//(1)次回値を生成
      
if(now<next){//(2)現在値<次回値
         
cmpres=1;
      }
      
else if(now==next){//現在値==次回値
         
cmpres=2;
      }
      
else{//(3)現在値>次回値
         
cmpres=3;
      }
   }
while();//条件式は保留。
   //終了待ち
   
getchar();
   
return 0;
(1)24行は次回値を生成して「次回値」の変数 next に代入します。
「現在値」と「次回値」は次で比較するので、この段階で「現在値」に上書きするのは間違いです。

(2)25行〜33行は「現在値」と「次回値」の関係を調査し、「比較結果」を格納する変数 cmpres に代入します。
この時の代入値は「選択結果」と同じ対応にしておきます。
これらは一行構文でも書けますが注釈がどれを指すか分かりにくくなるのでブロック構文にしてます(笑)

(3)31行は条件式を指定していませんが、「現在値>次回値」として扱っています。
これはそこまでの if や else if が偽になっていることから、
「現在値」と「次回値」の関係は「現在値<次回値」でも「現在値==次回値」でもないことが分かっています。
この条件は必然的に「現在値>次回値」の関係であることを意味するので、条件をチェックしなくてもいいのです。

なお、条件が複雑で見逃しがあるかもしれない時はここも else if として条件を書いておいて、
else を追加して else に来たら異常(ようはバグ)とみなす方法もあります。

さて、この時点でゲームの継続条件を判断するのに必要な情報が揃っています。
ゲームが継続する時はループを継続しないといけないので、
ループ継続条件はゲーム続行条件になります。
「現在値」と「次回値」の関係と「選択結果」と「比較結果」の対応は揃えてあるので、
保留していたループ継続条件は「選択結果==比較結果」となります。

また、比較が終わったので、もう「次回値」を「現在値」としてもよくなりました。
このタイミングで「現在値」を「次回の現在値(次回値)」に更新しておきます。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>
< 39>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
   
int now;//現在値
   
int sel=0;//プレイヤーの選択(選択結果)
   
int next;//次回値
   
char buf[256]="";//入力バッファ
   
int cmpres;//現在値と次回値の比較結果
   //randを初期化
   
srand(time(NULL));
   
//初期値を生成
   
now=rand()%10;
   
//ループ
   
do{
      printf(
"現在の値:%d\n"
         "1:現在値<次回値 2:現在値==次回値 3:現在値>次回値\n"
         "次回はどうなると思いますか?:"
,now);
      fgets(buf,255,stdin);
      
if((buf[0]>='1')&&(buf[0]<='3')){
         sel=buf[0]-
'0';
      }
      next=rand()%10;
      
if(now<next){
         cmpres=1;
      }
      
else if(now==next){
         cmpres=2;
      }
      
else{
         cmpres=3;
      }
      now=next;
   }
while(sel==cmpres);
   
//終了待ち
   
getchar();
   
return 0;
ここまでで、手順1〜5までが実装されました。
最後の手順6「選択に成功した数を表示し、ゲームを終了します。」を実装します。

これまでに「選択に成功した数」は数えていないので、新しく追加する必要があります。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>
< 39>
< 40>
< 41>
< 42>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
   
int now;//現在値
   
int sel=0;//プレイヤーの選択(選択結果)
   
int next;//次回値
   
int loopcnt;//成功回数
   
char buf[256]="";//入力バッファ
   
int cmpres;//現在値と次回値の比較結果
   //randを初期化
   
srand(time(NULL));
   
//初期値を生成
   
now=rand()%10;
   loopcnt=-1;
//(1)成功回数を初期化
   //ループ
   
do{
      loopcnt++;
//(2)成功回数をカウントアップ
      
printf("現在の値:%d\n"
         "1:現在値<次回値 2:現在値==次回値 3:現在値>次回値\n"
         "次回はどうなると思いますか?:"
,now);
      fgets(buf,255,stdin);
      
if((buf[0]>='1')&&(buf[0]<='3')){
         sel=buf[0]-
'0';
      }
      next=rand()%10;
      
if(now<next){
         cmpres=1;
      }
      
else if(now==next){
         cmpres=2;
      }
      
else{
         cmpres=3;
      }
      now=next;
   }
while(sel==cmpres);
   
//終了待ち
   
getchar();
   
return 0;
(1)16行は「成功回数」の初期値が-1になっています。
これは19行のループ開始直後のカウントアップで0になり、結果として0で初期化されます。

(2)19行はループした数を数えていきます。
「成功回数」は初期値が-1なので、1回目のプレイヤー選択の時は0、2回目は1となり、
「プレイヤーが選択に成功した数」が常に入ることになります。

選択に失敗した時はループが終了するので、
最後の成功数表示はループ終了後に記述すればいいでしょう。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>
< 39>
< 40>
< 41>
< 42>
< 43>
< 44>
< 45>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
   
int now;//現在値
   
int sel=0;//プレイヤーの選択(選択結果)
   
int next;//次回値
   
int loopcnt;//成功回数
   
char buf[256]="";//入力バッファ
   
int cmpres;//現在値と次回値の比較結果
   //randを初期化
   
srand(time(NULL));
   
//初期値を生成
   
now=rand()%10;
   loopcnt=-1;
   
//ループ
   
do{
      loopcnt++;
      printf(
"現在の値:%d\n"
         "1:現在値<次回値 2:現在値==次回値 3:現在値>次回値\n"
         "次回はどうなると思いますか?:"
,now);
      fgets(buf,255,stdin);
      
if((buf[0]>='1')&&(buf[0]<='3')){
         sel=buf[0]-
'0';
      }
      next=rand()%10;
      
if(now<next){
         cmpres=1;
      }
      
else if(now==next){
         cmpres=2;
      }
      
else{
         cmpres=3;
      }
      now=next;
   }
while(sel==cmpres);
   
//成功数表示
   
printf("失敗!%dでした。\n"
      "%d回成功しました。\n"
,now,loopcnt);
   
//終了待ち
   
getchar();
   
return 0;
これで必要用件は満たしたプログラムができました。
試しに何度かプレイしてみてください。
まぁ、特に面白くは無いと思いますけど(笑)

細々と区切って書いていきましたが、慣れてくればこれぐらいは瞬時に思いつくようになります。

さて、何度かプレイしているとあちこち不備が感じられるでしょう。
特に選択した後に何も言わずに次の選択になるのがなんかうっとおしいので、
成功時にも結果表示を追加してみます。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>
< 39>
< 40>
< 41>
< 42>
< 43>
< 44>
< 45>
< 46>
< 47>
< 48>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
   
int now;//現在値
   
int sel=0;//プレイヤーの選択(選択結果)
   
int next;//次回値
   
int loopcnt;//成功回数
   
char buf[256]="";//入力バッファ
   
int cmpres;//現在値と次回値の比較結果
   //randを初期化
   
srand(time(NULL));
   
//初期値を生成
   
now=rand()%10;
   loopcnt=-1;
   
//ループ
   
do{
      loopcnt++;
      printf(
"現在の値:%d\n"
         "1:現在値<次回値 2:現在値==次回値 3:現在値>次回値\n"
         "次回はどうなると思いますか?:"
,now);
      fgets(buf,255,stdin);
      
if((buf[0]>='1')&&(buf[0]<='3')){
         sel=buf[0]-
'0';
      }
      next=rand()%10;
      
if(now<next){
         cmpres=1;
      }
      
else if(now==next){
         cmpres=2;
      }
      
else{
         cmpres=3;
      }
      now=next;
      
if(sel==cmpres){
         printf(
"成功!%dでした。\n\n",now);
      }
   }
while(sel==cmpres);
   
//成功数表示
   
printf("失敗!%dでした。\n"
      "%d回成功しました。\n"
,now,loopcnt);
   
//終了待ち
   
getchar();
   
return 0;
と、こんなところでしょうか。
とりあえず do で作ってみましたが、なんかこの先の拡張に向いてなさそうなので、
for に置換えてみようと思います。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
<  8>
<  9>
< 10>
< 11>
< 12>
< 13>
< 14>
< 15>
< 16>
< 17>
< 18>
< 19>
< 20>
< 21>
< 22>
< 23>
< 24>
< 25>
< 26>
< 27>
< 28>
< 29>
< 30>
< 31>
< 32>
< 33>
< 34>
< 35>
< 36>
< 37>
< 38>
< 39>
< 40>
< 41>
< 42>
< 43>
< 44>
< 45>
< 46>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void){
   
int now;//現在値
   
int sel=0;//プレイヤーの選択(選択結果)
   
int next;//次回値
   
int loopcnt;//成功回数
   
char buf[256]="";//入力バッファ
   
int cmpres=0;//現在値と次回値の比較結果
   //randを初期化
   
srand(time(NULL));
   
//初期値を生成
   
now=rand()%10;
   
//ループ
   
for(loopcnt=0;sel==cmpres;loopcnt++){//(1)
      
printf("現在の値:%d\n"
         "1:現在値<次回値 2:現在値==次回値 3:現在値>次回値\n"
         "次回はどうなると思いますか?:"
,now);
      fgets(buf,255,stdin);
      
if((buf[0]>='1')&&(buf[0]<='3')){
         sel=buf[0]-
'0';
      }
      next=rand()%10;
      
if(now<next){
         cmpres=1;
      }
      
else if(now==next){
         cmpres=2;
      }
      
else{
         cmpres=3;
      }
      now=next;
      
if(sel==cmpres){
         printf(
"成功!%dでした。\n\n",now);
      }
   }
   
//成功数表示
   
printf("失敗!%dでした。\n"
      "%d回成功しました。\n"
,now,loopcnt);
   
//終了待ち
   
getchar();
   
return 0;
と、こうなりました。

(1)17行の for は少し変則的で、初回式、続行式と継続条件式が直接関係していません。
私的には、初回式 続行式と継続条件式が絶対に関係しなければならないとは考えていないので、
このような表記を使うこともありますが、これが読み辛く感じるなら避けたほうがいいでしょう。


さてさて、今回はなかなかの長丁場になってしまいましたが、
いかがでしたでしょうか。

今回も最終ソースにバグが残っています。
次回までに探してみてください。
今回のヒントは「何度もプレイする」ことです。
動作をよく見ていればおかしいのがわかるはずです。

次回は今回のソースに残ったバグを探してみます。


[前へ]   [目次へ]   [次へ]

プログラミング講座 総合目次

最終更新 2008/10/17