じゃんけんの処理を作成してみよう(初学者向け)

Java

じゃんけんの処理を作成してみよう。この記事はJavaの学習を始めて2週間くらいの人のための記事だ。 (スッキリJavaを教科書にしてメソッドあたりまで学習した人を対象としている)

実行例

まずは実行例から
[実行例1]

手を入力:グー(0),チョキ(1),パー(2)>>1
あなたはチョキ
PCはパー
あなたの勝ち!

[実行例2]

手を入力:グー(0),チョキ(1),パー(2)>>0
あなたはグー
PCはグー
あいこ

仕様

  • じゃんけんで出す手をキーボードから0,1,2で入力してもらう
  • PCの手はランダムに決定する
  • あいこの場合でもそれで終了でよい。
  • 正常系のみを考慮すればよい(必ず0,1,2の値が入力されるものとしてよい)

作成

今回は徐々にグレードアップさせて完成形に持っていく形で行う。途中のパートも大事なのでできれば同じように進めていってもらいたい。

手を出力

まずは自分の手を出力するところまでを作ってみよう。

public class Main {
	public static void main(String[] args) {
		System.out.print("手を入力:グー(0),チョキ(1),パー(2)>>");
		int userHand=new java.util.Scanner(System.in).nextInt();
		if(userHand == 0) {
			System.out.println("あなたはグー");
		}else if(userHand == 1) {
			System.out.println("あなたはチョキ");
		}else {
			System.out.println("あなたはパー");
		}
	}
}

まずは、このようなコードが思いつくのではないだろうか?ユーザーからの入力を受け取ってif文で分岐させて出力している。

switch式

ただ、今回の処理は条件式の部分が userHand == 0 のように変数と値が等しいかどうかのみで判定をおこなている。こういうときはswitchが使えるのであった。switchを使って改良しよう。(switch式はdefaultが必須な点に注意)

public class Main {
	public static void main(String[] args) {
		System.out.print("手を入力:グー(0),チョキ(1),パー(2)>>");
		int userHand = new java.util.Scanner(System.in).nextInt();
		String userStr = switch (userHand) {
			case 0 -> "グー";
			case 1 -> "チョキ";
			default -> "パー";
		};
		System.out.println("あなたは"+userStr);
	}
}

if文で書くより少しすっきりとかけたことがわかる。今度はPCの手の部分を作成しよう。

public class Main {
	public static void main(String[] args) {
		System.out.print("手を入力:グー(0),チョキ(1),パー(2)>>");
		int userHand = new java.util.Scanner(System.in).nextInt();
		String userStr = switch (userHand) {
			case 0 -> "グー";
			case 1 -> "チョキ";
			default -> "パー";
		};
		System.out.println("あなたは"+userStr);
		
		int pcHand = new java.util.Random().nextInt(3);
		String pcStr = switch (pcHand) {
			case 0 -> "グー";
			case 1 -> "チョキ";
			default -> "パー";
		};
		System.out.println("PCは"+pcStr);
		
	}
}

ふー、OKっと。
果たして、ほんとにOKだろうか?書いていて違和感はなかっただろうか?
あなたは気付いたはずだほとんど同じことを書いている。。。
そう、あなたはDRYの原則に反してしまったのだ。

DRYの原則(Don’t Repeat Yourself)
プログラマーは同じことを2度書いてはいけない

修正しよう。同じような処理を書く必要が生じたら、メソッドを作成すればよいのであった。
早速メソッドを作成して修正しよう。

public class Main {
	public static void main(String[] args) {
		System.out.print("手を入力:グー(0),チョキ(1),パー(2)>>");
		int userHand = new java.util.Scanner(System.in).nextInt();
		System.out.println("あなたは"+getHandName(userHand));
		
		int pcHand = new java.util.Random().nextInt(3);
		System.out.println("PCは"+getHandName(pcHand));
	}
	public static String getHandName(int hand) {
		String handName = switch(hand) {
			case 0 -> "グー";
			case 1 -> "チョキ";
			default -> "パー";
		};
		return handName;
	}
}

おお、メソッドを作成することで重複した処理はなくなりコードもすっきりした!

配列の利用

しかし、私達はさらに便利な仕組みをしっている。0がグー、1がチョキ、2がパーを表せばよいのでせっかく作ったメソッドだが、これを削除して配列を利用するようにしてみよう。

public class Main {
	public static void main(String[] args) {
		System.out.print("手を入力:グー(0),チョキ(1),パー(2)>>");
		String[] hands = {"グー","チョキ","パー"};
		int userHand = new java.util.Scanner(System.in).nextInt();
		System.out.println("あなたは"+hands[userHand]);
		
		int pcHand = new java.util.Random().nextInt(3);
		System.out.println("PCは"+hands[pcHand]);
	}
}

うそー!配列を用いることで先程作ったメソッドは全く不要になりソースコードの量が半分くらいになってしまった!
このように配列はうまく利用すると、処理を効率的に作成できることがある。

