じゃんけんの勝敗判定
じゃんけんの勝敗判定 とは
複数の
ifを組み合わせて、3 すくみの勝敗を判定するロジックを書こう。本レッスンでは、じゃんけんの勝敗判定 の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。
じゃんけんで学ぶ複雑な条件分岐
ここまでで if 文と else 文を使った 2 択の分岐を見てきました。「a > 0 なら正、そうでなければ負」のように、true か false の片方に従って処理を切り替えるパターンです。しかし現実のプログラムでは、もっと 3 通り以上の結果 を扱いたい場面が山のように出てきます。たとえばユーザーのランクを gold silver bronze の 3 段階で振り分けたい、テストの点数を A B C D の 4 段階で評価したい、といったケースです。
そんなときの最初の練習台にぴったりなのが、誰もが知っている じゃんけん です。手はグー・チョキ・パーの 3 種類、結果は勝ち・負け・引き分けの 3 種類。条件分岐の練習問題として完璧な題材です。
プログラミングの世界では、じゃんけんのような 三すくみ (rock-paper-scissors) は条件分岐の登竜門として有名です。
JavaだけでなくPythonJavaScriptどの言語の教材にも必ず登場します。
このレッスンでは、Solution.janken(int me, int opponent) メソッドを完成させて、私の手と相手の手から勝敗の文字列を返します。手は 0 がグー、1 がチョキ、2 がパーで、戻り値は "win" "lose" "draw" のいずれかです。
ルールの整理
まずはルールを Java のコードに落とし込みやすい形でまとめます。じゃんけんの勝敗関係は以下の通りです。
- グー (
0) はチョキ (1) に勝つ - チョキ (
1) はパー (2) に勝つ - パー (
2) はグー (0) に勝つ - 同じ手 (
me == opponent) なら引き分け
3 つの勝ちパターンと 1 つの引き分けパターン、合わせて 4 種類のケースを if で書き分けるのが基本のアプローチです。
「全部のパターンを列挙する」というのは、最初の頃は遠回りに感じるかもしれません。でも条件分岐に慣れていないうちは、スマートに書こうとして条件を取りこぼす ほうがずっと危険です。まずは愚直に全部書きましょう。
判定フローを図で確認する
判定の流れを Mermaid で図にすると、頭の中の整理がぐっと楽になります。
上から順に判定していけば、いつかは必ずどれかの return に当たります。図にしておくと「漏れがないか」「順番は妥当か」を確認しやすくなります。
引き分けを最初に判定するパターン
まずは最もシンプルな書き方から見ましょう。引き分けは me == opponent という 1 行で判定できるので、これを 最初に チェックするのがコツです。
Java
public class Solution {
public static String janken(int me, int opponent) {
if (me == opponent) {
return "draw";
}
if (me == 0 && opponent == 1) {
return "win";
}
if (me == 1 && opponent == 2) {
return "win";
}
if (me == 2 && opponent == 0) {
return "win";
}
return "lose";
}
}引き分けを先に片付けてしまえば、残りは「勝ち 3 パターン」と「それ以外は負け」の判定に集中できます。各 if の中で return してしまうので else を書く必要すらありません。これは Java で頻出する 早期 return (early return) のスタイルです。
早期 return は読みやすさの強い味方です。ネストが深くならず、各分岐の条件が独立して見えるので、後からコードを読み返したときに「ここで何を判定しているか」が一目でわかります。
勝ちパターンを全部列挙する書き方
別解として、勝ちパターンを || で 1 つの式にまとめる書き方もあります。
Java
public class Solution {
public static String janken(int me, int opponent) {
if (me == opponent) {
return "draw";
}
boolean win = (me == 0 && opponent == 1)
|| (me == 1 && opponent == 2)
|| (me == 2 && opponent == 0);
if (win) {
return "win";
}
return "lose";
}
}&& (かつ) と || (または) の使い分けがポイントです。1 つの勝ちパターンの中では me == 0 && opponent == 1 のように 両方が同時に成り立つ 必要があるので &&、3 つの勝ちパターンのどれかに当てはまればよいので || で繋ぐ、という構造です。
この書き方の利点は、勝ち条件全体が boolean win という名前付きの変数に入るので、後から「勝ち条件をテストしたい」「ログに残したい」といった拡張がしやすいことです。
(me + 1) % 3 == opponent でスマートに書く
上級者向けの別解として、剰余演算子 % を使った 1 行解法もあります。じゃんけんの勝ちパターンには 隠れた規則性 があります。
- グー (
0) はチョキ (1) に勝つ —0 + 1 = 1 - チョキ (
1) はパー (2) に勝つ —1 + 1 = 2 - パー (
2) はグー (0) に勝つ —2 + 1 = 3、ただし3 % 3 = 0
つまり「自分の手に +1 した値 (3 で割った余り) が相手の手と等しい」なら自分の勝ちです。コードにすると次の通りです。
Java
public class Solution {
public static String janken(int me, int opponent) {
if (me == opponent) {
return "draw";
}
if ((me + 1) % 3 == opponent) {
return "win";
}
return "lose";
}
}たった 3 つの if で全パターンを判定できるエレガントな書き方です。ただし、可読性は人によって賛否があります。チームのコードレビューでは「式の意味がパッと読めないからコメントを付けて」と指摘されることも多いので、用法用量を守って使ってください。
「短いコード = 良いコード」ではありません。
Javaの現場では 読み手の負担 が最優先されることが多く、愚直なifの列挙のほうが好まれるケースも珍しくありません。
よくある間違い
条件分岐の練習で初心者が踏むワナを 3 つ紹介します。
- 条件の網羅性が足りない — 勝ちパターンを 2 つしか書かずに残り 1 つを忘れる、引き分けの判定を書き忘れる、といったケースです。テストケースを増やしてあげると、こういった抜けが必ず炙り出されます。先ほどの
Mermaid図のように、すべての分岐を絵にしてから書くと抜けが減ります ||と&&の使い間違い —me == 0 || opponent == 1のように、本来&&で繋ぐべき条件を||で書いてしまうミスです。||は どちらか片方でもtrueならパスしてしまうので、「自分がグー」というだけで勝ち扱いになるなど、判定が壊滅的に狂います。「両方とも成り立つ必要があるなら&&、どれか 1 つでも成り立てばよいなら||」と毎回声に出して確かめてください- 引き分けの検出忘れ — 勝ちパターンだけ書いて、勝ちでなかったらすべて
"lose"を返してしまうと、janken(1, 1)のような同じ手のときも"lose"になります。me == opponentをどこかで必ず判定する必要があります
もうひとつ、地味だけど頻発するのが 戻り値の文字列のタイポ です。"win" "lose" "draw" という綴りは英語が苦手な人ほど "Win" "loose" "drow" のように打ち間違えがちです。Java の文字列比較は大文字小文字を区別するので、テストの expected と 1 文字でも違えば fail します。
エラーメッセージで
expected: win, actual: Winのような出力が出てきたら、まずは綴りを疑ってみましょう。コピー&ペーストできる場面ではなるべく手打ちせず、貼り付けるのも 1 つの安全策です。
やってみよう
それでは右側のエディタで課題に挑戦しましょう。書き方は自由ですが、以下の手順がオススメです。
- まず
if (me == opponent)で引き分けを判定する - 続いて勝ちの 3 パターン (
0vs1、1vs2、2vs0) をifで順番に判定する - どれにも当てはまらなければ最後に
return "lose";で締める
慣れてきたら、(me + 1) % 3 == opponent を使った 1 行解法に書き換えてみたり、|| で勝ちパターンを 1 つにまとめてみたり、リファクタリングの練習にしてみてください。同じ動きを 3 通りで書けるようになると、Java の条件分岐がぐっと自分のものになります。
また、Solution.janken(int me, int opponent) の引数を変えて、janken(0, 0) (引き分け)、janken(2, 1) (負け)、janken(1, 2) (勝ち) などを手元で予想 → 実行してみると理解が深まります。条件分岐は手を動かして体に染み込ませる単元です。Android アプリでも Web アプリでも Minecraft Mod でも、書くプログラムの大半に条件分岐が登場します。ここでしっかり練習しておきましょう。
次のレッスン
次は FizzBuzz の判定部分 で、複数の if を組み合わせて、3 すくみの勝敗を判定するロジックを書こう を学びます。
事前確認 — 進む前に次の 3 つができることを確認しましょう。
- じゃんけん の要点を自分の言葉で説明できる
- このレッスンの最小コード (または操作手順) を見ずに書ける
- 練習問題やクイズで間違えた箇所を読み直して理解した
理解度チェック (30 秒)
Q. じゃんけん とは何か、1 文で説明してください。
A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。
関連レッスン
要件
- クラス名は
Solution、メソッド名はjanken、引数はint me, int opponentの 2 つにすること - 戻り値の型は
Stringで、勝ちなら"win"、負けなら"lose"、引き分けなら"draw"を返すこと - ルールは グー (
0) > チョキ (1) > パー (2) > グー (0) の三すくみで、同じ手なら引き分け
入出力例
test-cases.txt
janken(0, 1) → "win"
janken(1, 2) → "win"
janken(2, 0) → "win"
janken(0, 2) → "lose"
janken(1, 0) → "lose"
janken(2, 1) → "lose"
janken(0, 0) → "draw"
janken(1, 1) → "draw"