MSBOOKS

プログラミングとか深層学習とか日常の出来事とか

【C言語入門】part6 : while文,do-while文(繰り返し処理)

前回はswitch文についてやりました.
今回はwhile文,do-while文(繰り返し処理)についてやっていきます.

while文(繰り返し処理)

前回,キャラクターの行動選択の処理をやりましたが,1回行動選択したら終わりでした.通常,ゲームでは複数回行動選択して,敵を倒すまでやるものです.
f:id:msteacher:20190111112059p:plain
だからといってそのまま行動選択の処理を何回も書き続けると,書くのが大変なだけでなく,主人公の行動や敵のHPによっては行動選択の画面の表示回数が異なり,そのまま書くのは難しい感じがします.そんなときに使うのが繰り返し処理while文です.条件を指定し,その条件を満たしている間はずっと繰り返すといった処理になります.まずは簡単にwhile文の動作を見てみましょう.
・test6-1.c

#include <stdio.h>
int main(void){
   int enemyHP;
   printf("敵のHPを入力してね:");
   scanf("%d",&enemyHP);
   printf("今の敵のHP:%d\n",enemyHP);
   while(enemyHP>0){
      printf("敵は3ダメージ受けた!\n");
      enemyHP = enemyHP - 3;
      if(enemyHP < 0){
         enemyHP = 0;
      }
      printf("今の敵のHP:%d\n",enemyHP);
   }
   printf("敵を倒した!\n");
   return 0;
}

・実行結果

C:\Users\ユーザ名\Desktop>test6-1
敵のHPを入力してね:7
今の敵のHP:7
敵は3ダメージ受けた!
今の敵のHP:4
敵は3ダメージ受けた!
今の敵のHP:1
敵は3ダメージ受けた!
今の敵のHP:0
敵を倒した!

動作の雰囲気はつかめたでしょうか.whileの()の中に書いてある条件式を満たしている間は,繰り返し{}の中の処理をするといった感じです.while文の書き方は以下のようになります.条件式は以前if文の時にやった条件式を用い,if文同様複数の条件式を組み合わせることも可能です.
・while文

while(条件式){
   //処理
}

while文がわかれば,先ほど言った行動選択を1回書くだけで,敵のHPが0より大きい場合であれば何回も繰り返すことができます.(test5-3.cを改良)
・test6-2.c

#include <stdio.h>
int main(void){
   int enemyHP;
   int choose;
   printf("敵のHPを入力してね:");
   scanf("%d",&enemyHP);
   printf("今の敵のHP:%d\n",enemyHP);
   while(enemyHP > 0){
      printf("主人公はどうする?\n");
      printf("1.攻撃する\n2.魔法で攻撃する\n3.逃げる\n");
      printf("1,2,3のいずれかを入力:");
      scanf("%d",&choose);
      switch(choose){
         case 1:
            printf("攻撃をした!\n3ダメージ!\n");
            enemyHP = enemyHP - 3;
            break;
         case 2:
            printf("魔法で攻撃した!\n6ダメージ!\n");
            enemyHP = enemyHP - 6;
            break;
         case 3:
            printf("敵から逃げようとしたが,逃げられなかった!\n");
            break;
         default:
            printf("不正な入力です\n");
      }
      //HPがマイナスにならないように調整
      if(enemyHP < 0){
          enemyHP = 0;
      }
      printf("今の敵のHP:%d\n\n",enemyHP);
   }
   printf("主人公の勝利!\n");
   return 0;
}

・実行結果

C:\Users\ユーザ名\Desktop>test6-2
敵のHPを入力してね:7
今の敵のHP:7
敵が出てきた!主人公はどうする?
1.攻撃する
2.魔法で攻撃する
3.逃げる
1,2,3のいずれかを入力:2
魔法で攻撃した!
6ダメージ!
今の敵のHP:1

敵が出てきた!主人公はどうする?
1.攻撃する
2.魔法で攻撃する
3.逃げる
1,2,3のいずれかを入力:1
攻撃をした!
3ダメージ!
今の敵のHP:0

主人公の勝利!

実行結果のイメージを数直線に表すと以下のようになります.
f:id:msteacher:20190111102005p:plain
ちなみに,無限繰り返し(以後無限ループ)を作ることもできます.条件式の部分に1を書けば無限ループのプログラムを書くことが出来ますが,終了条件を必ず書くようにしてください.
・test6-3.c

