Javaによるミニゲーム作成 ～数あてゲーム～

どうもお疲れ様です。きうちです。

日々「おもしろいことはないかな～」と様々な技術をかじってみたり（そして基本的には食い散らかしｗ）、くだらないジョークソフトを作ったりしてすごしています＾＾

今回は、お仕事とはちょっと離れて、小難しい技術情報ともちょっと離れて、「Javaで簡単なミニゲームを作ってみよう！」というコンセプトでお話します。

ミニゲームといっても、グラフィックを使うやつだと紙面をとりそうなので、ひとまず古式ゆかしい「数あてゲーム」の作り方を解説してみたいと思います！

##１．数あてゲームとは？

数あてゲーム、知ってますか。簡単に言うと、「『コンピュータがランダムに考えた数字』を、回答とそれに対するヒントにより当てて行く」というものです。

ヒントは、「大きい」「小さい」の2種類です。

例えばコンピュータが「808」という数字を考えた場合。この時点ではもちろんプレイヤーはその数字を知りません。
その状態で「500」と回答すると、ヒントとして「小さい」が与えられます。
逆に「900」と回答すると、「大きい」が与えられます。

これを利用して、10回以内にコンピュータの考えた数をあてます。

![数あて_イメージ.png](https://qiita-image-store.s3.amazonaws.com/0/60722/4ef61aa7-50d8-9b01-b01a-e7ee8acb8064.png)

##２．キー入力の受け付け

Javaでゲームを作る場合、Swingかアプレットを使うと見栄えのするゲームが作れますが、ここでは簡単のため、標準入力から（つまり、キーボードから）数字を打ち込むとゲームが進むようなものを考えます。

ってことは、キー入力の受付が必要ですね。

その場合、どうやるかっていうと、「System.in」というやつを使います。
Javaをご存知の方の中には「あれ？」って思った方もいらっしゃるでしょうか。「System.out」はそれこそHelloWorldに始まってよくご覧になる／ご利用になると思いますが、そう、「アウト」ではなく「イン」を使うんです。

ちなみにSystem.inの正体はInputStreamクラス（の派生クラス）のインスタンスです。
これをそのまま扱うのはめんどっちいので、BufferedReaderをカマせて使います。

```java
BufferedReader br
    = new BufferedReader(new InputStreamReader(System.in));
```

という風に。

では、次のようなものを作ってみましょうか。

+ 入力を促す文字列を表示する。
+ 入力された文字列を読み取って表示する

```java:Kazuate.java　(文字列入力を受け付けて表示するところまで)
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Kazuate {

	public static void main(String[] args) {
		System.out.print("何か入力してください > ");
		String inputValue = getStringInput();
		System.out.println(inputValue + " と打ち込まれました!");
	}

	private static String getStringInput() {
		BufferedReader br = null;
		String line = null;

		try {
			br = new BufferedReader(new InputStreamReader(System.in));
			line = br.readLine();
		} catch(IOException e) {
			e.printStackTrace();
		}

		return line;
	}
}
```

こんな風にすると、入力を受け付けることができます。
ちなみに上記のようにコーディングしておくと、入力がなされてENTERキーが押されるまでプログラムの処理は待機状態になります。

![WS000033.PNG](https://qiita-image-store.s3.amazonaws.com/0/60722/e1afa175-39ff-0f36-06c3-d63b90649e8e.png)

余談ですが、System.inはあくまで「標準入力」ですから、キーボードから打つ以外にも、パイプを使って他のプログラムが出力した文字列を読み取って処理する、といったようなプログラムにも応用できます！

★　★　★

ゲームは数字の入力を必要としていますから、次のような処理が必要になりますね。

+ 入力を受け付ける
+ 入力値が数値だったら次へ。そうでなかったらもう一回入力をしてもらう。

このために、前述のプログラムに「getNumberInput」というメソッドを追加します。
それと、mainメソッドを書き換えてみます。

```java:Kazuate.java　(getNumberInputの追加とmainの修正)
	public static void main(String[] args) {
		System.out.print("数値を入力してください > ");
		int inputValue = getNumberInput();
		System.out.println(inputValue + " が打ち込まれました!");
	}

	private static int getNumberInput() {
		int userAns = -1;

		do {
			String s = getStringInput();
			try {
				userAns = Integer.parseInt(s);
			} catch(NumberFormatException e) {
				System.out.print("もう一度入力してください > ");
			}
		} while(userAns == -1);

		return userAns;
	}
```

今度は、「this is a pen.」とか打っても「もう一度入力してください > 」と表示されて再度の入力が促されます。

![WS000034.PNG](https://qiita-image-store.s3.amazonaws.com/0/60722/25792d81-335d-aefa-387a-34170316fa3b.png)

##３．数あてゲームにしていこう！

さてさて、ここまでで下ごしらえは終わりですね。いよいよ本番。
次のようなカンジで作りましょう。

	(1)0～999のランダムな数字を作り、保持
	(2)プレイヤーに数字を入力させる
	(3)(1)の数字と入力された数字を比較し、結果を表示
		◆等しい⇒「正解！」
		◆(1)の数字 < 入力値 ⇒ 「大きすぎます。」
		◆上記以外 ⇒ 「小さすぎます。」

```java:Kazuate.java　(数あてゲームらしくする)
	public static void main(String[] args) {
		Random random = new Random();
		int ans = random.nextInt(1000);
		System.out.print("数値を入力してください > ");
		int userAns = getNumberInput();
		System.out.printf("\t%d ..... ", userAns);
		if (ans == userAns) {
			System.out.println("正解！");
		} else if (ans < userAns) {
			System.out.println("大きすぎます。");
		} else {
			System.out.println("小さすぎます。");
		}
	}
```

あ、java.util.Random をimportするのもお忘れなく。

こうすると、実行したらこんな風になるはずです。

![WS000035.PNG](https://qiita-image-store.s3.amazonaws.com/0/60722/31f301d2-f758-f52a-3b33-e21828d7a8c5.png)

…一回入力したらそこで終わっちゃいますね。
まさか、一発で当てるなんて無理っす！

そこで、カウンタ変数を追加して、それが、一回回答するごとに増えていって、while文かなんかで繰り返すようにしましょか。

```java:Kazuate.java　(繰り返すようにする)
	public static void main(String[] args) {
		Random random = new Random();

		int ans = random.nextInt(1000);
		int count = 0;

		while(count < 10) {
			System.out.print("数値を入力してください > ");
			int userAns = getNumberInput();
			System.out.printf("\t%d ..... ", userAns);
			if (ans == userAns) {
				System.out.println("正解！");
			} else if (ans < userAns) {
				System.out.println("大きすぎます。");
			} else {
				System.out.println("小さすぎます。");
			}
			count++;
		}
	}
```

実行すると、次のようなカンジになります。

![WS000036.PNG](https://qiita-image-store.s3.amazonaws.com/0/60722/4beb1461-0815-c63d-e400-efc8e7d2e767.png)

いかがでしょう。これなら、何回でもトライできますね！
while文の条件として count < 10 としましたんで、10回答えたらそこで終了です。
20回にしたければ、count < 20 ってすればいいです。

これでもう、当初の計画通りなカンジにはなっています。

##４．ゲームとして完成させよう！

でも、実はこれだけだと不備があるんです。

例えば、「10回に到達する前に正解した！」場合でも、そのまままた「数値を入力してください > 」と、何事も無かったかのように入力を促されてしまいます。

それから、10回やっても正解できなかった場合。上記のコードだと、その場合は何もおきずに終わります。ちょっと不親切ですね。正解はなんだったんだよう？
ってことで、ゲームオーバーの場合は正解を表示するようにしましょうか。

あと、「今何回目の回答かな？」というのが分かるとうれしいですね。
入力を促す表示をちょっと変えましょう。


```java:Kazuate.java　(細かい部分の調整)
	public static void main(String[] args) {
		Random random = new Random();

		int ans = random.nextInt(1000);
		int count = 0;
		int userAns = 0;

		System.out.println("0～999の数字を入力してください。");

		while(count < 10) {
			System.out.printf("%d> ", count + 1);
			userAns = getNumberInput();
			System.out.printf("\t%d ..... ", userAns);
			if (ans == userAns) {
				System.out.println("正解！");
				break;
			} else if (ans < userAns) {
				System.out.println("大きすぎます。");
			} else {
				System.out.println("小さすぎます。");
			}
			count++;
		}

		if (ans != userAns) {
			System.out.println("Game Over! 正解は " + ans + " でした。");
		}
	}
```

どうですか。これでもう完璧ですよ！
毎回毎回回答回数が表示されるし、途中で正解するとbreakで抜けるし、10回回答した後正解していなかったら、正解が表示されるし！

![数あて.png](https://qiita-image-store.s3.amazonaws.com/0/60722/2ddbb244-e028-2b56-93a9-78255bc1f569.png)

そんなわけで、繰り返し遊んでみてください＾＾

##５．改良してみよう！

ここまで読んでくださったあなた。
ぜひ、以下のような改良にチャレンジしてみてください！

	(1) 正解またはゲームオーバーしたら、次のゲームが始まる。
	(2）始まる前に成績が表示される。
		・当てるのに成功した回数／トータルの回数
		・成功率
		・最小回答数

それから、今回の内容を応用すると、コンピュータと勝負するじゃんけんゲームとかしりとりゲームとかも作れますね！

以上、きうちでした。それではまた！

###2014/12/9追記
.NET版(C#とVB.NET)およびC言語版も載せました！
よかったらこちらもどうぞ。
http://qiita.com/kiuyas/items/374b2c8d334784f9f995

###2014/12/9さらに追記
ゲームオーバー判定で「count == 10 && 」が不要というコメントをいただきましたので、そのように修正しました。