こんにちは､ ようこそ｡
前回の課題であった､ 数字当てゲームの実装について､
デモ解答を提供したいと思います｡
では､ コードブロックを表示し､ 現在のプロジェクトを閉じて､
新しいプロジェクトを作成してみましょう｡
この新企画は､ 「推理ゲーム」とでも名付けようかと思います｡
コンソールアプリケーションになります｡
だから､ 番号を当てるということでいいんです｡
そして､ このプロジェクトを始めるにあたって､ 私は変数を宣言します｡
私はいつも宣言するのが好きで､ それはトップで私の変数です｡
でも､ 乱数発生装置も使ってみようと思っています｡
だから､ カップルに含まれるようにしたいんです｡
Stdlibはデフォルトで既に存在します｡
だから､ あとは自分の時間を足すだけなんです｡ hは､ これも乱数発生器のためのものだからです｡
これは基本的に乱数発生シート用です｡
このプログラムで必要な他の変数は､ 乱数を格納することです｡
だから､ 乱数と言って､ 実際のライブラリから取得するまでは0に初期化することにします｡
そこで､ initializeを宣言します｡
ユーザーからの推測を格納するための変数を作り､ それを0に初期化することにします｡
推測の回数､ 現在の推測の回数､
実際には推測の最大回数と言うべきでしょうか､
推測の最大回数を表す別の変数を作成します｡
また､ 乱数発生器に使えるように､ 時間変数も作らなければならない｡
だから､ 時間t tということでいいんです｡
まず､ ユーザーに入力を求める前に､ 乱数を発生させたいと思います｡
ユーザーに入力を求めてからやればいいのでしょうが､ 最初にやってしまうのは簡単なことですからね｡
そこで､ 乱数発生器を初期化し､ そこにちょっとしたコメントを入れてみます｡
意味のある良い名前を使い､ 必ずインデントしてください｡
講義で話したsrand関数が使われるんだ｡
本来はシードで渡せばいいんです｡
そこで､ unsignedと言いながら､ t変数を渡すだけです｡
This is going to get timeは現在時刻を取得する関数です｡
tで渡すので､ それを種にして､ 終了する｡
さて､ 実際に乱数発生器を手に入れる必要があるので､ 乱数を取得すると言うことにします｡
そして､ これはrand関数から返された値を変数に代入しているだけです｡
そこで､ 乱数をローカル変数に等しいとし､
代入演算子をrandとして､ 関数を呼び出す｡
何も知らなくても､ 21で種明かしをする｡
これによって､ 乱数が0から20の間だけになることが保証されます｡
では､ ユーザーが何をしているのか分かるように､ 画面に2つのメッセージを出力してみましょう｡
だから､ プリントアプリをやって､ これは推理ゲームなんだと｡
そして､ 「0から20までの数字をもう一度選びました」ということも､
念のためお伝えしておきます｡
そして､ 0と20を当てて､ 改行で終わらせるのです｡
ゲームに求めるものを伝えたからには､ 実際にループを始めてみる｡
そして､ 推測の数字が5になるまでループするんだ｡
ですから､ 私はむしろ､ ここで推測の数をクリアしたかったのですが､
初期化したくないのです｡
ループの中で初期化したいんです｡
もし､ forループの最初のセミコロンの前に初期化すれば､ 空にしておくことができます｡
例えばこんな感じで､ ここで初期化します｡
そして､ forループの中では､ 初期化する必要がないので､ セミコロンを置くだけです｡
なぜなら､ 今回のforループでは､ セミコロンの前にあるのは､
ループ制御変数の初期化であることを思い出してください｡
もう初期化しちゃったから必要ないんだけど､
そんなの嫌だ｡
この上で宣言し､ この下で初期化します｡
最大推理数＝5とする｡
これが私のループ制御変数です､ 初期化します｡
最大推理回数に達するとループから飛び出すんだ｡
だからここでは､ 実は違う書き方をしようと思うんです｡
ループが5から0になるように書こうと思います｡
だから､ 私の最大数は5に等しい｡
この変数の名前を変更した方がよいでしょう｡
名前を変えて､ 推理回数と言いましょう｡
そして､ 推測の数は5からスタートすることになります｡
そして､ これを逆向きにするんです｡
そして､ 推測の数が0になったら､ ループから飛び出したいのです｡
そこで､ ループの終了条件として､ 「推測の数が0より大きい」とすることにしました｡
私がここで何をしているか見てください｡
もう推しがいなくなったら､ ループから飛び出すだけです｡
だから､ 0より大きい場合は､ ループの中にとどまることになるんだ｡
そして最後に､ 2番目の気候コロンの後のforループで提供したいのは､
このループ制御変数の変更です｡
ということで､ 推理をマイナスマイナスでやってみました｡
このループの中で注目すべきは､ 私が...を始めていることです｡
ほとんど逆戻りしています｡
5から始めて､ 0にする｡
講義で提供するすべての例では､ 0から始めて､ どんどん上げていきます｡
でも､ 両方やっても問題ないですよ｡
ここでまだ5回反復しているんですね｡
今度は､ あと何回試せるかをメッセージで表示したい｡
そこで､ printfと言いますが､ 実は三項演算子は使っていないので､
ここに組み込んでみます｡
この方法は必要ありませんが､ 三項演算子の使い方を紹介するために､
この方法でやってみます｡
ここで､ あなたは､ パーセントdとパーセントsを残しているとプリントアウトします｡
パーセントdは推測とtrのパーセントsが同数になる､
変わる｡
トライになるか､ トライになるかのどちらかです｡
それで､ 残り何回当てられるかで表示するものを変えています｡
推測の数が1と同じ場合｡
トライを計画するのではなく､ 1トライが残っている､ それでは意味がないんです｡
プリントアウトして1回挑戦してみたい｡
そこで､ ここでは項演算子､ より正確には条件演算子を使うことにする｡
推測の数が1に等しければ､ 推測の数をyに設定し､ そうでなければIESに設定すると言うことです｡
そして､ ここで返されたものが何であれ､ パーセントsが行っていることを印刷するつもりです｡
つまり､ これはIESのどちらかを返し､ 正しい情報を表示することになります｡
そこで､ ターミナルを使って､ 正しい情報をプリントアウトする方法をちょっと派手にやってみました｡
最後に試行回数をプリントアウトした後､ 次に推測を入力するように指示したい｡
そこで､ いくつかの情報を我慢して､
printf just enter a guessと言い､ これには改行が入ります｡
そこで推測を入力し､ その推測をscanfを使って読み込むのですが､ キーボードから読み込む方法は知っていますよね｡
整数になるので､ パーセントdして､ guess変数に入れる｡
だから､ アンパサンドを使う必要があるんです｡
今度は私がチェックしなければならない｡
気になる推しがいるかどうか確認しなければならない｡
そこで､ ここにも終了条件があります｡
if文を使ってみる｡
私の推測が乱数と等しい場合､ 私は何をしたいかと言うことです｡
まあ､ まずはプリントアウトして､ 「正しく当てられたよ」と伝えたいですね｡
だから､ プリントアウトするんです､ おめでとうございます､ ご想像のとおりです｡
他に､ 正しく当てられたらどうしたいのか？
ループを抜けたい｡
では､ どうすればループを抜けることができるのでしょうか？
ブレイクを入れることができた｡
私のループから飛び出してくるような､ ブレイクをすることができました｡
さて､ イコールでなかったので､ else ifを追加したいと思います｡
イコールでない場合は､ その数字を検証したのかどうかを確認したい｡
だから､ 前に､ もう一度入力されるように検証のためのループを入れ子にしておけばいいと言いましたが､
そんなことをしなくても､ fステートメントだけで十分です｡
そこで､ ここでは数値を検証してみたいと思います｡
乱数と等しくない場合は､ 0から20の間にあることを確認したい｡
つまり､ 推測値が0より小さいか､ 論理演算子または2本の平行棒を使用しているか､
推測値が20より大きい場合､ 基本的に推測値が無効であるかどうかをチェックします｡
もしそうで､ ここでは1つのステートメントしかないので､ ブラケットを使用しないのであれば､
ブラケットはオプションです｡
だから､ もし無効な推測を入力されたら､ 私がやるのはprintfだけだ｡
プリントアウトする 0から20までの数字だと
そして､ ここで起こることは何もない｡
それをプリントアウトして､ 次のイテレーションに進むだけです｡
残り試行回数を教えて､ 推測を入力するんです｡
さて､ もし有効な数字を入力しても推測としてカウントされないようにしたかったら､
ネストされたループを持つことができます｡
でも､ 25の何かにエントリーされても､ 推測でカウントされそうです｡
だから､ 0から20の間の数字を設定したというだけです｡
もし､ 有効な番号を入力したが､ それが正しくなかったとしたら､ それはelse節となる｡
ここでは､ すみません､ 間違った数字です､ と言って､ それより大きいか小さいかを伝えるだけです｡
そして､ ここで三項演算子を使うこともできるのですが､ 使わないことにします｡
では､ ここでもifを付けてみましょう｡
Else if random is greater than guess, then what do I want to print?
私は申し訳ありませんパーセントdが間違っている､ パーセントdは彼らの推測になりますを印刷したいです｡
マイナンバーはパーセントsが最初の単語です｡
ランダムの方が大きければ､ マイナンバーの方が大きい｡
私の数値はそれより大きいので､ パーセンタイルは一切使わず､
改行だけにしています｡
Else 乱数が推測より小さい場合は､ より低いことを伝えたいのです｡
だから､ コピー＆ペーストも同じようにできるんです｡
すみません､ 数字が違います､ 私の数字はそれより少ないんですと言えるんです｡
そしてまた､ この2つのelse ifを実際に置くことができます｡
1つのelse ifを持ち､ ここで実際に条件文を作ることができます｡
三項演算子でもいいのですが､ 2つのelse ifを使ってそれ以下ということにしています｡
そうすれば､ それでループが終わるんです｡
そして､ もしループの外に出て､ 正しく当てられず､
試行回数を超えてしまったら､ 「5回試行したけど､
当てられなかったよ」と言って､ 数字を教えてあげるだけです｡
そこで､ ゲームに勝てなかったことを伝えるために､ ユーザーに最後の出力を行い､ その数字が何であったかを伝えます｡
5回挑戦して失敗したわけですが､ その番号は乱数だったのですね｡
なるほど､ それで納得してもらえたのでしょうか？
ここでは､ いろいろなことをやっています｡
ループの中でif文､ else文を使って､ 数字ゲームを実装しているんだ｡
そして最後に､ main関数から戻るのです｡
では､ コンパイルしてみましょう｡ この演習の間､ あなたは何をするべきだったのでしょうか？
スモールチャンクでテストや作業をしていたはずです｡
まずは乱数が動くようにしてから､ テスト､
コンパイル､ テストを行うべきだったのでしょう｡
そして､ ループを開始します｡
ループが推測の回数だけ､ 最大5回走ることを確認し､ それを動作させてテストしてください｡
そして､ ユーザーに何らかの情報を回答してもらい､ それを動作させてテストします｡
そして､ 正しく推測した場合に終了するタイミングを比較し､ テストしてからif文のテストを書く､
これが私の実装方法です｡
では､ これをビルドして､ コンパイルができるかどうか見てみましょう｡
間違いがなければいいのですが｡
ビルドと言いたいところだが
エラーが発生しました､ 無効な番号があります｡
乱数でなければならない､ それは私の変数の名前だからランダムではない｡
そして､ 今回はエラー0件､ 警告2件です｡
私は､ パーセントdは､ いくつかの一致するintを期待しています｡
乱数や推測を出すのを忘れていました｡
ここの推しと､ もう一人の推しと｡
推しのごめん推しは間違ってたよ､ こんなことしなくていいよ､ でもちょっといいことあるよ､
と言ってあげたい｡
さて､ いつも警告を修正したいので､ ビルドしてみましょう｡
警告0エラー0にしたい｡
さて､ これを実行すると､ テストが必要です｡
そして､ ここで実行したいさまざまなテストケースがあるのです｡
複数のテストケースを実行することをお勧めします｡ なぜなら､ 1つのことだけをテストするのは決して良いことではないからです｡
私は0から20までの数字を選んだので､
あなたはそれを当てなければなりません｡
だから､ 16を入れるんです｡
すみません､ 16は間違いで､ それ以下の数字です｡
8にしてみようと思います｡
8は間違い､ それ以下｡
4をするつもりです｡
4は間違い､ マイナンバーはもっと大きい｡
ああ間違えた､ 最初に読んでないんだ｡
だから､ 8より大きいんです｡
12を試してみよう｡
あと2回しか挑戦できない｡
12で右かな｡
だから､ おめでとうと書いてある｡
でも､ 失敗を想定したテストもしたいですよね｡
ここで少しテストしたいのですが､ ここでテストしてみますと､
私の推測を言いますと､ 0を入れるだけです｡
0は間違いです｡
0を入れ続けて､ そこで5回ほど間違いがあったようです｡
5回挑戦して失敗したと表示される｡
当てたい数字は3でしたね｡
だからうまくいくんです｡
では､ 無効な数字を入力した場合にどうなるか､ テストしてみましょう｡
を入力すると､ 45になります｡
数字は0と20だと言ったんです｡
これもテストケースで､ トライにカウントされたわけです｡
そのアカウントをtryにしないのであれば､ ネストしたループを持つことができる｡
そんなところでしょうか｡
すべてのケースをテストしました｡
うまく当てられた場合を検証してみました｡
うまく当てられなかった場合を想定してテストしています｡
そして､ 無効な番号を入力した場合のテストも行いました｡
最初のテストでは､ 「より小さい」「より大きい」を入力するよう指示されたと思いますが､ 他のテストは､
「より大きい」「より小さい」を入力しているかどうかを確認することだけです｡
では､ それを試してみましょう｡
だから今回､ 20を入力すると､ 未満と表示されるのは良いことです｡
今度は10を入力して､ 当てました｡
だから､ 早いんでしょうね｡
しかし､ 同じ出力で less than と greater than を見るべきでしょう｡
もう一度やってみると､ プログラムを実行するたびに違う数字を生成しているので､
乱数が効いているのです｡
20は間違いです｡
では､ 19を試してみましょう｡
1を試してみて､ それよりも大きい｡
だから､ より大きく､ より小さく見えるのです｡
だから､ 効いているのは間違いないんです｡
かなり良さそうだ｡
ひとつだけ､ プログラムにちょっとしたバグがあることにお気づきでしょうか......あなたをテストしていたんです｡
だから､ ここでブレークしているから実行しても､ 成功したのに失敗したとプリントアウトされてしまうのです｡
だから､ 前の出力で､ 正しい数字だと気づいたはずです｡
めでたくプリントアウトされました｡
しかし､ その後､ 私は失敗しましたとプリントアウトされ､ それはおかしいと思います｡
だから､ それを解決する方法は､ ブレイクスルーがない､ リターンが欲しいということなんです｡
そして今､ それが戻ってきたとき､ あなたが勝った場合､
39行目にプリントアウトされることはありません｡
失敗したとプリントアウトされることはないのです｡
最後のテストで､ もっと早く気づくべきだったのですが､
ビルドして正しく推測しても､ エラーは表示されないのです｡
だから､ 実行を保存して､ これを実行すると､ 5つの推測ができる｡
10､ それ以下と言うことになります｡
6と言うことで､ 当てました｡
そして､ そこに「おめでとうございます」と書かれていることにお気づきでしょうか｡
私も失敗しましたという余計な出力は出てきません｡
ですから､ プログラムが正しく動作するように､ 30行目にリターンを追加することを確認したいのです｡
もし必要なら､ printfを出力しないように別のif文を追加することができます｡
ここでブレークして､ printfでカウントの推測数をチェックし､
それが5未満かどうかを判断することもできたはずです｡
でも､ そこにちょっとしたバグがあり､ テストで気づいてくれれば､
リターンでは修正されます｡
だから､ きちんとした小さなプログラム｡
ループやすべての判断文､ そして本当に問題解決について多くを学ぶのに役立ったのではないでしょうか｡
願わくば､ 実際にコードを書き始める前にこれを取り出して設計することが､
常に重要なのです｡
お役に立てれば幸いです｡
ソースコードを提供しますので､ 比較して見て､ 解析してください｡
ありがとうございます｡