【Java初心者用】数当てゲームを作る(メソッドあり)
if文やwhile文の勉強をすると、「数当てゲームを作る」という練習問題が良く出されます。ゲームを自分で作成することができるので、初心者にとっては楽しくチャレンジすることができます。また単純ですが完成して自分でゲームを実際におこなうと充実感を得ることができます。
しかし、基本的な制御構文を使えば作成することができるのですが、どのように組み合わせればよいのか初心者にとっては最初は難しい問題でもあります。
そこで、今回は「数当てゲーム」の作り方をフローチャートとサンプルプログラムを使って説明します。また、スキルアップを目指して、メソッドを使ったプログラムの作り方についても説明します。
是非、この記事を読んで、プログラムの作り方やメソッドを使う利点などについて確認して下さい。
まず、この記事は以下のような人を対象としています。
対象者・Javaの勉強を始めたばかりの初心者
・while文やbreakの使い方を知りたい人
・キーボードからデータを読み込む方法を知りたい人
・数当てゲームの作り方を知りたい人
・メソッドの作り方を知りたい人
この記事を読むと、次のようなことが理解できるようになります。
この記事を読むとできること・数当てゲームの作り方を知ることができる
・while文の使い方を知ることができる
・キーボードから整数だけを読み込む方法を知ることができる
・メソッドの書き方を知ることができる
Javaで『数当てゲーム』を作りたいのですが、どのようにすれば良いでしょうか?
乱数やwhile文、if文、キーボード入力などの仕組みを組み合わせるとできます!!
凄く難しそうですね…。
そんなことはありません。基本的な知識があれば作成できます。
今回は、『数当てゲーム』の作り方をじっくり解説します。
数当てゲームの仕様
今回作成する『数当てゲーム』は、コンピュータが作った値(答え)をユーザが予想し、キーボードから予想値を入力すると判定結果が表示されるというものです。仕様(ルール)は以下の通りとします。
- コンピュータは1から100までの間の1つの値を乱数で作成する
- ユーザは答えの予想値をキーボードから入力する
- 整数以外のデータが入力された場合は再度入力させる
- 答えと予想値が違う場合、答えが予想値より大きいか小さいかのヒントを表示する
- 何回目の予想値を入力しているか回数を表示する
- 入力は正解するまで何度でもおこなえる
ゲーム性を出すために、答えの範囲は1から100までと少し予想をするのを難しくします。ただし、実際の答えがユーザが入力した予想値よりも大きいのか、小さいのかのヒントを毎回表示するようにします。
今回は正解するまで何度でも予想値を入力できるようにしていますが、「入力は5回まで」というように制限を設定しても良いでしょう。また、最初に100点の持ち点を与えておき、1回入力するたびに減点し、正解した時に何点残っているかなどのランキングを追加しても面白いと思います。
フローチャート
まず、この数当てゲームのプログラムを作成する前に「フローチャート」(アルゴリズム)を考えてみましょう。初心者は何も考えずにとりあえずプログラムを入力してしまいますが、途中で何を入力すれば良いのか分からなくなってしまうことがあると思います。これは、入力する前にきちんとフローチャート(アルゴリズム)を考えていないためです。または、考えてはいても曖昧なままプログラムを入力してしまって、やりたいことが明確になっていない状態です。
初心者にとってフローチャート(アルゴリズム)を考えることは大変な作業ですが、プログラムを作成する場合はきちんと考えておかなければなりません。ある程度、プログラミングスキルが身に付くとわざわざフローチャートとして書かなくても頭の中で明確にフローチャートをイメージすることができるようになります。しかし、初心者は是非入力する前に、フローチャート(アルゴリズム)の作成をおこなうようにチャレンジしてください。そうすれば、プログラミングスキルもどんどん向上していきます。
フローチャート内で赤枠で囲んだ部分が無限ループとなります。今回は正解するまで何度でも予想値を入力できるようにするため、入力回数が何回になるかプログラムを作成する時点では全く分かりません。このように回数が分からない繰り返し処理を実装する場合、一般的に無限ループを利用します。
無限ループは文字の通り処理が永遠に終了しません。したがって、無限ループを利用する場合は必ず無限ループを終了する(抜け出す)ための条件式を記述します。今回、無限ループを終了する条件は「コンピュータの答えとユーザの予想値が一致した時」となります。
サンプルコード
上記のフローチャートを実際にプログラムにしたものが次のコードとなります。当たり前ですが、フローチャートの内容をそのままJavaのプログラムとして記述しているので、それぞれが対応しています。ぜひフローチャートとプログラムを対比させながら確認してみて下さい。
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 |
package kazuate; import java.util.Random; import java.util.Scanner; public class NumberGuess { public static void main(String[] args) { // キーボードの準備 Scanner stdin = new Scanner(System.in); int count = 0; // 入力した回数を格納する変数 // コンピュータの答えを作成 int comAnswer = new Random().nextInt(100)+1; while (true) { try { // ユーザの予想値をキーボードから入力 count++; // 入力した回数を1加算 System.out.println(count+"回目:予想値(1~100)の入力"); // キーボードから整数としてデータを読み込み変数に保存 int userGuess = stdin.nextInt(); // 答えと一致するかどうかのチェック // 答えの方が予想値より小さい場合 if (comAnswer < userGuess) { System.out.println("もっと小さな値です"); // 答えの方が予想値より大きい場合 } else if (comAnswer > userGuess) { System.out.println("もっと大きな値です"); // 上記の条件以外(答えと予想値が等しい場合) } else { System.out.println("\n正解です!!"); break; // 無限ルールの終了 } } catch (Exception e) { // 整数以外のデータが入力された場合 System.out.println("【エラー】予想値は数値を入力してください"); // 入力されたデータの除去 stdin.next(); } } } } |
コードの説明
コードの説明ですが、各行にコメントを記載しています。そのコメントを読んでもらえばその行で何をおこなっているのか理解できるようにしています。
なお、実際仕事でプログラムを作成する場合は、このようなコードを書くことは推奨されていないことが多いです。何故かというと、コードの内容をそのままコメントにすると確認する際に二度手間になるためです。プログラミングのスキルがあればコードを見ただけで何をおこなおうとしているのか理解できます。そのため、わざわざコードを読めばわかることはコメントに書いてしまうと、コメントを読む時間が余計にかかってしまいます。
一般的にコメントには「what(何をしているか)ではなく、why(何故そのコードなのか)」を書けばよいとされています。プログラマが他のプログラマに対して、そのコードをどのような理由で記述したのか、何故その値を使っているのかなどの情報を提供するためにコメントを記述すべきです。
では、コードの解説を要点のみ取り上げておこないます。
15行目 int comAnswer = new Random().nextInt(100)+1;
この行では1から100の間の乱数を1つ生成し、変数に代入しています。乱数を作成する場合、nextInt()メソッドの引数に指定する値に注意しなければなりません。100までの範囲なので、nextInt(100)と記述したくなりますが、これでは0から99までの範囲でしか乱数は作成されません。nextInt()メソッドは「0から引数に指定した値-1の範囲」で乱数を作成します。「0から始まる」点に気を付けなければなりません。
今回の問題では「1から100まで」となっているため、nextInt()メソッドの「0から始まる」という部分を自分で「1から始まる」というように加工しなければなりません。それが、nextInt(100)+1の「+1」部分となります。nextInt(100)で「0から99まで」としておき、「+1」することによって「1から100まで」に加工することができます。このようにプログラマには与えられたメソッドを自分で加工して目的の処理をおこなえるようにする工夫が求められます。
18行目から41行目までの try~catch文
22行目の「nextInt()メソッド」でキーボードで入力されたデータを整数として読み込みんでいます。しかし、「abc」や「10a」のように整数として扱えないデータが入力される場合があります。このような整数以外のデータが入力された場合、22行目で例外が発生します。そこで、このように例外が発生する可能性がある処理をtry文で囲み、例外が発生した時の処理(対策)をcatch部分に記述しておきます。これで、ユーザが間違えて整数以外のデータを入力してもプログラムを終了させずに対応させることができます。
プログラムを作成する場合は、正しいデータが入力されることを前提にせず、間違えたデータが入力された場合にどうするかということを考えてコードを記述するようにしましょう。
26行目から35行目 答えと予想値が一致するかの判定
変数comAnswerには答え、userGuessにはユーザの予想値がそれぞれ代入されているので、2つの変数の大小、一致をif文を利用してチェックしています。また、一致しない場合はヒントをそれぞれ表示しています。
2つの値が一致した場合(32行目)、「break」を実行して無限ループを終了しています。なお、無限ループは17行目の「while(true)」のように、whle文の条件式に「true」を指定して実現しています。またfor文を利用して無限ループをおこないたい場合は「for(;;)」と記述します。どちらを利用しても構いませんが、一般的にwhile(true)の記述の方が多いでしょう。
40行目 stdin.next()
22行目のキーボードから入力されたデータを整数として読み込む場合、整数以外のデータが入力されると例外が発生し、38行目と40行目の処理が実行されます。整数として読み込みができない場合、新しいデータをキーボードから入力してもらうのですが、この40行目の処理がなければ38行目と40行目が永遠と繰り返し処理されます。実際に40行目をコメントアウトして確認してみて下さい。
キーボードから入力したデータはすぐにプログラム内に読み込まれる訳ではありません。一度、バッファと呼ばれるメモリ内に保存されます。nextInt()はこのバッファからプログラム内に整数として読み込みます。したがって、例えば「abc」とキーボードから入力した場合、まず「abc」というデータはバッファ内に保存されます。次にnextInt()メソッドでプログラム内に読み込もうとしますが、整数ではないため読み込むことができず、例外が発生します。つまり、入力したデータ「abc」はバッファ内に残ったままとなります。
stdin.next()はバッファ内のデータを文字列として読み込むメソッドで、これはとにかくバッファからデータを確実にプログラム内に読み込みます。このメソッドを実行すると、入力された整数以外のデータをバッファから除去することができます。
ここでの注意点は、next()で読み込んだ文字列データを変数に保存していないことです。変数に保存していないので、読み込まれたデータは消えてしまいます。変数に代入しても構いませんが、読み込んだデータはプログラム内では使用しないので、そのような場合は変数にあえて保存せず捨ててしまうと良いでしょう。
これでバッファが空になったので、22行目に戻った時に新しくデータをキーボードから入力できるようになります。40行目の処理を記述しない場合、再度新しいデータを入力してもらうために22行目に戻っても、バッファに直前に入力したデータ(abc)が残っており、このデータは整数として読み込めないため再度例外が発生します。新しいデータを読み込むことができずに、例外は永遠と繰り返し発生してまいます。
実行結果
実行した結果は次の通りです。
メソッドを使ったサンプルコード
上記のサンプルプログラムは目的の処理をおこなっているので正しいプログラムです。しかし、全ての処理がmain()メソッド内に記述されているため、プログラム全体の見通しが悪くなっています。
このサイトでも何度も話をしていますが、プログラムを作成する場合はmain()内にはなるべく詳細な処理を記述せず、全体が見通せるようにメソッドだけを記述するほうが良いでしょう。
以下のプログラムがメソッドを使った場合のサンプルプログラムです。もちろん、このサンプル以外にもメソッドを使ったプログラムの書き方は存在するので、ひとつの参考として確認して下さい。
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
package kazuate; import java.util.Random; import java.util.Scanner; public class NumberGuessMethod { public static void main(String[] args) { // コンピュータの答えを取得 int comAnser = getComAnswer(); // 正解するまで何度でも繰り返す while (true) { // ユーザの予想値の取得 int userGuess = getUserGuess(); // 正解/不正解のチェック boolean judge = isJudgeAnswer(comAnser, userGuess); // 正解の場合 if (judge) { break; // 処理の終了 }else { // 不正解の場合 // 何もしない(次のループ) } } } // コンピュータの答えを返すメソッド public static int getComAnswer() { // 作成した乱数を返す return new Random().nextInt(100)+1; } // キーボードから入力された数値(予想値)を返すメソッド public static int getUserGuess() { int guess; // キーボードの準備 Scanner stdin = new Scanner(System.in); while (true) { try { System.out.println("予想値(1~100)の入力"); // キーボードから予想値を読み込む guess = stdin.nextInt(); break; // 整数として読み込めたので無限ループの終了 } catch (Exception e) { System.out.println("予想値は整数を入力してください"); stdin.next(); } } return guess; // 読み込んだ整数を返す } // 答えと予想値が一致するかどうかを判定するメソッド public static boolean isJudgeAnswer(int com, int user) { if (com < user) { // 答えの方が小さい場合 // ヒントの表示 System.out.println("もっと小さな値です。"); // 不正解なのでfalseを返す return false; } else if (com > user) { // 答えの方が大きい場合 // ヒントの表示 System.out.println("もっと大きな値です。"); // 不正解なのでfalseを返す return false; } else { // 答えと予想値が一致した場合 System.out.println("\n正解です!!"); // 正解なのでtrueを返す return true; } } } |
基本的に初めに掲載したサンプルプログラムを処理ごとにメソッドに分けて記述をしています。プログラムの行数を比較すると、コメントの行数などの違いはあるのですが、メソッドに分けた方が1.5倍以上と多くなっています。行数だけ見ると、逆に多くなっているのでメソッドに分けない方が良いのではないか、と考えるかも知れません。
しかし、メソッドに分ける利点は「メソッドごとにおこなう処理が分けられている」ことです。乱数を作成する内容を修正する場合は、乱数を作成するメソッド部分だけを修正すれば良く、他のコードは一切修正する必要がありません。また、メソッドに分けているので、修正する必要がある場合はどこを修正すれば良いのか簡単に見分けることもできます。
このようにメソッドに分けることによって、修正が発生しても対応しやすくなり、コードが明確になるためミスを防ぐこともできます。行数が長くなっても、メリットの方が大きいと言えます。
また、main()メソッド内には実行するメソッド名のみが記述されているので、プログラム全体でどのような処理をおこなうのか全体像を確認することができます。main()メソッドでは個々の詳細内容を知る必要はありません。詳細内容を確認する必要がある場合は、別途記述されているメソッドを参照すれば良いことになります。
メソッドを利用する場合、メソッド名を見ればおおよそどのような処理がおこなわれるのかが推測できるように、適切なメソッド名をつけるように心がけて下さい。一般的には「動詞+名詞」の形式でメソッド名を定義します。なお、boolean型のデータを返すメソッドの場合、通常「is」で始まるメソッド名を付けます。
各コードのについては、先に掲載したサンプルプログラムと同じなので説明は省略します。
まとめ
今回は、『数当てゲーム』の作り方について説明しました。
単純なゲームを作りながら、Java言語の基本的な制御構文(fi文やwhile文、break文など)やキーボード操作、例外処理などを確認することができました。初心者はすぐには自分でゼロから作成することは難しいと思いますので、まずはこのサンプルを確認してみて下さい。とにかくプログラムの勉強をしたい場合は他人のプログラムを参考にすることです。参考にした後は、是非自分でカスタマイズしてみて下さい。最初にも書いていますが、入力できる回数を5回までに制限したり、点数を付けたりするためには、これらのプログラムをどのように修正すれば良いのか、自分で工夫してみましょう。
・不特定回数処理を実行したい場合は、無限ループを利用する
・無限ループを利用する場合は必ず終了条件をif文などで記述する
・Randomクラスで乱数を作成する場合は引数の値に注意する
・キーボードからデータを読み込む場合、不適切なデータが入力された時の処理を実装する
・プログラムはmain()メソッドに詳細をなるべく記述せず、メソッドを活用する
・メソッドは処理ごとに分けて記述する
数当てゲームの作り方はいかがでしたか?
基本的な制御構文だけで記述されていたので、それぞれの内容は理解できました!!
ただ、自分でゼロから作れるかはまだ自信がありません。
最初は大変ですが、やはりフローチャートを書く練習をした方が良いですよ。
他の人が作ったプログラムからフローチャートを書くのも良い練習になります。
これからはフローチャートをまず書くように心がけます!!
自分でも色々なゲームが作れるように頑張ります!!