【Ruby】じゃんけんプログラム

# 仕様
- ユーザーにじゃんけんの手を選んでもらう。（0：グー、1：チョキ、2：パー）
- プログラム側では、ユーザーと勝負するじゃんけんの手をランダムに選ぶ。
- 「ユーザーが選んだじゃんけんの手」と「プログラムがランダムに選んだじゃんけんの手」を表示する。
- ユーザーが勝った場合は「あなたの勝ちです。」と表示する。
- ユーザーが負けた場合は「あなたの負けです。」と表示する。
- あいこの場合は 「あいこで」と表示させたあともう一度じゃんけんをさせる。
- あいこが3回続いた場合は、「3回あいこが続きました。また挑戦してください。」と表示させたあとにプログラムを終了させる。

# 自分のコードとChatGPTのコードの比較
### 自分のコード
```ruby:janken
# 配列とあいこカウント用変数の定義
janken_hands = ["グー", "チョキ", "パー"]
aiko_count = 0

puts "最初はグー、じゃんけん..."

# 3回ループさせる
while aiko_count < 3
  if aiko_count > 0
    puts "あいこで"
  end
  puts "[0]:グー [1]:チョキ [2]:パー"

# ユーザの入力受取
  usr_hand = gets.to_i
# 不正な入力の場合はメッセージを出力
  while usr_hand < 0 || usr_hand > 2
    puts "半角の「0」、「1」、「2」のいずれかを入力してください。" 
    usr_hand = gets.to_i
  end
# プログラム側の手を生成
  pgm_hand = rand(3)
  
  puts "あなたの手:#{janken_hands[usr_hand]}, わたしの手:#{janken_hands[pgm_hand]}"

# 勝負がついたらそこで終了
  if usr_hand - pgm_hand == -1 || usr_hand - pgm_hand == 2
    puts "あなたの勝ちです。"
    break
  elsif usr_hand - pgm_hand == -2 || usr_hand - pgm_hand == 1
    puts "あなたの負けです。"
    break
  else
    aiko_count += 1
  end
end

# あいこが3回続いた時のみ出力
if aiko_count == 3
  puts "3回あいこが続きました。また挑戦してください。"
end

__END__

パターン分け

usrの勝ち
  usr: 0 pgm: 1
  usr: 1 pgm: 2
  usr: 2 pgm: 0

pgmの勝ち
  usr: 0 pgm: 2
  usr: 1 pgm: 0
  usr: 2 pgm: 1

あいこ
  usr: 0 pgm: 0
  usr: 1 pgm: 1
  usr: 2 pgm: 2
```
ポイントは以下の２点です。
- 仕様には含まれていないが、ユーザの入力が不正である場合はメッセージを表示して正しい入力をしてもらうように促す。
- ユーザの手（`usr_hand`）と対戦相手のプログラムの手（`pgm_hand`）の差を勝敗の判定に利用する。
（`__END__`以下のコメントをご参照ください）

### ChatGPTのコード
以下は、要件を満たすRubyプログラムの例です。

```ruby
def judge(user, program)
  if user == program
    "draw"
  elsif (user == 0 && program == 1) || (user == 1 && program == 2) || (user == 2 && program == 0)
    "user"
  else
    "program"
  end
end

def print_result(user_hand, program_hand, result)
  puts "ユーザーの手: #{convert_hand(user_hand)}, プログラムの手: #{convert_hand(program_hand)}"
  case result
  when "user"
    puts "あなたの勝ちです。"
  when "program"
    puts "あなたの負けです。"
  when "draw"
    puts "あいこで"
  end
end

def convert_hand(hand)
  case hand
  when 0
    "グー"
  when 1
    "チョキ"
  when 2
    "パー"
  end
end

def main
  user_draw_count = 0
  while user_draw_count < 3
    puts "じゃんけんの手を選んでください（0：グー、1：チョキ、2：パー）："
    user_hand = gets.chomp.to_i
    unless [0, 1, 2].include?(user_hand)
      puts "無効な入力です。再度入力してください。"
      next
    end

    program_hand = rand(3)
    result = judge(user_hand, program_hand)
    print_result(user_hand, program_hand, result)

    if result == "draw"
      user_draw_count += 1
    else
      user_draw_count = 0
    end
  end
  puts "3回あいこが続きました。また挑戦してください。"
end

main
```

このプログラムでは、`judge`メソッドでじゃんけんの勝敗を判定し、`print_result`メソッドで結果を出力しています。`main`メソッドでは、ユーザーがじゃんけんを選択し、結果を表示します。また、あいこが3回続いた場合にはループを終了し、終了メッセージを表示します。