#include <stdio.h>
int main(void){
   int enemyHP=10;
   int choose;
   printf("今の敵のHP:%d\n",enemyHP);
   while(1){
      printf("主人公はどうする?\n");
      printf("1.攻撃する\n2.魔法で攻撃する\n3.逃げる\n");
      printf("1,2,3のいずれかを入力:");
      scanf("%d",&choose);
      switch(choose){
         case 1:
            printf("攻撃をした!\n3ダメージ!\n");
            enemyHP = enemyHP - 3;
            break;
         case 2:
            printf("魔法で攻撃した!\n6ダメージ!\n");
            enemyHP = enemyHP - 6;
            break;
         case 3:
            printf("敵から逃げようとしたが,逃げられなかった!\n");
            break;
         default:
            printf("不正な入力です\n");
      }
      //HPがマイナスになっても生き続けるようにする
      if(enemyHP < 0){
	 printf("HPがマイナスでも倒れない不死身の体のようだ!\n");
      }
      printf("今の敵のHP:%d\n\n",enemyHP);
      printf("**********\nこのゲームをやめる?\nYES:1,NO:2を入力してね:\n");
      scanf("%d",&choose);
      if(choose==1){
	printf("ゲーム終了!\n**********\n");
	break;
      }
      else{
	printf("ゲームを続けます。\n**********\n");
      }
   }
   return 0;
}

・実行結果

C:\Users\ユーザ名\Desktop>test6-3
今の敵のHP:10
主人公はどうする?
1.攻撃する
2.魔法で攻撃する
3.逃げる
1,2,3のいずれかを入力:2
魔法で攻撃した!
6ダメージ!
今の敵のHP:4

**********
このゲームをやめる?
YES:1,NO:2を入力してね:
2
ゲームを続けます。
**********
主人公はどうする?
1.攻撃する
2.魔法で攻撃する
3.逃げる
1,2,3のいずれかを入力:2
魔法で攻撃した!
6ダメージ!
HPがマイナスでも倒れない不死身の体のようだ!
今の敵のHP:-2

**********
このゲームをやめる?
YES:1,NO:2を入力してね:
1
ゲーム終了!
**********

この場合の終了条件は最後にゲームを終了するか尋ねられたときにYES(1)を入力すればwhile無限ループからbreakする(抜け出す)ことができます.breakはswitch文のときに使ったように,while文の領域内から脱出することができます.
このように終了条件を書かないと無限にプログラムが終了しないので気をつけてください.ちなみに間違えて無限ループするプログラムを書いて実行してしまったときは,実行中の画面をクリックし,キーボードのCtrlキーとCキーを同時に押せば強制終了することができます.

do-while文

while文とほぼ同じ動作をするdo-while文というものがあります.これはdo→whileという名前のように,do(動作させて)からwhile(条件を確かめループ)させるといった処理になります.通常while文はwhileに入るときに一回条件を確認してから入るものです.test6-1.cで最初の入力(敵のHP)を0で入力してみると,while文の中の処理は何もされないことがわかると思います.do-whileは一回動作させてからループをするので,例えばはじめに敵のHPを入力させた(do)ときに,その入力がマイナスであったり0であったりするとき(whileの条件式)もう一度入力を促す(whileループ)といった処理に使えます.
・test6-4.c

#include <stdio.h>
int main(void){
   int enemyHP;
   int choose;
   do{
      printf("敵のHPを入力してね:");
      scanf("%d",&enemyHP);
      if(enemyHP <= 0){
	printf("HPは0より大きい値で入力してね\n");
      }
   }while(enemyHP <= 0);
   printf("今の敵のHP:%d\n",enemyHP);
   while(enemyHP > 0){
      printf("主人公はどうする?\n");
      printf("1.攻撃する\n2.魔法で攻撃する\n3.逃げる\n");
      printf("1,2,3のいずれかを入力:");
      scanf("%d",&choose);
      switch(choose){
         case 1:
            printf("攻撃をした!\n3ダメージ!\n");
            enemyHP = enemyHP - 3;
            break;
         case 2:
            printf("魔法で攻撃した!\n6ダメージ!\n");
            enemyHP = enemyHP - 6;
            break;
         case 3:
            printf("敵から逃げようとしたが,逃げられなかった!\n");
            break;
         default:
            printf("不正な入力です\n");
      }
      //HPがマイナスにならないように調整
      if(enemyHP < 0){
          enemyHP = 0;
      }
      printf("今の敵のHP:%d\n\n",enemyHP);
   }
   printf("主人公の勝利!\n");
   return 0;
}

