Java じゃんけんゲームを作ってみる ~基本文法をマスターする~

じゃんけんゲーム

Javaプログラミングの基本文法を理解するためにも、実際にアプリケーションを作ってみるのが手っ取り早い方法です。

教科書などを眺めても、実践に勝る学習はありません。つまりは。。。

経験に勝る知識なし!

ということです。

じゃんけんゲームの処理内容

本来は、自分で考えて仕様を考えるべきですが、初めの一歩ということで使用を提示します。

作成するクラスは「JankenPon」クラスにします。

<じゃんけんゲームの仕様>

  1. 標準入力での、0:グー、1:チョキ、2:パーと定義する
  2. ユーザーの手(入力)とCPUの手(乱数)を表示する
  3. 勝敗判定を行い、「YOU_WIN」か「YOU_LOSE」「DRAW」を表示する

この様に使用を決めたらそれぞれの項目(要件)を満たすようにプログラムを作ります。

0:グー、1:チョキ、2:パーと定義する

標準入力を受け取りグー~パーを判定します。
どのように実装したらよいでしょうか?

今回は、試しに以下のような実装をしてみましょう。

if文をマスターする

マスターするといっても、普通に使うだけですが。。。

Scanner scan = new Scanner(System.in);
int input = Integer.parseInt(scan.nextLine());
String jankenTe = null;
if (input == 0) {
    jankenTe = "グー";
} else if (input == 1) {
    jankenTe = "チョキ";
} else if (input == 2) {
    jankenTe = "パー";
} else {
    System.out.println("想定外の値です。" + input);
}

じゃんけんの手をString型の変数JankenTeに設定(代入)します。
0, 1, 2以外の値が入力されたときは想定外の値です。

ユーザーの手(入力)とCPUの手(乱数)を表示する

CPUの手を乱数を使って表示します。これはもう説明する必要はないと思います。

// 0-2の乱数を生成する
cpuTe = String.valueOf(new Random().nextInt(2));

if (cpuTe.equals("0")) {
    jankenCpu = "グー";
} else if (cpuTe.equals("1")) {
    jankenCpu = "チョキ";
} else if (cpuTe.equals("2")) {
    jankenCpu = "パー";
}

これも、if文で実装することができます。もちろん、より良いアイディアがあればそちらの実装を行うのが正しい判断です。検証するのを忘れないようにしてください。

勝敗判定を行い、「YOU_WIN」か「YOU_LOSE」「DRAW」を表示する

勝敗判定は、ちょっと良いアイディアが出ず。。。そのまま判定処理を作りました。
はっきり言ってダメダメなコードです。

String hantei = input + cpuTe;
String result = null;
if (hantei.equals("01")) {
    result = "YOU_WIN";
} else if (hantei.equals("12")) {
    result = "YOU_WIN";
} else if (hantei.equals("20")) {
    result = "YOU_WIN";
} else if (hantei.equals("00")) {
    result = "DRAW";
} else if (hantei.equals("11")) {
    result = "DRAW";
} else if (hantei.equals("22")) {
    result = "DRAW";
} else if (hantei.equals("02")) {
    result = "YOU_LOSE";
} else if (hantei.equals("10")) {
    result = "YOU_LOSE";
} else if (hantei.equals("21")) {
    result = "YOU_LOSE";
} else {
    System.out.println("想定外の値です。" + hantei);
}

何とかじゃんけんゲームができました。しかし、このようなプログラムは美しくありません。

別に「アーティスチックでないからダメ」といっているわけではなく、見ずらいしわかりずらい。

人にやさしくないプログラムだということです。

人にやさしく

ズバリ、読みやすく、理解しやすいコードが良いブログラムです。もちろん、余計なメモリを使わないとかいろいろありますが。。。

ちなみに、上のようなコードで作成したじゃんけんゲームは以下のようなコードになりました。