結果判定

それでは結果判定の部分を作成しよう。じゃんけんの結果は「勝ち」、「負け」、「あいこ」しかない。
こういったときはif ~ else if ~ else を組むとよいのであった早速作成していこう。このときのポイントはelseには条件式を書かなくてよい点である。以下のようにコーディングしてみる。


public class Main {
	public static void main(String[] args) {
		System.out.print("手を入力:グー(0),チョキ(1),パー(2)>>");
		String[] hands = {"グー","チョキ","パー"};
		int userHand = new java.util.Scanner(System.in).nextInt();
		System.out.println("あなたは"+hands[userHand]);
		
		int pcHand = new java.util.Random().nextInt(3);
		System.out.println("PCは"+hands[pcHand]);
		
		if(userHand == pcHand) {
			System.out.println("あいこ");
		}else if(  userHand == 0 && pcHand == 1
				|| userHand == 1 && pcHand == 2
				|| userHand == 2 && pcHand == 0) {
			System.out.println("あなたの勝ち!");
		}else {
			System.out.println("あなたの負け...");
		}
	}
}

負けパターンをelseにもってくることで条件式の記述が楽になっている。それでも勝ちパターンを列挙していくのは今ひとつイケてない。。。どう考えても法則がありそうなのだが何かないのであろうか?

例えばuserHandからpcHandを引いてみる。というのはどうだろうか?
userHandが0(グー) – pcHandが1(チョキ) = -1
userHandが1(チョキ) – pcHandが2(パー) = -1
おお、-1のときが勝ちなんではないうか!!
userHandが2(パー) – pcHandが0(グー) = 2
残念、、、-1になってくれなかった。

やはり、シンプルに記述できないのであろうか。。。

実はできる。
さきほど、最初の2つまではうまくいったのに最後の一つが関係性がクロスしてしまって結果がうまくいかなくなってしまった。そう、関係性が決してクロスしないようにuserHandに下駄をはかせてみよう。
userHand + 3 - pcHand
userHandに3を足した状態からpcHandを引けばuserHandが負になることは決してない。結果をみてみよう。
userHandが0(グー)+ 3 – pcHandが1(チョキ) = 2
userHandが1(チョキ) +3 – pcHandが2(パー) = 2
userHandが2(パー) + 3 – pcHandが0(グー) = 5
やはり最後が異なってしまった。しかし、我々には強力な武器がある、こういった循環事象を扱うときに強力なあれが。。。
そう、剰余を扱う%記号を使えば解決だ!
(userHand + 3 - pcHand) % 3

userHandが0(グー)+ 3 – pcHandが1(チョキ) = 2 ——-> 2 % 3 -> 2
userHandが1(チョキ) +3 – pcHandが2(パー) = 2 ——–> 2 % 3 -> 2
userHandが2(パー) + 3 – pcHandが0(グー) = 5 ——–> 5 % 3 – > 2

勝ちパターンが2で一致した!!
これを踏まえてコードを修正しよう。


public class Main {
	public static void main(String[] args) {
		System.out.print("手を入力:グー(0),チョキ(1),パー(2)>>");
		String[] hands = {"グー","チョキ","パー"};
		int userHand = new java.util.Scanner(System.in).nextInt();
		System.out.println("あなたは"+hands[userHand]);
		
		int pcHand = new java.util.Random().nextInt(3);
		System.out.println("PCは"+hands[pcHand]);
		
		int diff = (userHand + 3 - pcHand) % 3;
		
		if(diff == 0) {
			System.out.println("あいこ");
		}else if(diff == 2) {
			System.out.println("あなたの勝ち!");
		}else {
			System.out.println("あなたの負け...");
		}
	}
}

条件式がすっきりした!
おや、0のときが「あいこ」 1のときが「負け」、2のときが「勝ち」になっている。
これは、さっきと同じだ、まさか。。。

public class Main {
	public static void main(String[] args) {
		System.out.print("手を入力:グー(0),チョキ(1),パー(2)>>");
		String[] hands = {"グー","チョキ","パー"};
		int userHand = new java.util.Scanner(System.in).nextInt();
		System.out.println("あなたは"+hands[userHand]);
		
		int pcHand = new java.util.Random().nextInt(3);
		System.out.println("PCは"+hands[pcHand]);
		
		int diff = (userHand + 3 - pcHand) % 3;
		String[] results= {"あいこ","あなたの負け...","あなたの勝ち!"};
		System.out.println(results[ diff]);
		
	}
}

ぎゃー、じゃんけんの処理だというのにif文なしで処理を作成できてしまった!!!

終わりに

今回の処理からもわかるように、配列を活用できる局面というのは多い。また剰余を扱う%演算子はときに絶大な効果をもたらすのでしっかりと慣れていこう。

参考記事

Java
スポンサーリンク
シェアする
mjpurinをフォローする

コメント

タイトルとURLをコピーしました