・実行結果

C:\Users\ユーザ名\Desktop>test6-4
敵のHPを入力してね:0
HPは0より大きい値で入力してね
敵のHPを入力してね:-1
HPは0より大きい値で入力してね
敵のHPを入力してね:3
今の敵のHP:3
主人公はどうする?
1.攻撃する
2.魔法で攻撃する
3.逃げる
1,2,3のいずれかを入力:1
攻撃をした!
3ダメージ!
今の敵のHP:0

主人公の勝利!

このように,求める入力以外のものが入力されたときに再度の入力を促すといった処理に使えます.do-while文の書き方は以下のようになります.while(条件式)の後に;(セミコロン)が入っているので注意してください.
・do-while文

do{
   //はじめにする処理
}while(条件式);

今日やった3つのwhile文について以下の図に処理の流れをまとめてみました.
f:id:msteacher:20190111111107p:plain
if文とwhile文が組み合わさってくると少し混乱するかもしれませんが,1つ1つ分けて考えてみると,単純なものの組み合わせなので難しいプログラムでも一行ずつ確実に読んでいきましょう.練習問題もいつもより少し行数が多くなるものだと思います.わからなかったら解答例を見てもOKなので頑張って理解していきましょう.
お疲れ様でした.

練習問題

・問題1
 a(演算子)b(例えばa+b)の計算をし以下の条件を満たす電卓のプログラムを書いてみよう.変数は整数型変数a,b,ans,chooseを使います.(test6-5.c)
①.aとbにそれぞれ値を入力させます.
②.演算子を選択してもらいます.1~4をキーボードから入力してもらい,(1:+,2:-,3:*,4:/)のように条件分岐させます(割り算は余り(%)も求めます).もしこれ以外の入力があった場合は再度入力を促します.
③.それぞれの演算子での計算結果を出力させます.
④.終了するかどうかのキーボード入力を促し,1の場合は終了,2の場合は①に戻ります(ループします).それ以外の入力があった場合は再度入力を促します.
・実行結果

C:\Users\ユーザ名\Desktop>test6-5
*****電卓*****
a=4
b=5
演算子を選んでください.
(1:+,2:-,3:×,4:÷):5
1~4の値を入力してください.
(1:+,2:-,3:×,4:÷):1
4+5=9
電卓を終了しますか?
(YES:1,NO:2):2
電卓を続けます.
a=5
b=3
演算子を選んでください.
(1:+,2:-,3:×,4:÷):4
5÷3=1余り2
電卓を終了しますか?
(YES:1,NO:2):4
1か2を入力してください.
電卓を終了しますか?
(YES:1,NO:2):1
電卓を終了します.

・問題2
 test6-4.cを改良し,主人公のHPを入れる整数型変数heroHPを定義し,キーボード入力から主人公HPの入力を受け付け(do-while文で0以下にならないようにする),代入する.自分の行動選択が終わった後,敵のHPが0でなければ敵の行動に移り,自分のHPに毎回3ダメージ与えるようにする.どちらかのHPが0になったらwhileループを抜け(while文の条件を変えるか無限ループを作りbreakで条件を書く),どちらが勝ったか画面表示するようにプログラムを改良してみよう.(test6-6.c)
・実行結果

C:\Users\ユーザ名\Desktop>test6-6
敵のHPを入力してね:5
主人公のHPを入力してね:5
今の敵のHP:5
今の主人公のHP:5
戦闘開始!
主人公のターン!主人公はどうする?
1.攻撃する
2.魔法で攻撃する
3.逃げる
1,2,3のいずれかを入力:1
攻撃をした!
3ダメージ!
敵のターン!
主人公に攻撃をした!
3ダメージ!
今の主人公のHP:2
今の敵のHP:2

主人公のターン!主人公はどうする?
1.攻撃する
2.魔法で攻撃する
3.逃げる
1,2,3のいずれかを入力:1
攻撃をした!
3ダメージ!
今の主人公のHP:2
今の敵のHP:0

主人公の勝利!