public static void main(String[] args) {
    // 1.初めの処理
    System.out.println("じゃんけん ...");
    Scanner scan = new Scanner(System.in);

    // 2.標準入力受付
    String input = scan.nextLine();
    String jankenTe = null;

    // 3.ユーザーの手を判定する
    if (input.equals("0")) {
        jankenTe = "グー";
    } else if (input.equals("1")) {
        jankenTe = "チョキ";
    } else if (input.equals("2")) {
        jankenTe = "パー";
    } else {
        System.out.println("想定外の値です。" + input);
    }

    String cpuTe = null;
    String jankenCpu = null;
    if (jankenTe == null) {
        // 強制終了
        System.exit(-1);
    } else {
        // 4.CPUの手を判定する
        cpuTe = String.valueOf(new Random().nextInt(2));

        if (cpuTe.equals("0")) {
            jankenCpu = "グー";
        } else if (cpuTe.equals("1")) {
            jankenCpu = "チョキ";
        } else if (cpuTe.equals("2")) {
            jankenCpu = "パー";
        }
    }
    System.out.println("ポン");
    System.out.println("あなた: " + jankenTe + " CPU; " + jankenCpu);

    // 5.勝敗判定を行う
    String hantei = input + cpuTe;
    String result = null;
    if (hantei.equals("01")) {
        result = "YOU_WIN";
    } else if (hantei.equals("12")) {
        result = "YOU_WIN";
    } else if (hantei.equals("20")) {
        result = "YOU_WIN";
    } else if (hantei.equals("00")) {
        result = "DRAW";
    } else if (hantei.equals("11")) {
        result = "DRAW";
    } else if (hantei.equals("22")) {
        result = "DRAW";
    } else if (hantei.equals("02")) {
        result = "YOU_LOSE";
    } else if (hantei.equals("10")) {
        result = "YOU_LOSE";
    } else if (hantei.equals("21")) {
        result = "YOU_LOSE";
    } else {
        System.out.println("想定外の値です。" + hantei);
    }
    // 6. 結果の表示
    System.out.println(hantei);
    System.out.println(result);
}

しかし、やはりだめなコード(個人的にそのように思う)なので、これをまともなコードに直します。

この様な行為のことをリファクタリングといいます。

リファクタリング

まずは、読みずらいところを洗い出します。
以下の部分が読みずらいと思いました。

1.if文のネスト(階層化しているところ)が無駄なネストになっている

String cpuTe = null;
String jankenCpu = null;
if (jankenTe == null) {
    // 強制終了
    System.exit(-1);
} else {
    // 4.CPUの手を判定する
    cpuTe = String.valueOf(new Random().nextInt(2));

    if (cpuTe.equals("0")) {
        jankenCpu = "グー";
    } else if (cpuTe.equals("1")) {
        jankenCpu = "チョキ";
    } else if (cpuTe.equals("2")) {
        jankenCpu = "パー";
    }
}

どこが、無駄かというとelse~の部分です。もしjannkenTeがnullになっているのならば、プログラムを強制終了するので、if-elseでわける意味がありません。無駄なコードになっているのです。

ではどのようになおすか?ズバリ下のように直します。elseが丸まる必要ないのです。

String cpuTe = null;
String jankenCpu = null;
if (jankenTe == null) {
    // 強制終了
    System.exit(-1);
}

// 4.CPUの手を判定する
cpuTe = String.valueOf(new Random().nextInt(2));

if (cpuTe.equals("0")) {
    jankenCpu = "グー";
} else if (cpuTe.equals("1")) {
    jankenCpu = "チョキ";
} else if (cpuTe.equals("2")) {
    jankenCpu = "パー";
}

処理の内容を考えてみれば、不要ですよね?なぜなら

System.exit(-1);

でプログラムは強制終了(引数に「-1」を与えているので『異常終了』)するからです。

つまり、if文の中の処理を行うとき=jankenTeがnullの時は「CPUの手を判定」より後の処理が行われません。

メソッドを利用する

その他にも、行けていないところがあります。それは、似たような処理がたくさんあるというところです。
特にif文の処理が同じような処理を行っています。

なので、メソッドを作成して同じコードを書かないようにします。

今までに、ちょこっと書いたりしました。改めて理解しましょう。

具体的には、以下のif文です。2か所、同じコードがあります。

String jankenTe = null;
if (input.equals("0")) {
    jankenTe = "グー";
} else if (input.equals("1")) {
    jankenTe = "チョキ";
} else if (input.equals("2")) {
    jankenTe = "パー";
} else {
    System.out.println("想定外の値です。" + input);
}

これをメソッドとして切り出します。具体的には下のように切り出します。staticがついているのはメインメソッドから呼び出すためです。

public static String jankenHantei(String input) {
    String jankenTe = null;
    if (input.equals("0")) {
        jankenTe = "グー";
    } else if (input.equals("1")) {
        jankenTe = "チョキ";
    } else if (input.equals("2")) {
        jankenTe = "パー";
    } else {
        System.out.println("想定外の値です。" + input);
    }
    return jankenTe;
}

そして、このコードと同じ部分を書き換えます。下のようなコードになりました。