### 学び
- ChatGPTは機能ごとにメソッドを定義しています。これが「「オブジェクト指向」なのでしょうか？
このようなコードを書けるようになりたいものです。
- ChatGPTも不正な入力の判定をしています。`include?()`は[ドットインストール](https://dotinstall.com)で学んだ気がします。せっかく学んだことを使いこなせていないのが残念...
- やはり自分以外の何者か（AI含む）が書いたコードを読むのは勉強になります。（現職の新人研修の講師の先生もおっしゃっていました）

# ChatGPTによるコードレビュー
このコードは、じゃんけんゲームの基本的な機能を実装しており、動作する点では問題ありませんが、いくつかの改善点があります。以下にレビューを行います。

1. コード内のコメントや変数名は理解しやすくて良いですが、コメントが冗長に感じられる場合があります。コメントがコードの意図を明確に伝えている場合は問題ありませんが、コメントが明らかな場合は省略することも検討できます。

2. 不正な入力を検出するためのループがありますが、`gets.to_i`では改行文字が数値に変換されてしまうため、不適切な入力が無視されずに無限ループに陥る可能性があります。この問題を解決するためには、`gets.chomp.to_i`のように`.chomp`を使用して改行文字を削除する必要があります。

3. コードの条件式が少し複雑であり、理解しにくい部分があります。条件式を読みやすくするために、関数や変数を使用して条件を説明することができます。

4. あいこが発生した場合、カウンターがインクリメントされていますが、次のループで条件を満たさなくても、カウンターが増加することになります。これは、不必要なカウントが行われる可能性があります。あいこが発生した場合は、カウンターをリセットする必要があります。

5. `usr_hand - pgm_hand`の計算は、条件を判定するために使用されていますが、可読性を向上させるために、結果を予め計算して変数に格納することを検討することができます。

6. コードの終了後、`__END__`マーカーの下にコメントがあります。このコメントは、コードの機能とは関係がないため、不要です。

これらのポイントを考慮して、コードを改善することができます。以下は、修正したコードの例です。

```ruby
janken_hands = ["グー", "チョキ", "パー"]
aiko_count = 0

puts "最初はグー、じゃんけん..."

while aiko_count < 3
  if aiko_count > 0
    puts "あいこで"
  end
  puts "[0]:グー [1]:チョキ [2]:パー"

  usr_hand = gets.chomp.to_i
  
  # 不正な入力をチェックする
  until (0..2).include?(usr_hand)
    puts "半角の「0」、「1」、「2」のいずれかを入力してください。" 
    usr_hand = gets.chomp.to_i
  end

  pgm_hand = rand(3)
  
  puts "あなたの手:#{janken_hands[usr_hand]}, わたしの手:#{janken_hands[pgm_hand]}"

  result = usr_hand - pgm_hand

  if result == 1 || result == -2
    puts "あなたの勝ちです。"
    break
  elsif result == -1 || result == 2
    puts "あなたの負けです。"
    break
  else
    aiko_count += 1
  end
end

if aiko_count == 3
  puts "3回あいこが続きました。また挑戦してください。"
end
```

このようにすることで、コードがより読みやすくなり、理解しやすくなります。

### 疑義
この中で特に修正が必要な点は、プログラムが正常に動作しない恐れのある「2」と「4」かと思う。
しかし、「4」で「（前略）カウンターをリセットする必要があります。」と書いてあるが、修正後のコードでカウンターをリセットしている箇所が見当たらない...

### ChatGPTへの質問
「カウンターをリセットする必要があります。」とのことですが、それは修正後のコードのどの箇所のことですか？

### ChatGPTの回答
申し訳ありません、その部分は修正後のコードには含まれていませんでした。元のコードでは、あいこの場合にカウンターが増加しますが、あいこが解消された後にそのカウンターがリセットされるべきです。つまり、`aiko_count`が再び0に戻る必要があります。

修正後のコードでは、`aiko_count`が再び0に戻る処理が不足していました。修正後のコードに以下のような行を追加することで、この問題を解決できます。

```ruby
  else
    aiko_count = 0  # あいこが解消された場合、カウンターをリセットする
```

これにより、あいこのカウントが解消された場合に、`aiko_count`がリセットされるようになります。

### ChatGPTの模範解答を確認
申し訳ございませんが、以前の修正を行ったコードに、さらにリセット処理を追加したものが以下です。

```ruby
janken_hands = ["グー", "チョキ", "パー"]
aiko_count = 0

puts "最初はグー、じゃんけん..."

while aiko_count < 3
  if aiko_count > 0
    puts "あいこで"
  end
  puts "[0]:グー [1]:チョキ [2]:パー"

  usr_hand = gets.chomp.to_i
  
  # 不正な入力をチェックする
  until (0..2).include?(usr_hand)
    puts "半角の「0」、「1」、「2」のいずれかを入力してください。" 
    usr_hand = gets.chomp.to_i
  end

  pgm_hand = rand(3)
  
  puts "あなたの手:#{janken_hands[usr_hand]}, わたしの手:#{janken_hands[pgm_hand]}"

  result = usr_hand - pgm_hand

  if result == 1 || result == -2
    puts "あなたの勝ちです。"
    break
  elsif result == -1 || result == 2
    puts "あなたの負けです。"
    break
  else
    aiko_count += 1
    aiko_count = 0 if aiko_count == 3  # あいこが解消された場合、カウンターをリセットする
  end
end

if aiko_count == 3
  puts "3回あいこが続きました。また挑戦してください。"
end
```

この修正により、あいこのカウントが解消された場合に、`aiko_count`がリセットされるようになります。

**ん？？？**
あいこが解消？したらカウンターをリセット？
分からなくなってしまったので、一旦ここで切ります。