C:\Users\ユーザ名\Desktop>test6-6
敵のHPを入力してね:5
主人公のHPを入力してね:5
今の敵のHP:5
今の主人公のHP:5
戦闘開始!
主人公のターン!主人公はどうする?
1.攻撃する
2.魔法で攻撃する
3.逃げる
1,2,3のいずれかを入力:3
敵から逃げようとしたが,逃げられなかった!
敵のターン!
主人公に攻撃をした!
3ダメージ!
今の主人公のHP:2
今の敵のHP:5

主人公のターン!主人公はどうする?
1.攻撃する
2.魔法で攻撃する
3.逃げる
1,2,3のいずれかを入力:1
攻撃をした!
3ダメージ!
敵のターン!
主人公に攻撃をした!
3ダメージ!
今の主人公のHP:0
今の敵のHP:2

敵の勝利!

練習問題の解答例

・問題1
 ・test6-5.c

#include <stdio.h>
int main(void){
   int a;
   int b;
   int ans;
   int choose;
   printf("*****電卓*****\n");
   while(1){
      printf("a=");
      scanf("%d",&a);
      printf("b=");
      scanf("%d",&b);
      printf("演算子を選んでください.\n");
      do{
	 printf("(1:+,2:-,3:×,4:÷):");
	 scanf("%d",&choose);
	 if(choose<1||choose>4){
	   printf("1~4の値を入力してください.\n");
	 }
      }while(choose<1||choose>4);
      switch(choose){
         case 1:
	    ans = a + b;
	    printf("%d+%d=%d\n",a,b,ans);
	    break;
         case 2:
	    ans = a - b;
	    printf("%d-%d=%d\n",a,b,ans);
	    break;
         case 3:
	    ans = a * b;
	    printf("%d×%d=%d\n",a,b,ans);
	    break;
         case 4:
	    ans = a / b;
	    printf("%d÷%d%d",a,b,ans);
	    ans = a % b;
	    printf("余り%d\n",ans);
	    break;
      }
      do{
	 printf("電卓を終了しますか?\n(YES:1,NO:2):");
	 scanf("%d",&choose);
	 if(choose!=1&&choose!=2){
	    printf("1か2を入力してください.\n");
	 }
      }while(choose!=1&&choose!=2);
      if(choose==1){
	 printf("電卓を終了します.\n");
	 break;
      }
      else{
	 printf("電卓を続けます.\n");
      }
   }
   return 0;   
}

・問題2
 ・test6-6.c

#include <stdio.h>
int main(void){
   int enemyHP;
   int heroHP;
   int choose;
   do{
      printf("敵のHPを入力してね:");
      scanf("%d",&enemyHP);
      if(enemyHP <= 0){
	printf("HPは0より大きい値で入力してね\n");
      }
   }while(enemyHP <= 0);
   do{
      printf("主人公のHPを入力してね:");
      scanf("%d",&heroHP);
      if(heroHP <= 0){
	printf("HPは0より大きい値で入力してね\n");
      }
   }while(heroHP <= 0);
   printf("今の敵のHP:%d\n",enemyHP);
   printf("今の主人公のHP:%d\n",heroHP);
   printf("戦闘開始!\n");
   while(enemyHP > 0 && heroHP > 0){
      printf("主人公のターン!主人公はどうする?\n");
      printf("1.攻撃する\n2.魔法で攻撃する\n3.逃げる\n");
      printf("1,2,3のいずれかを入力:");
      scanf("%d",&choose);
      switch(choose){
         case 1:
            printf("攻撃をした!\n3ダメージ!\n");
            enemyHP = enemyHP - 3;
            break;
         case 2:
            printf("魔法で攻撃した!\n6ダメージ!\n");
            enemyHP = enemyHP - 6;
            break;
         case 3:
            printf("敵から逃げようとしたが,逃げられなかった!\n");
            break;
         default:
            printf("不正な入力です\n");
      }
      //HPがマイナスにならないように調整
      if(enemyHP < 0){
         enemyHP = 0;
      }
      if(enemyHP != 0){
	 printf("敵のターン!\n");
	 printf("主人公に攻撃をした!\n3ダメージ!\n");
	 heroHP = heroHP - 3;
         //HPがマイナスにならないように調整
	 if(heroHP < 0){
	    heroHP = 0;
	 }
      }
      printf("今の主人公のHP:%d\n",heroHP);
      printf("今の敵のHP:%d\n\n",enemyHP);
   }
   if(heroHP==0){
      printf("敵の勝利!\n");
   }
   else if(enemyHP==0){
      printf("主人公の勝利!\n");
   }
   return 0;
}