はじめに
ミニマムなゲーム作りをしたいということで、Linuxを触りながらJavaを使ってシンプルなじゃんけんゲームを作成しています。
前回までに、プレイヤーとCPUの手を比較して勝敗を判定する機能や、y/nによる再戦機能を実装しました。
今回は、ゲームのエラー解決と保守性を意識して、入力処理機能とゲーム進行および勝敗判定の2ファイルに分割していきます。
分割については入力処理をInputHandler.javaという別クラスに分離していきます。
前提事項
前提事項としては以下のとおりです。
使用環境・ツール
OS:Windows11 Home
CPU:Intel
Linux環境:Ubuntu
OpenJDK 21
補助ツール
Chat GPT
AIにも慣れておきたいなぁ、ということで使用しています。
学習目的のため、実行するコマンドをただ提示させるのではなく、コマンドを実行する理由やコマンドの意味もをあわせて提示するように指示しました。
※念のため実際に説明や解釈が合っているか調べて確認しています。
その他
この記事内では、実行コマンドの下にコマンドの意味を記載しています。
今回の実装内容
今回実装した内容は以下のとおりです。
- 入力処理をInputHandler.javaに分離
- プレイヤーの手入力時に、1~3の範囲で入力チェックする
- プレイヤーの手入力時に、範囲外は再度入力を求める
- y/nによる再戦入力のチェック
- Y/Nの大文字入力にも対応
現状の課題
現状は、想定範囲内の操作であればゲームとして遊べますが、想定外の入力については3つの課題があります。
課題1.プレイヤーの手が1~2以外はすべてパーとして処理される
これまではプレイヤーの入力について、条件分岐で「1=グー、2=チョキ、それ以外をパー」として処理していました。
static String getHandName(int hand) {
if (hand == 1) {
return "グー✊";
} else if (hand == 2) {
return "チョキ✌";
} else {
return "パー✋";
}
}
そのため、実は「1~2」以外の数値がが入力された場合、すべて「パー」として処理されるようになっていました。
エラーではないものの、想定した動作ではないため修正していきます。
課題2.プレイヤーの手を入力時に文字列が入力されると、致命的エラーになる
数字ではなく「a」のような文字列を入力すると、nextInt()では整数として読み取れず、エラーが発生し続行不可能になってしまいます。
致命的エラーのため、要修正となります。
課題3.再戦についてy以外の入力はすべてゲーム終了として処理される
再戦するかどうかの確認では
while (playAgain.equals("y")) {
とすることで、「playAgainがyである間はゲームを繰り返す」
という処理を作りましたが、「y」以外の入力、つまり「Y」でもゲーム終了として処理してしまうようになっていました。
これはプレイヤーを困惑させてしまう部分かなと思います。
このように課題が3つあるため、今回は正しい入力が行われるまで入力を繰り返す仕組みを実装しました。
入力チェック・処理を担当するファイルを作成する
入力処理をゲーム本体から分離するため、新しくInputHandler.javaを作成しました。
ファイル構成は以下のとおり。
jankengame/
├ Janken.java ←ゲーム進行と勝敗判定を実行する
└ InputHandler.java ←プレイヤーの入力受付処理・チェックを実行する
Janken.javaではゲームの進行や勝敗判定を担当し、InputHandler.javaではキーボード入力の受付と処理、入力チェックを担当させます。
これにより、ゲームの進行に関するコードと、入力に関するコードを分けて管理できるようにしました。
InputHandler.javaの全コード
import java.util.Scanner;
public class InputHandler {
private Scanner scanner = new Scanner(System.in);
// プレイヤーの手を入力する
public int getPlayerHand() {
while (true) {
System.out.println("番号を入力してください");
System.out.println("1: グー");
System.out.println("2: チョキ");
System.out.println("3: パー");
String input = scanner.next();
// 数字かどうかを確認
if (input.equals("1") || input.equals("2") || input.equals("3")) {
return Integer.parseInt(input);
}
System.out.println("1~3の数字を入力してください。");
}
}
// 再戦するかどうかを入力する
public boolean getPlayAgain() {
while (true) {
System.out.println("もう一度遊びますか?(y/n)");
String answer = scanner.next();
if (answer.equalsIgnoreCase("y")) {
return true;
} else if (answer.equalsIgnoreCase("n")) {
return false;
}
System.out.println("y または n を入力してください。");
}
}
// Scannerを閉じる
public void close() {
scanner.close();
}
}
課題1と2を解決する
まずは、プレイヤーの手を入力する処理をInputHandler.javaに実装していきます。
そして、課題1の1~2以外が入力されるとすべてパーとして処理されてしまう問題をInteger.parseInt()を用いて解決します。
課題1の解決のカギはInteger.parseInt()
public int getPlayerHand() {
while (true) {
System.out.println("番号を入力してください");
System.out.println("1: グー");
System.out.println("2: チョキ");
System.out.println("3: パー");
String input = scanner.next();
if (input.equals("1") || input.equals("2") || input.equals("3")) {
return Integer.parseInt(input);
}
System.out.println("1~3の数字を入力してください。");
}
}
今回のポイントは、while (true)とreturnを使っていることです。
while文によって、正しい入力がされるまで処理を繰り返します。
そしてtrueになる、つまり入力された値が「1」「2」「3」のいずれかであれば、Integer.parseInt()でint型に変換し、その値を呼び出し元へ返します。
一方、それ以外の値が入力された場合はfalseと判定し、エラーメッセージを表示して再び入力を求めます。
課題2の解決のカギはscanner.next()
今回nextInt()ではなくnext()としたことで、プレイヤーの入力を文字列として受け取っています。
これにより、数字以外の文字列が入力されても、整数への変換を行う前にチェックできるようになり、致命的エラーを解決しました。
再戦するかどうかの入力チェック
次に、再戦確認の入力処理もInputHandler.javaにまとめます。
public boolean getPlayAgain() {
while (true) {
System.out.println("もう一度遊びますか?(y/n)");
String answer = scanner.next();
if (answer.equalsIgnoreCase("y")) {
return true;
} else if (answer.equalsIgnoreCase("n")) {
return false;
}
System.out.println("y または n を入力してください。");
}
}
課題3の解決のカギはequalsIgnoreCase()
文字列を比較するときに大文字と小文字を区別しないメソッドequalsIgnoreCase()を使用していきます。
使用することで「y」だけでなく「Y」、「n」だけでなく「N」も受け付けることができます。
また、y/n以外が入力された場合は、再度入力を求めるようにしました。
Janken.javaから入力の処理結果を呼び出す
入力処理を別クラスのInputHandler.javaに分けたので、Janken.java側から処理結果を呼び出すため次のように変更しました。
import java.util.Random;
public class Janken {
//途中省略
public static void main(String[] args) {
Random random = new Random();
// 入力処理を担当するオブジェクトを作成
InputHandler inputHandler = new InputHandler();
boolean playAgain = true;
while (playAgain) {
// プレイヤーの手を取得
int playerHand = inputHandler.getPlayerHand();
//途中省略
// 再戦するかどうかを取得
playAgain = inputHandler.getPlayAgain();
}
// ゲーム終了時にScannerを閉じる
inputHandler.close();
System.out.println("ゲームを終了します。");
}
}
InputHandlerのオブジェクトを生成し、getPlayerHand()を呼び出すことで、入力された手を取得できます。
再戦確認も同様です。
playAgain = inputHandler.getPlayAgain();
これにより、Janken.java側で入力値のチェックを直接記述する必要がなくなりました。
なお、Scannerの終了処理もInputHandler側にまとめ、ゲーム終了時にinputHandler.close()を呼び出すようにしています。
動作確認
実装後、以下のコマンドでコンパイルしました。
javac Janken.java InputHandler.java
続いて、ゲームを実行します。
java Janken
以下の項目について動作確認を行いました。
・1~3を入力して、正常にじゃんけんができる
・4や0を入力すると、再入力を求められる
・abcを入力してもエラーで終了しない
・yを入力すると再戦できる
・YやNの大文字入力にも対応している
・nを入力するとゲームが終了する
すべての項目で想定どおりの動作を確認できました!
※少しカジュアルさを出したくて「あそぶ!」「やめる!」やめたときに「またね!」と表示するようにしています。
まとめ
今回は、じゃんけんゲームに入力チェック機能を追加し、入力処理をInputHandler.javaに分離しました。
これまでJanken.javaにまとめていた処理を役割ごとに分けることで、コードの見通しを改善することができました。
また、入力値をチェックすることで、想定外の入力があってもゲームがすぐに終了しない仕組みを実装できました。
そして、今回の制作を通してwhile文による繰り返し処理や、クラスごとに役割を分ける設計について学ぶことができました。
これで最低限のゲームとして成立するようになりました。
今後は、これをWeb上でプレイできるようにするか、あるいはまた別の新しいミニゲームを作ってみようと思っていますので、こうご期待!