public class JankenPon {
    public static void main(String[] args) {
        // 1.初めの処理
        System.out.println("じゃんけん ...");
        Scanner scan = new Scanner(System.in);

        // 2.標準入力受付
        String input = scan.nextLine();
        // 3.ユーザーの手を判定する
        String jankenTe = jankenHantei(input);

        String cpuTe = null;
        String jankenCpu = null;
        if (jankenTe == null) {
            // 強制終了
            System.exit(-1);
        }
        // 4.CPUの手を判定する
        cpuTe = String.valueOf(new Random().nextInt(2));
        jankenCpu = jankenHantei(cpuTe);

        System.out.println("ポン");
        System.out.println("あなた: " + jankenTe + " CPU; " + jankenCpu);

        // 5.勝敗判定を行う
        String hantei = input + cpuTe;
        String result = null;
        if (hantei.equals("01")) {
            result = "YOU_WIN";
        } else if (hantei.equals("12")) {
            result = "YOU_WIN";
        } else if (hantei.equals("20")) {
            result = "YOU_WIN";
        } else if (hantei.equals("00")) {
            result = "DRAW";
        } else if (hantei.equals("11")) {
            result = "DRAW";
        } else if (hantei.equals("22")) {
            result = "DRAW";
        } else if (hantei.equals("02")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("10")) {
            result = "YOU_LOSE";
        } else if (hantei.equals("21")) {
            result = "YOU_LOSE";
        } else {
            System.out.println("想定外の値です。" + hantei);
        }
        // 6. 結果の表示
        System.out.println(hantei);
        System.out.println(result);
    }

    public static String jankenHantei(String input) {
        String jankenTe = null;
        if (input.equals("0")) {
            jankenTe = "グー";
        } else if (input.equals("1")) {
            jankenTe = "チョキ";
        } else if (input.equals("2")) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
        return jankenTe;
    }
}

これで、多少はよくなりました。しかし、まだです。

Mapインターフェースを使用する

じゃんけんの判定方法ですが、すごく頭の悪いやり方です。しかし、これから見せるコードも頭の良いやり方ではありません。なぜかというと、何の工夫もないからです。

頭の良いやり方ではない方法
まずは下のような、フィールド変数(定数)とメソッドを作成します。

/** フィールド変数(定数) */
public static final String YOU_WIN = "YOU_WIN";
public static final String YOU_LOSE = "YOU_LOSE";
public static final String DRAW = "DRAW";</pre>

そして、次のメソッドを追加します。
<pre> public static String jankenHantei(String input) {
        String jankenTe = null;
        if (input.equals("0")) {
            jankenTe = "グー";
        } else if (input.equals("1")) {
            jankenTe = "チョキ";
        } else if (input.equals("2")) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
        return jankenTe;
    }

作成したメソッドを使用して、メインメソッドを書き直します。

そうすると下のようになりました。

public class Chap0 {
    public static final String YOU_WIN = "YOU_WIN";
    public static final String YOU_LOSE = "YOU_LOSE";
    public static final String DRAW = "DRAW";

    public static void main(String[] args) {
        // 1.初めの処理
        Map<String, String> hanteiMap = createMap();
        System.out.println("じゃんけん ...");
        Scanner scan = new Scanner(System.in);

        // 2.標準入力受付
        String input = scan.nextLine();
        // 3.ユーザーの手を判定する
        String jankenTe = jankenHantei(input);

        String cpuTe = null;
        String jankenCpu = null;
        if (jankenTe == null) {
            // 強制終了
            System.exit(-1);
        }
        // 4.CPUの手を判定する
        cpuTe = String.valueOf(new Random().nextInt(2));
        jankenCpu = jankenHantei(cpuTe);

        System.out.println("ポン");
        System.out.println("あなた: " + jankenTe + " CPU: " + jankenCpu);

        // 5.勝敗判定を行う
        String hantei = input + cpuTe;
        String result = hanteiMap.get(hantei);
        // 6. 結果の表示
        System.out.println(hantei);
        System.out.println(result);
    }

    public static String jankenHantei(String input) {
        String jankenTe = null;
        if (input.equals("0")) {
            jankenTe = "グー";
        } else if (input.equals("1")) {
            jankenTe = "チョキ";
        } else if (input.equals("2")) {
            jankenTe = "パー";
        } else {
            System.out.println("想定外の値です。" + input);
        }
        return jankenTe;
    }

    public static Map<String, String> createMap() {
        Map<String, String> hanteiMap = new HashMap<String, String>();
        hanteiMap.put("01", YOU_WIN);
        hanteiMap.put("12", YOU_WIN);
        hanteiMap.put("20", YOU_WIN);
        hanteiMap.put("00", DRAW);
        hanteiMap.put("11", DRAW);
        hanteiMap.put("22", DRAW);
        hanteiMap.put("02", YOU_LOSE);
        hanteiMap.put("10", YOU_LOSE);
        hanteiMap.put("21", YOU_LOSE);

        return hanteiMap;
    }
}

実行結果は下のような形で出力されました。

じゃんけん ...
1
ポン
あなた: チョキ CPU; チョキ
11
DRAW

如何でしょうか?だいぶすっきりしたように見えると思います。プログラムの処理内容が理解できていれば、無図解ことではありません。「このような方法もある」というのを知っておくと後々にカッコよいコードが書けるようになります。※自分も努力いたします。。。

でわでわ。。。