質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.36%

Java:「じゃんけんゲーム」でコンパイルしてもスコアが反映されない状態です。ソースコードでおかしい点は、どこにありますでしょうか?

受付中

回答 4

投稿

  • 評価
  • クリップ 0
  • VIEW 2,242

score 9

失礼いたします。

今社内の研修でJavaを使用して「じゃんけんゲーム」を制作しております。

処理内容はこちらです。
・playerとCPUの2人でじゃんけんをする。
・playerは「1:グー、2:チョキ、3:パー」で整数を選択し、CPUは出目をランダム表示。
・先に3勝した方の勝ちとする
・2連勝したら-1ptされる ※

※2連勝したら-1ptされる

「2連続で勝利した場合、同時にその方に-1pt」
例えば、
1回戦目 playerが勝ち:CPUが負け スコア【1pt:0pt】
2回戦目playerが勝ち:CPUが負け スコア【2pt:0pt】
しかしここでplayerが2連勝なので、-1pt スコア【1pt:0pt】
3回戦目



というような流れの処理となる。

2連勝して-1ptの処理が発生した後は、連勝のカウントをリセットし、続きを始めます。

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

本題なのですが、上の処理内容で「2連勝したら-1ptされる 」を加えたところでコンパイルすると
エラーは起きないのですがスコアが表示されません。
結果が「あいこ」になった時のみスコアは表示されるみたいです。

下記のソースコードの「//勝ち負け判定」の場所にある
System.out.println("player" + playerPt + "対cpu" + cpuPt);
if(playerPt >= 3) {

System.out.println("playerの・・・");
break;
} else if(cpuPt >= 3) {

System.out.println("playerの・・・");
break;

}
この部分がスコアにあたるコードです。
このスコアの処理の上に「2連勝したら-1ptされる」の処理を打ち込んでいて
おそらくはそこからの干渉・不備が原因ではないかと考え、 break;を入れる事も試したのですが、違うみたいでした。
じゃんけんの動作は正常に動いているので、スコアが表示され、なおかつ「2連勝したら-1ptされる」の処理も反映されるようにするには
どこ部分を修正すればよいのでしょうか?

プログラミング初心者ということもあり、理解に時間が掛かったりもありますが、出来るだけ分かり易くお願いできればと存じます、、

宜しくお願い致します。

■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

package janken;    

import janken.Enums.ErrorInfo;
import janken.Enums.S手;
import janken.Enums.S結果;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/**
* じゃんけん実行クラス
*/
public class ExeJanken {

    /**
    * じゃんけん実行
    *
    * @param args
    */

    public static void main(String[] args) {

        S結果 result = null;
        int playerPt = 0;
        int cpuPt = 0;
        int lastWinner = 0;

        //勝敗が決まるまで実施
        while(true) {

            // Playerの手を取得
            S手 playerHand = readPlayerHand();
            if(playerHand == null) {

                // 未入力で終了
                return;

            }

            // 手をセット
            Janken janken = new Janken(playerHand);    

            // 手を表示
            System.out.println(" → " + janken.getInputHand());

            // 勝ち負け判定
            result = janken.judge();

            if(result == S結果.あいこ) {

                System.out.println("あいこでしょ!");
            } else if (result == S結果.勝ち) {

                System.out.println("playerの勝ち");
                playerPt ++;

            } else if (result == S結果.負け) {

                System.out.println("cpuの勝ち");
                cpuPt ++;

            } else {
                break;

            }
            if(result == S結果.勝ち) {

                lastWinner = 1;

            }


            if(lastWinner == 1 && result == S結果.勝ち) {

                playerPt--;
                lastWinner = 0;

            }
            if(result == S結果.負け) {

                lastWinner = 2;

            }
            if(lastWinner == 2 && result == S結果.負け) {

                cpuPt--;
                lastWinner = 0;

            }

            System.out.println("player" + playerPt + "対cpu" + cpuPt);
            if(playerPt >= 3) {

                System.out.println("playerの・・・");
                break;
            } else if(cpuPt >= 3) {

                System.out.println("playerの・・・");
                break;

            }
        }

        // 結果を表示
        outputResult(result);
    }

