友達のじゃんけんの勝敗判定のコードを効率化&わかりやすくしてみた

## 自己紹介

Python大好きwhile-true-ifです。

## 対象者

- Python好きな人
- Python初心者
- Pythonでじゃんけんのコンソールアプリを作りたい人

## この記事を読む前に

- この記事では効率化という言葉が使われます。ここでいう効率化とは、「コードを書く効率」です。処理時間ではありません。
- あくまでもこの記事は2人のじゃんけんに特化した勝敗判定となっています。3人以上は想定していません。
- この記事に出てくる「よりよいコード」とはだいたい同じくらいの処理時間(差が1000ms以内)であり、コードが短くなることを意味します。

## じゃんけんとは

**グーとチョキとパーがあります。**

下の表は左側のグー・チョキ・パーが自分の出した手で、上側のグー・チョキ・パーが相手の出した手です。

<table border="1">
    <tr>
        <th></th>
        <th>グー</th>
        <th>チョキ</th>
        <th>パー</th>
    </tr>
    <tr>
        <th>グー</th>
        <td align="center">△</td>
        <td align="center">○</td>
        <td align="center">×</td>
    </tr>
    <tr>
        <th>チョキ</th>
        <td align="center">×</td>
        <td align="center">△</td>
        <td align="center">○</td>
    </tr>
    <tr>
        <th>パー</th>
        <td align="center">○</td>
        <td align="center">×</td>
        <td align="center">△</td>
    </tr>
</table>

○は自分が勝ったことを表し、△は引き分け・あいこであることを表し、×は負けたことを表します。

じゃんけんのルールの説明はこれで終わりにして、Pythonでじゃんけんの勝敗判定をしていきたいと思います。

## じゃんけんの勝敗判定

### 経緯

友達がPythonを使ってじゃんけんのコンソールアプリを作っていました。

そのときに、友達の書いた勝敗判定が下のコードです。

ここでは、**0がグー、1がチョキ、2がパーとします**。

### 友達のコード

```python
# 0:グー 1:チョキ 2:パー
a,b=map(int,input().split())
if a==0:
    if b==0:
        print("draw")
    elif b==1:
        print("win")
    else:
        print("lose")
elif a==1:
    if b==0:
        print("lose")
    elif b==1:
        print("draw")
    else:
        print("win")
else:
    if b==0:
        print("win")
    elif b==1:
        print("lose")
    else:
        print("draw")
```

### 友達のコードの説明

まず、1行目がコメントなので、スルーしてOKです。

次に、2行目でaとbという変数に半角スペース区切りのint型で2つの変数に代入します。つまり、`1 2`という入力が与えられたとき、`a=1`、`b=2`となります。

そこから下の行は条件分岐で、すべてのパターン(3<sup>2</sup>=9(通り))を試して、`win`、`draw`、`lose`を出力しています。

このコードを見て、**わかりにくい**と思いました。そのため、修正していきたいと思います。

### じゃんけんの勝敗判定の効率的なつけ方

0をグー、1をチョキ、2をパーにしたとき、

```math
(自分の出した手 - 相手の出した手 + 3) mod 3
```

この式に自分の出した手と相手の出した手に代入します。

次に、**その式の値をnとおいたときに、n=0だったら引き分け・あいこ、n=1だったら負け、n=2だったら勝ち**となります。

ちなみに、modというのはあまりです。
つまり、`(自分の出した手 - 相手の出した手 + 3)mod3`は
(自分の出した手 - 相手の出した手 + 3)から3で割った時のあまりです。

たとえば、

```math
10 mod 3 = 1
```

となります。

### +3ってなんなの？

一応、`負の数 mod 3`に対処するため、付け加えました。

Pythonで

```python
print(-1%3)
```

を実行したところ、`2`と出力されました。これが自分の想定している出力です。

しかし、自分もあまり知りませんが、他の言語で`1`と出力されたら、困るので、`+3`を付けたさせていただきました。

少なくとも、Pythonでは`+3`を付け足さなくても、ちゃんとした勝敗判定が出ます。

### じゃんけんの勝敗判定を効率的に行ったコード

```python
# 0:グー 1:チョキ 2:パー
def result(my_hand, opponent_hand):
    key = (my_hand - opponent_hand + 3) % 3
    if key == 0:
        return 1
    elif key == 1:
        return 0
    else:
        return 2

result_output = ["lose", "draw", "win"]
my_hand, opponent_hand = map(int, input().split())
output_key = result(my_hand, opponent_hand)
print(result_output[output_key])
```

### 自分のコードの説明

1行目はコメントなのでスルーしてOKです。
2~9行目はさっきの方法で、じゃんけんの勝敗判定を効率的に行っています。
10行目はなにもないのでスルーしてOKです。
11行目は`result_output`というlistに、`lose`、`draw`、`win`という要素を格納しています。
12行目では`my_hand`と`opponent_hand`という変数に半角スペース区切りでint型のものを代入しています。
13行目では2~9行目のコードを実行して、`output_key`という変数に返された値を代入しています。
14行目では`result_output`から`output_key`番目の値を出力しています。
　※ここでは、0番目スタートであるとします。

### より良いコード

これは@shiracamus(しらかみゅ)さんからいただいたコードです。

```python
# 0:グー 1:チョキ 2:パー
def janken(my_hand, opponent_hand):
    return ("draw", "lose", "win")[(my_hand - opponent_hand) % 3]

print(janken(*map(int, input().split())))
```

### よりよいコードの説明

1行目はコメントなのでスルーしてOKです。

2,3行目で自分のコードの2~9,11,13行目を行っています。

そして、3行目はなにもないのでスルーしてOKです。

4行目は入力されたものを`janken`という関数に通して、出力しています。自分のコードだと、12,14行目にあたります。

自分のコードより洗練されています。

## おわりに

美しいコードを心がけて、Pythonのコードを書いていきたいと思います。

---

いいねとフォローをお願いします。

フォローされたらフォロー返しします。