じゃんけんゲームのコードがアイディア1つでシンプルになってちょっぴり感動したお話

## はじめに
プログラミング経験ほぼ0でPythonの独学を始めてからそろそろ半年、
学校の授業として50分×7コマ/週のペースで学び始めてからおよそ２ヶ月の私がプログラミングでちょっぴり感動した出来事をお話しします。

## 事の経緯
学校の課題でPythonを用いてじゃんけんゲームを作ることになりました。
課題を出される前に学校で習った内容は演算子,if文,for文に留まっていました。
私は独学によりwhile文や辞書型をある程度理解していたため、模範解答よりも少しリアルっぽい？じゃんけんゲームを作ることができました。

↓模範解答
```python
import random
for i in range(1000):
    user = int(input("1:グー、2:チョキ、3:パー、なにだす？"))
    com = random.randint(1, 3)
    print(user)
    print(com)
    if user == com:
        print("あいこ")
    elif user == 1 and com == 2:
        print("comはチョキ。あなたの勝ち")
        break
    elif user == 1 and com == 3:
        print("comはパー。あなたの負け")
        break
    elif user == 2 and com == 1:
        print("comはグー。あなたの勝ち")
        break
    elif user == 2 and com == 3:
        print("comはパー。あなたの負け")
        break
    elif user == 3 and com == 1:
        print("comはグー。あなたの勝ち")
        break
    elif user == 3 and com == 2:
        print("comはチョキ。あなたの負け")
        break
    else:
        print("1~3までの数字を入力してください")
```

↓私が書いたコード
```python
# 1. randomをインポートする
import random
# 辞書を定義して、じゃんけんの手とその対応する数値をマッピングする
choices = {"グー": 1, "チョキ": 2, "パー": 3}
computer_choice = random.randint(1, 3)
reverse_choices = {1: "グー", 2: "チョキ", 3: "パー"}
# 勝敗を判定する関数
def determine_winner(user_choice, computer_choice):
    if user_choice == computer_choice:
        return "あいこ"
    elif (user_choice == 1 and computer_choice == 2) or \
         (user_choice == 2 and computer_choice == 3) or \
         (user_choice == 3 and computer_choice == 1):
        return "あなたの勝ち"
    else:
        return "コンピューターの勝ち"
# じゃんけんゲームを実行する関数
def play_janken():
    while True:
        # 2. インプット関数で自分の入力値を変数に代入
        user_input = input("じゃんけんの手を入力してください（グー、チョキ、パー）：")
        user_choice = choices.get(user_input)
        # user_choiceがNoneなら無効な入力
        if user_choice is None:
            print("無効な入力です。もう一度入力してください。")
            continue
        # 3. コンピューターの出す整数をランダム(乱数)にする
        computer_choice = random.randint(1, 3)
        # 結果を判定
        result = determine_winner(user_choice, computer_choice)
        # 結果を表示
        print(f"あなたの手は {user_input} でした。")
        print(f"コンピューターの手は {reverse_choices[computer_choice]} でした。")
        print(result)
        # あいこの場合、再度じゃんけん
        if result == "あいこ":
            print("あいこで...")
        else:
            break
# じゃんけんゲームを開始
play_janken()
```
模範解答と私が書いたコードをユーザー側の目線で比較すると、
1. 模範解答側はグー、チョキ、パーに対応する数字(1,2,3)を入力する必要があり、
私が書いたコードではグー、チョキ、パーの文字列での入力になる。
1. 模範解答側は1,2,3以外の数、文字列などを入れるとValueErrorになりプログラムが終了する
私が書いたコードではグー、チョキ、パー以外の数、文字列などを入れると「無効な入力です。もう一度入力してください。」というメッセージが流れた後、再びじゃんけんの手の入力を求められる。
1. 模範解答側はユーザーが勝つか負けるまでorループが1000回繰り返されるまでプログラムが終了しない。
私が書いたコードではユーザーが有効な入力をし、ユーザーが勝つか負けるまでorユーザーが連続して無効な入力をし続ける限りプログラムが終了しない。
などが違いとして挙げられます。

まだ学校では習っていない範囲の知識を活用してプログラミングができたことにウキウキだった私に対して、**アイディア１つでよりシンプルなコードが書けること**を講師の方に気づかせていただきました。

https://ichikou.jp/wp/2023/10/04/python%E3%81%A7%EF%BC%92%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%AE%E3%81%98%E3%82%83%E3%82%93%E3%81%91%E3%82%93%E3%82%B2%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B/

↑こちらの記事にある２つ目のコード例を参考にしました。

```python
    elif (user_choice == 1 and computer_choice == 2) or \
         (user_choice == 2 and computer_choice == 3) or \
         (user_choice == 3 and computer_choice == 1):
```
この箇所はユーザー側が勝利する場合についての記述なのですが**割り算の余り**に着目することで、**一つの数式で全ての勝利条件をカバー**することができます。

↓割り算の余りに着目してシンプルになったコード
```python
# 1. randomをインポートする
import random

# 辞書を定義して、じゃんけんの手とその対応する数値をマッピングする
choices = {"グー": 0, "チョキ": 1, "パー": 2}
computer_choice = random.randint(0, 2)
reverse_choices = {0: "グー", 1: "チョキ", 2: "パー"}

# 勝敗を判定する関数
def determine_winner(user_choice, computer_choice):
    if user_choice == computer_choice:
        return "あいこ"
    elif (user_choice - computer_choice + 3) % 3 == 2:# ←三行必要だったコードが一行に！！！
        return "あなたの勝ち"
    else:
        return "コンピューターの勝ち"

# じゃんけんゲームを実行する関数
def play_janken():
    while True:
        # 2. インプット関数で自分の入力値を変数に代入
        user_input = input("じゃんけんの手を入力してください（グー、チョキ、パー）：")
        user_choice = choices.get(user_input)
        
        # user_choiceがNoneなら無効な入力
        if user_choice is None:
            print("無効な入力です。もう一度入力してください。")
            continue

        # 3. コンピューターの出す整数をランダム(乱数)にする
        computer_choice = random.randint(0, 2)

        # 結果を判定
        result = determine_winner(user_choice, computer_choice)

        # 結果を表示
        print(f"あなたの手は {user_input} でした。")
        print(f"コンピューターの手は {reverse_choices[computer_choice]} でした。")
        print(result)

        # あいこの場合、再度じゃんけん
        if result == "あいこ":
            print("あいこで...")
        else:
            break

# じゃんけんゲームを開始
play_janken()
```
実際、どちらのコードを採用したいかで考えると
割り算の余りに着目したコードはシンプルになった点が良く、
元のコードは各ケースの勝利条件を個別に確認できるためコードを読みやすい点がメリットになるので、ここまで来ると好みの範疇になるのかな？と思いました。
## おわりに
じゃんけんゲームという簡単なプログラム１つを取っても、工夫できる余地があったことからプログラミングの奥深さを再認識しました。