    /**
    * Playerの手を入力
    *
    * @return 入力した手
    */
    private static S手 readPlayerHand() {

        // 標準入力を1行バッファ
        BufferedReader input = new BufferedReader(new InputStreamReader(System.in));

        // 正しい値が読み込まれるまでループ
        String line = null;
        while(true) {

            System.out.print("じゃんけん[1:グー、2:チョキ、3:パー ※未入力Enterで終了] : ");

            try {

                // 入力文字列を取得(1行)
                line = input.readLine();
                if(line.trim().isEmpty()) return null;

                // 入力チェック
                ErrorInfo errInfo = validate(line);
                if(null != errInfo){

                    System.out.println(errInfo.getMsg());
                    continue;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            break;

        }

        // Playerの手を返却
        return S手.getEnum(Integer.parseInt(line));
    }

    /**
    * 入力チェック
    *
    * @param inValue チェックする文字列
    * @return エラー情報 正常の場合:null、異常の場合:エラー情報
    */
    private static ErrorInfo validate(String inValue) {

        //桁数チェック
        if(inValue.length() != 1) return ErrorInfo.LENGTH_ERROR;

        //入力文字チェック
        if(!inValue.matches("[123]")) return ErrorInfo.INVALID_VALUE;

        return null;
    }

    /**
    * 結果出力
    */
    private static void outputResult(S結果 result) {

        System.out.println(result.name() + "!!!");
    }
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • yukihisa

    2018/09/20 13:49

    大事なのが、「ここで質問をする」というインプットに対して、「回答(アウトプット)がなされるまでの間何をするのか」です。現状の流れを見ていると画面の前でアラートが出るまでぼけーっと待っている様な気がしてなりません・・・というか、現場では作業が終わらないうちから別作業をするなんて新人の内は混乱のもとになるので、そうせざるを得ない状況になりません?その辺り研修の監督(上司?先輩?)はどうお考えなのか非常に興味があります。。。

    キャンセル

  • m.ts10806

    2018/09/20 13:53

    目指すのはteratailの質問者ですか?エンジニアですか?

    キャンセル

  • m.ts10806

    2018/09/20 13:54

    言われたことを言われるままにするのはエンジニアではないですよ。

    キャンセル

回答 4

+11

 最初に

直接的な回答にはならないかもしれませんが、
今後のためを見据えての内容を意識して書きます。

 伝えたいこと

上の処理内容で「2連勝したら-1ptされる 」を加えたところでコンパイルすると
エラーは起きないのですがスコアが表示されません。

デバッグを覚えるべきだと思います。

端的に言うと「バグを取り除く作業」のことです。

もう少し具体的に言うと
「ブレークポイント」を使うことでプログラムを一時停止することができる。
一時停止中は、変数やフィールドの中身を見ることができ、
さらに値を変更することもできる。
という代物です。

 なぜデバッグを覚えるべきか?

エラーを知るには
前提としてプログラムの動きを理解する必要があります。

ここでポイントなのは、
人の話から直接エラーを知ることはできないということです。

あくまでそれは考えるきっかけを与えているだけで
それを元に修正しているに過ぎないだけです。

結局、修正するにしても
プログラムの動きを理解していないと意味が無いわけです。
プログラムの動きを理解してやっとエラーが知れる訳です。

くどい言い回しですみません。

とにかく、ヒューマンエラーは完全に防げないですし
今ここに書いてる内容だって嘘が入ってるかもしれないです。
ただ、プログラムは嘘をつきません。書いたコードの通りに動きます。

なので質問者さんにはデバッグを覚えて欲しいということです。

 デバッガの導入について

前の質問を拝見しましたが、
VSCodeを使っているとのことなので
Javaなら一般的にVSCodeの拡張機能(Java Extention Pack)かEclipseを
使用してデバッグをするのだと思います。

Eclipseを使うならEcllpseをインストールしてください。

検索ワードとしては
「Eclipse」「Java Eclipse デバッグ」辺りでしょうか。

VSCodeの拡張機能を使うなら
「Java VSCode Java Extention Pack 導入」や「Java Extention Pack デバッグ」
辺りでしょうか。

好きな方法を試してみてください。

使い方も同様に上記検索ワードでひっかかると思います。
それでも使い方が分からないときは、
ここでご返信されるか別途ご質問されると良いかと思います。

 最後に

なぜこのような回答をしたかと申しますと
プログラムの動きについて
回答者さんとの会話のキャッチボールをする回数が多すぎるように見えたからです。

その根本的な原因は
きっとプログラムの動きがイメージできている自信がない所からだと思います。

まずは「デバッグ」の方法を教えることで
プログラムを止めながら実際に動きを確認するのが
質問者さんの長期的な問題解決にとって最善かと判断し、回答しました。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

+2

ご提示いただいたコードの勝ち負け判定の後が以下の流れになっています。

  1. (プレイヤーが勝ちなら)前回の勝者をプレイヤーと記録
  2. (前回の勝者がプレイヤーかつプレイヤーが勝ちなら)プレイヤーのポイントを減らし、前回の勝者をリセット
  3. (プレイヤーが負けなら)前回の勝者をCPUと記録
  4. (前回の勝者がプレイヤーかつプレイヤーが負けなら)CPUのポイントを減らし、前回の勝者をリセット

1と2、3と4で同じ事が起きていますが、1と2を例に説明します。
まずプレイヤーが勝ったと仮定して話します。
プレイヤーが勝っていたら前回の勝者をプレイヤーと記録するので、
この時点で今回の勝者も前回の勝者もプレイヤーとなってしまっています。
そこで2連勝の判定を行っていますが、前回の勝者が更新されたところ直後なので
質問者様の思っている判定とは異なった判定をしています。

3と4のパターンもCPUが勝った(プレイヤーが負けた)として考えれば同様です。

つまり、前回の勝者(lastWinner)を更新している箇所が間違っています。
前回の勝者の更新は、その値を使い終わった後にするべきです。


【追記】
必要部分を抜粋したコードを追いながら説明します。
(提示されたコードの空行を消すなど若干手を加えましたが、処理自体は変えてません)

/* 前提条件として「前回はCPUが勝った」として追います。 */

// 勝ち負け判定
result = janken.judge();    /* 今回はプレイヤーが勝ったとします。 */

if(result == S結果.あいこ) {
    System.out.println("あいこでしょ!");
} else if (result == S結果.勝ち) {  /* ここの分岐に来ます */
    System.out.println("playerの勝ち");
    playerPt ++;    /* プレイヤーに1点加算されます。 */
} else if (result == S結果.負け) {
    System.out.println("cpuの勝ち");
    cpuPt ++;
} else {
    break;
}

/* この分岐に入ります。 */
if(result == S結果.勝ち) {
    /* 前回の勝者をプレイヤーに更新します。 */
    /* ※この時点で前回の勝者はCPUだったはずなのに、
        プレイヤーに更新されてしまいます。 */
    lastWinner = 1;
}

/* 直前の更新でlastWinnerが1になってしまい、今回はプレイヤーが勝ったので
   この分岐に入ってしまいます。 */
if(lastWinner == 1 && result == S結果.勝ち) {
    playerPt--;
    lastWinner = 0;
}

コード内にコメントで書きましたが改めて。
 前回はCPUが勝ち、今回はプレイヤーが勝ったとして話を進めます。 

まず、プレイヤーが勝ったのでplayerPtが1増えます。

} else if (result == S結果.勝ち) {
    System.out.println("playerの勝ち");
    playerPt ++;
}

【問題点】次に、プレイヤーが勝ったのでlastWinnerを1(プレイヤー)にします。
 前回の勝者はCPUだったのに、ここで前回の勝者はプレイヤーと書き換えられます。 

if(result == S結果.勝ち) {
    lastWinner = 1;
}

その結果、連勝と判断されて、本来は入らない連勝の分岐に入り、
playerPtが1減ります。

if(lastWinner == 1 && result == S結果.勝ち) {
    playerPt--;
    lastWinner = 0;
}

このような流れで、連勝していないのに連勝と判断され、ポイントが+1→-1で0になります。


プログラムは書いたとおりに実行されます。
お書きになったプログラムを実行して結果を見る他に、
実際にコードを見ながら「今ここが実行され、次はここに来て…」と指で指して、
混乱しないように変数の値を紙か何かにメモしながら値の更新を確認し、
プログラムの流れを追うことができるようにしていきましょう。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/20 16:11

    > opyon様
    (写経を批判するわけではないです)
    写経は写経で得るものはあるとは思いますが、ものによると思います。
    サンプルコードがあるテキストやサイトを使う学習であれば
    写経による学習は問題ないかと思いますが、
    練習問題や演習などは自身で考えるという段階が必要だと思います。
    さじ加減は難しいですが、早い段階で正解コードを載せてあげるのは少しやりすぎかなと個人的には思います。

    コメントが長くなる件に関しては、初回の質問が解決した時点で打ち切るように
    勧められなかった私のミスでもあるのですが、
    新たに出た疑問に関しては別の質問として挙げ直してもらうよう勧めれば
    コメントが長くなることはそんなにないのかなと思います。
    opyon様のおっしゃる通り、短いやり取りで解決されることに越したことはないですけどね。

    キャンセル

  • 2018/09/20 16:20

    なるほど仰るとおりだと思います。

    キャンセル

  • 2018/09/20 16:30

    個人的に「業務の一環」として携わるものに写経は良くないと考えています。
    たとえ写経(丸写しや1から10まで言われるがまま作成)であれ、出来上がった成果物を受け取った場合、依頼者は「今後このレベルはできる」と見るからです。
    写経から勉強する気のある独学者、学習中の意識がある人間ならば写経から得るものも多いと思いますが、業務や研修でとりあえずコピペで提出!という人間が写経から学ぶのか、というと必ずしもそうではないですよね。

    今回の質問者様は自分なりに考えることはしているようですが、丸写しで課題を突破した場合、今後の憂いとして「この前やったからできるはず」と仕事を振られてもできずに怒られる・・・という状況が想像に難くありません(実際そういう人間を多々見ます)。
    なので、僕は今回の場合などは極力コードを提示せずに思考の方向性を示して自己解決を促す、というようにしています。(そうしろ、というわけではありません。)

    キャンセル

+1

java8で書き直しましたのでご参考までにどうぞ。

import java.util.Random;

public class qa147558 {
    private static int rsp(int p, int c) {
        int win = -1;
        if (p != c) {

            switch (p) {

            case 1:
                // =1=グー
                if (c == 2) {
                    win = 0;
                } else {
                    win = 1;
                }
                break;
            case 2:
                // =2=チョ
                if (c == 3) {
                    win = 0;
                } else {
                    win = 1;
                }
                break;
            case 3:
                // =3=パー
                if (c == 1) {
                    win = 0;
                } else {
                    win = 1;
                }
                break;
            }
        }
        return win;
    }

    public static void main(String[] args) {

        int lastwin = -2;
        int[][] players = { { 0, 0 }, { 1, 0 } };
        int winner = -1;

        String[] m = { "グー", "チョ", "パー" };
        String[] m_p = { "CPU1", "CPU2" };
        System.out.printf("%svs%s%n", m_p[0], m_p[1]);

        Random r = new Random();

        while (winner < 0) {

            // r:グー s:チョキ p:パー
            int c = r.nextInt(3) + 1;
            int p = r.nextInt(3) + 1;

            int win = rsp(p, c);

            System.out.printf("%svs%s ", m[p - 1], m[c - 1]);

            if (win == -1) {
                System.out.println("引分");
                continue;
            }
            if (lastwin == win) {
                lastwin = -2;
                System.out.printf("連勝 %s%n", m_p[win]);
            } else {
                players[win][1] += 1;
                lastwin = win;
                System.out.printf("勝者 %s%n", m_p[win]);
            }
            if (players[win][1] == 3) {
                winner = win;
            }
        }
        System.out.println();
        System.out.printf("最終勝者は %s%n", m_p[winner]);
    }
}


出力結果

CPU1vsCPU2
チョvsグー 勝者 CPU2
チョvsパー 勝者 CPU1
パーvsグー 連勝 CPU1
チョvsパー 勝者 CPU1
グーvsパー 勝者 CPU2
グーvsパー 連勝 CPU2
グーvsチョ 勝者 CPU1

最終勝者は CPU1

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/20 15:48

    入力は出来てるようなので、ロジックだけでもご参考にどうぞ。

    キャンセル

  • 2018/09/20 16:02

    せっかく焼き直すなら、enumくらいは使った方が良いのでは。
    質問者のコードでも使われてますし。

    キャンセル

  • 2018/09/20 16:04

    ご指摘ありがとうございます。
    enumですね、日頃あまり使ったことなかったので、練習してみます。

    キャンセル

  • 2018/09/20 18:29 編集

    enumで実装しました。別質問あげていますのでもしよろしければご意見頂けたら幸いです。

    キャンセル

-1

言語違いますが勝ち負けの状態と人とCPUとどちらも0,1で表せますのでロジックは読めるかと思います。
基本的なif文やwhile文やリストや辞書(map)しか使ってません。select文が無いのが残念。
ご参考までに。

# coding=UTF-8
import random

def rsp(p, c):

    win = -1

    if p == c:
        pass
    else:
        # =1=グー
        if p == 1:
            if c == 2:
                win = 0
            else:
                win = 1

        # =2=チョキ
        if p == 2:
            if c == 3:
                win = 0
            else:
                win = 1

        # =3=パー
        if p == 3:
            if c == 1:
                win = 0
            else:
                win = 1

    return win


lastwin = -2
sum_p = sum_c = 0
players = [[0, 0], [1, 0]]
winner = -1
m = {1:'グー', 2:'チョ', 3:'パー'}
m_p = {0:'CPU1', 1:'CPU2'}

print(m_p[0], 'vs', m_p[1])
while winner < 0:

    # r:グー s:チョキ p:パー
    c = random.choice([1, 2, 3])

    # 入力面倒なのでCPUvsCPU
    # p = int(input().rstrip())
    p = random.choice([1, 2, 3])

    print(m[c], m[p], end = ' ')

    win = rsp(c, p)

    if win == -1:
        print('引分')
        continue

    if lastwin == win:
#         players[win][1] -= 1
        lastwin = -2
        print('連勝', m_p[win])
    else:
        players[win][1] += 1
        lastwin = win
        print('勝者', m_p[win])

    if players[win][1] == 3:
        winner = win
print()
print('最終勝者は', m_p[winner])
出力結果
CPU1 vs CPU2
チョ グー 勝者 CPU2
チョ チョ 引分
チョ グー 連勝 CPU2
チョ グー 勝者 CPU2
チョ チョ 引分
グー チョ 勝者 CPU1
グー チョ 連勝 CPU1
グー チョ 勝者 CPU1
グー パー 勝者 CPU2

最終勝者は CPU2

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/20 14:41

    opyon 様
    プログラミングの初心者で、自分の書いたコードの流れもきちんと把握できないレベルの質問者様に対して、入力もなく全自動のコードを提示して「言語は違うけどロジックはわかるでしょう」というのはいささか乱暴な気がします。

    キャンセル

  • 2018/09/20 14:44 編集

    結構長いコードだったので大丈夫かなと思いましたが、配慮が足りずすみません。
    後ほど時間あれば、javaでも書いてみますね。

    キャンセル

  • 2018/09/20 15:49

    別回答としてjava8で書き直しました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.36%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Javaに関する質問
  • Java:「じゃんけんゲーム」でコンパイルしてもスコアが反映されない状態です。ソースコードでおかしい点は、どこにありますでしょうか?