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

単語当てゲームLv1の実装(C/C++)


今回は、前回作成した関係図(↓)を元に、プログラム化を進めていきます。
各要素の関係図
この関係図には、既にプログラム化に必要な概略が入っています。
コードの流れが循環している部分
(「その問題の答えをプレイヤーはキーボードから入力」と「成否を判定します」)
はループを使うことが分かりますし、
線がつながっていない要素と処理は、変数のスコープで見えない状態でもかまわないということです。


さて、それではプログラムに起こしていきましょう。
まず基本形。
<  1>
<  2>
<  3>
<  4>
<  5>
<  6>
<  7>
#include <stdio.h>

int main(void){
   
//終了待ち
   
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>
#include <stdio.h>

int main(void){
   
//(1)ソース上に直接書き込んだ問題
   
char question[10][2][16]={
      {
"1+2=","3"},
      {
"3+4=","7"},
      {
"1-2=","-1"},
      {
"3-4=","-1"},
      {
"1*2=","2"},
      {
"3*4=","12"},
      {
"1/2=","0.5"},
      {
"3/4=","0.75"},
      {
"1の2乗=","1"},
      {
"3の4乗=","81"}
   };
   
//選択された問題
   
int sel_question;
   
//入力された答え
   
char player_ans[80]="";
   
   
//終了待ち
   
getchar();
   
return 0;
(1)5行目から16行目の範囲は1つの文です。
ここで定義する三次元配列 question は10*2*16の要素を持つ char 型配列です。
まず、10の部分が問題数で、次の2が「問題」「正解」、最後の16が文字列用の領域です。
今回の初期化式は {} で複数回囲んでそれぞれの要素を一斉に初期化しています。
この場合、 question[0][0] は "1+2=" となり、 question[0][0][0] は '1' となります。
そして question[0][1] は "3" となり、 question[1][0] は "3+4=" という風に初期化されます。


次に、各機能を追加していきます。
<  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){
   
//ソース上に直接書き込んだ問題
   
char question[10][2][16]={
      {
"1+2=","3"},
      {
"3+4=","7"},
      {
"1-2=","-1"},
      {
"3-4=","-1"},
      {
"1*2=","2"},
      {
"3*4=","12"},
      {
"1/2=","0.5"},
      {
"3/4=","0.75"},
      {
"1の2乗=","1"},
      {
"3の4乗=","81"}
   };
   
//選択された問題
   
int sel_question;
   
//入力された答え
   
char player_ans[80]="";
   
   
//機能「乱数を使って選択し表示します」
   
srand(time(NULL));//乱数を初期化
   
sel_question=rand()%10;//「選択された問題」に0〜9の乱数を生成して代入
   
printf("%s",question[sel_question][0]);//(1)選択した問題を表示
   
   //機能「その問題の答えをプレイヤーはキーボードから入力」
   
fgets(player_ans,79,stdin);
   
   
//機能「成否を判定します」
   
   //機能「クリア表示して終了」
   
   //終了待ち
   
getchar();
   
return 0;
(1)27行目で puts ではなく printf を使っているのは単にこの位置で改行されないようにしているだけです。
問題文表示後に改行するのであれば puts(question[sel_question][0]) でOKです。


さて、次の成否判定ですが、戻るルートがあるので今回は無限ループで括っておきます。
<  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>
< 49>
< 50>
< 51>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

int main(void){
   
//ソース上に直接書き込んだ問題
   
char question[10][2][16]={
      {
"1+2=","3"},
      {
"3+4=","7"},
      {
"1-2=","-1"},
      {
"3-4=","-1"},
      {
"1*2=","2"},
      {
"3*4=","12"},
      {
"1/2=","0.5"},
      {
"3/4=","0.75"},
      {
"1の2乗=","1"},
      {
"3の4乗=","81"}
   };
   
//選択された問題
   
int sel_question;
   
//入力された答え
   
char player_ans[80]="";
   
   
//機能「乱数を使って選択し表示します」
   
srand(time(NULL));
   sel_question=rand()%10;
   printf(
"%s",question[sel_question][0]);
   
   
while(1){//for(;;)が本来の無限ループですが、こっちもよく使われます
      //機能「その問題の答えをプレイヤーはキーボードから入力」
      
fgets(player_ans,79,stdin);
   
      
//機能「成否を判定します」
      /*(1)入力の最後の改行文字除去は、大真面目にやると↓のようになりますが・・・
      if(strlen(player_ans)&&(player_ans[strlen(player_ans)-1]=='\n')){
         player_ans[strlen(player_ans)-1]='\0';
      }
      */
      
if(player_ans[0]!='\0')player_ans[strlen(player_ans)-1]='\0';//(2)これぐらいでも十分です。
      
if(!strcmp(player_ans,question[sel_question][1]))break;//(3)入力と正解を比較し、同じならループ終了
      
puts("違います。もう一度入力してください。");
   }
   
   
//機能「クリア表示して終了」
   
puts("正解です。Enterを押すと終了します。");
   
   
//終了待ち
   
getchar();
   
return 0;
(1)35〜39行は入力の最後に付いている改行文字を除去するコードです。
が、ぶっちゃけここまで真面目にやる必要はあまりありません(笑)
通常は fgets 関数の入力文字列なら(2)40行の式で十分です。
なお、最初の if の判定は「 player_ans の文字数が0」の時に strlen(player_ans)-1 の結果が-1になるので、
player_ans[-1] へアクセスしてしまうことを避けるためにあります。

(3)41行で、 strcmp 関数を使って文字列を比較しています。
strcmp 関数は渡した二個の文字列の内容が同じなら0を返します。
C/C++では0だけが偽扱いされるため、 ! (論理NOT、つまり真偽反転)演算子で0だけを真にします。
その上で、条件が真の場合のみ無限ループを break して終了させます。


これで、とりあえず前回作成した関係図通りの動きをするプログラムが作成できたことになります。
今回は「関係図と実際のコードとの対応」という形に慣れてもらうため、意図的なバグ混入はありません。

今回で Lv1 の内容が終了したので、次回からは Lv2 の内容に入っていきます。

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

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

最終更新 2009/11/06