学習の為に、Javaで石取りゲームを作っているのですが、
この状態から、
自分が取る数に制限をかける事と、
・BufferedReaderを使っているところ
・相手が石を取るところ
・自分が石を取るところ
のメソッドを作りたいのですが、難しくて行き詰まっています。
どうか、優しめに教えていただけると嬉しいです。
それ以外にもおかしいところがたくさんあるかと思いますので、できればそこも教えていただければと思います。
よろしくお願いします。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.Random; public class TakeStone4 { public static void main(String[] args) throws NumberFormatException, IOException { System.out.println("石とりゲームスタート"); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); System.out.println("石山の数を入力してください。"); int stone = inputInt(bufferedReader); System.out.println("一度にとれる石の数を入力してください。"); //取れる数の入力 int take = inputInt(bufferedReader); System.out.println("石山は、" + stone + "個で"); System.out.println("取れる数は、" + take + "個で開始です。"); while(stone > 1) { int partner = takePartner(take, stone); stone= stone - partner; System.out.println("相手は"+ partner +"個取り残り"+ stone +"個です。"); int victory = (take+1)- partner ; System.out.println("勝つには"+victory+"個取りましょう"); System.out.println("自分がとる数を入力して下さい。"); int me = takeMe(bufferedReader,take); stone = stone - me; System.out.println("残り"+ stone +"個です。"); } System.out.println("残り1個で相手に順番を回したのであなたの勝ちです。"); } private static int inputInt(BufferedReader bufferedReader) { int input = 0; while(input == 0) { try { input = Integer.parseInt(bufferedReader.readLine()); }catch( NumberFormatException e){ System.out.println("数字を入力してください。"); }catch( IOException e){ System.out.println("数字を入力してください。"); } } return input; } private static int takePartner(int take, int stone) { Random rand = new Random(); int partner = rand.nextInt(take) + 1 ; return partner; } private static int takeMe(BufferedReader bufferedReader,int take) { int me = take + 1 ; while(me > take) { me = inputInt(bufferedReader); if(me > take) { System.out.println("取れる数を超えています!"); System.out.println("再入力してください。"); } } return me; } }
各メソッドが何をどうしたいのか分かりません.
「こうしたくてこういうコードを書いたが, この箇所でこうなるはずがこうなっている」という風にご説明願えますか.
また, コードは「```」の行で前後を挟む形でご提示ください. 専用の枠内に表示されるようになります.
ご質問は修正出来ます. プレビューがリアルタイムに入力欄の下に出ているはずですので, 確認してみてください.
ありがとうございます!
すぐに修正してみます^ ^
メソッド化もしてみてください. 何が難しいのか, お分かりの範囲で記入してください.
また, コードはコンパイルエラーが無い状態を維持するようにし, 実行も出来ると(動作がおかしくても)なお良いかと思います。
ありがとうございます。
メソッド化を試みています。そのメソッド化する箇所で質問なのですが、
System.out.println("石山の数を入力してください。");
以降で、入力が”数値で1以上の場合"繰り替えす状態にしたく、whileとtryで書いてみたのですが、実行するとwhilenの箇所をすっとばしてしまいます。どうすればよろしいでしょうか。
while の挙動についてはどこまで学習されたのでしょう.
while は条件式が true の間ループします. 初めから false の場合は一度もループしません.
while の実行時, stone の値は幾つになっているでしょうか.
0にしていました。理解いたしました。ありがとうございます。
while(stone >= 1)に変更すると入力するところまでは動いたのですが、次にcatchを確認する為に、文字を入力したのですが
石とりゲームスタート
石山の数を入力してください。
moji
Exception in thread "main" java.lang.NumberFormatException: For input string: "moji"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.base/java.lang.Integer.parseInt(Integer.java:652)
at java.base/java.lang.Integer.parseInt(Integer.java:770)
at TakeStone4.main(TakeStone4.java:16)
というエラーがでました。これについても教えて頂けませんでしょうか。
なぜその例外が発生したか, 発生箇所と意味は何処までお分かりですか.
また, try-catch は何故そこにあるのかはご理解頂いているでしょうか.
ネットでtry catchについて調べ、
stone = Integer.parseInt(in.readLine());
のところでエラーが出た場合に、catchの
System.out.println("数字を入力してください。");
に飛ぶようにしたかったです。
また、テストで”moji"と入力したので、数字ではない旨の例外が発生したと思ったのですが、どうでしょうか。
よろしくお願い致します。
テスト方法はあっていますが, 原因と対処方法が学習されていないようです.
まず, 何を入力させ, 何が発生しうるかを考えます.
数値を入力する場合, 当然文字が入力されてしまう場合があります.
本来はそれらを踏まえて何によって入力を取るかから考えますが, 今回は標準入力からの
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
Integer.parseInt(in.readLine());
というコードを使うということにしましょう.
そして, 何が発生し, それに対してどう対処するかを決めます.
Integer.parseInt(in.readLine());
というコードでは, in.readLine() によって改行までのキー入力が文字列として受け取れ, Integer.parseInt によって数字列が数値に変換されます.
この時, 2種類の例外が発生する可能性があります. readLine からの IOException と, parseInt からの NumberFormatException です. ライブラリのドキュメントには各メソッドがどのような場合にどのような例外を発するかが書かれていますので, それを確認しておくのは基本事項です.
readLine の IOException は, 標準入力からの入力に異常が発生した場合です. あまり無いかもしれませんが, 入力時に ctrl+c 等で発生するかも知れません. 標準入力が失敗した場合, プログラムで出来ることは殆どありません. メッセージを出して終了するべきでしょう.
一方, parseInt の NumberFormatException は対処できます. 発生原因は数字列を渡すべきなのに(変換不能な)文字列だった場合, テストされた 'moji' の入力です.
この場合エラーメッセージを出して再入力させるのは利に叶っています.
では, NumberFormatException を捕らえてメッセージを出すにはどうしたら良いか... try-catch です. catch(NumberFormatException e) によって捕らえられます.
ですが, コードをご確認いただくと, NumberFormatException の catch はありません. catch にない例外が発生すると, 直ちに処理は中止され, 上位のメソッドに伝搬します.
main メソッドの宣言に throws NumberFormatException, ~ という箇所があります. これが参考にされたコードからのコピーなのかコンパイラが入れろと言ってきて入れられたのか分かりませんが, これによって『このメソッドは NumberFormatException を発生させる可能性がある』と宣言し, java システムがそれをcatch, 先の例外のメッセージを表示することになります.
この事は, 他の readLine, parseInt の全てで可能性がありますので, それぞれの箇所で(必要ならそれぞれの方法で)対処するコードを書いておく必要があります.
大変丁寧なご回答、感謝します。
内容を勉強する中で、改善することができました。
検索して調べたのですが、ライブラリのドキュメントの見方がわかりませんでした。
クラスやメソッドが分からなくなった時, 真っ先に見るべきは公式のドキュメントです.
残念ながら完全に全てが記述されているという訳には行きませんので, 補足的に利用者の記事を探す場合はありますが, とにかく基本はドキュメントですので, 読めるようになっておく必要はあると思います.
IOException が発生したら面白そう(例外メッセージの無限ループ
IOException 無限ループを調べてみましたが、対策がわかりません、、
catch( IOException e){
System.out.println("数字を入力してください。");
}
}
return input;
ここを改善しないと、キーボード以外からの入力の場合無限ループになるということでしょうか?
お願い致します。
IOExceotion は入出力時に発生する例外です。
何らかの影響で閉じられてしまった場合、提示されたコードでは例外発生を繰り返します。
IO関連を再構築するか終了することになります。
※ 基本処理を中断(終了)します。
調べると、
e.printStackTrace();
を、IOExceptionのcatchにいれるように書いてあったのですが、意味がわかりません。
教えていただけると嬉しいです。
例外が発生する場合, 殆どはコードの不備(ほぼバグ)であり, 残りは外部的な要因(システムが壊れている等)となります.
どちらにしても, **何が起きたのかを表示(記録)しておかなければ後から検証することができません** ので, 少なくとも e.printStackTrace(); を入れておきましょうということかと思います.
asahina1979 さんの言われているのは, IOException は「数字を入力して欲しいのに文字が入力された」程度の場合には発生せず, それ以外で実質「回復不可能な事象が起きている」ための発生の可能性のほうが高いため, メッセージを表示して再入力にしてもまた IOException が発生し…と無限ループしてしまうだろうということです.
今回の場合, NumberFormatException では再入力しますが, IOException は『入力処理で異常が発生しました』とでも表示して(詳細が調べられるように e.printStackTrace も行っておいて) プログラムを終了するのが良いかと思います.
本当にありがとうございます。
}catch( IOException e){
System.out.println("入力処理で異常が発生しました。");
e.printStackTrace();
System.exit(0);
}
この形でよろしいでしょうか。
形としては合っていると思います.
ただ, これを inputInt に入れてしまうと, exit によっていきなりプログラムが終了する形になり, そのような打ち切り方はあまり好まれません.
下位のメソッドで異常が発生した場合は, それを呼び出し側に通知して, 「呼び出し側で可能ならば対処して貰う」ような形にするのが通例かと思います.
例えば, メソッド呼び出しの連鎖の例として main → takeMe → inputInt と呼ばれて異常が発生したとします.
例外が無かった時代(C 等)は, メソッドの戻り値として異常値を返して呼び出し側に異常発生を伝えることが一般的でしたが, java でも同じように出来ます.
inputInt も takeMe も正常なら正数を返す仕様なので, 異常発生時は -1 を返すことにし, main で takeMe の返り値が -1 なら exit するということです.
=== main メソッド(一部) ===
int me = takeMe(bufferedReader,take);
if(me == -1) System.exit(1); //異常終了時は 1 以上とするのが通例
=== inputInt メソッド(一部) ===
try {
input = Integer.parseInt(bufferedReader.readLine());
}catch( IOException e){
System.out.println("入力処理で異常が発生しました。");
e.printStackTrace();
return -1;
}
=== takeMe メソッド(一部) ===
me = inputInt(bufferedReader);
if(me == -1) return -1;
しかし, 例外を使うと, 帰り値を使わずに異常を上位に伝えられます. それがメソッドの定義についている throws です.
=== main メソッド(一部) ===
int me;
:
try {
me = takeMe(bufferedReader,take);
}catch(IOException e){
System.out.println("入力処理で異常が発生しました。");
e.printStackTrace();
System.exit(1);
}
=== inputInt メソッド(一部) ===
private static int inputInt(BufferedReader bufferedReader) throws IOException {
:
input = Integer.parseInt(bufferedReader.readLine());
※IOException は catch しない → 発生時はそこで処理が中止され, 呼び出し元に戻る.
=== takeMe メソッド(一部) ===
private static int takeMe(BufferedReader bufferedReader,int take) throws IOException {
:
me = inputInt(bufferedReader);
※ここでも IOException は catch しない → 発生時はそこで処理が中止され, 呼び出し元に戻る.
メソッド内で呼び出しているメソッドに throws がある場合, 呼び出す側では try-catch で受け取るか 呼び出し側の宣言で throws を使って例外をさらに呼び出し側に送るかを選択しなければなりません. (しなくていい例外もあります.)
つまり, throws によって, 「このメソッドではこんな異常が発生するかもしれないので, 発生した場合にどう対応するのか考えてくださいね」と言っていることになります. (そしてそのことが readLine 等のドキュメントに書かれています.)
なるほど!!!!!
とてもよく分かりました。
throws IOException {
をつかって上に返して、
main内ではcatchで受け取り、e.printStackTrace();
System.exit(1);
で終わらせる。
ということですね?
ありがとうございます!
あなたの回答
tips
プレビュー