PHPコマンドラインじゃんけんゲーム開発：自分のための備忘録

この記事は、PHP学習中の私が、コマンドラインで動くシンプルなじゃんけんゲームを作ってみた過程で、**「なぜか動かない！」「どうしてこうなるんだ？」**と頭を抱えたポイントとその解決策を自分のために記録しておく備忘録です。

プログラミングの基礎中の基礎であるはずなのに、いざコードを書いてみると、論理値の扱いやループの制御、変数の型の問題など、思わぬところでつまずくことが多々ありました。特に、エラーメッセージを見てもピンとこなかったり、自分の書いたコードのどこが悪いのか全く分からず、ただただ時間が溶けていく感覚に陥ったことも...。

しかし、一つ一つの「なぜ？」を解消していくことで、PHPの基本的な挙動や、プログラミングにおけるデバッグの楽しさを少しずつ理解できました。

ということで、じゃんけんゲームのコードと共に、つまづいたところを振り返ってみます。


# 完成したコード

```php

$hands = [1 => 'グー', 2 => 'チョキ', 3 => 'パー'];

while(true){

  echo 'じゃんけんゲーム' . PHP_EOL;
  echo '1.グー' . PHP_EOL;
  echo '2.チョキ' . PHP_EOL;
  echo '3.パー' . PHP_EOL;
  echo 'じゃんけんの手を選んでください：';

  // プレイヤーの手
  $playerHandInput = trim(fgets(STDIN));

  // コンピューターの手
  $computerHand = array_rand($hands);

  // 入力確認
  if(!is_numeric($playerHandInput) || !array_key_exists($playerHandInput,$hands)){
    echo '-------------------------------------' . PHP_EOL;
    echo '1、2、3のいずれかの数字を入力してください' . PHP_EOL;
    echo '-------------------------------------' . PHP_EOL;
    continue;
  }

  //プレイヤーの手を整数型にキャスト
  $playerHand = (int)$playerHandInput;

  // 結果表示
  echo 'あなたは「' . $hands[$playerHand] . '」をだしました' . PHP_EOL;
  echo 'コンピューターは「' . $hands[$computerHand] . '」をだしました' . PHP_EOL . PHP_EOL;

  // 勝敗判定
  if(
    ($playerHand === 1 && $computerHand === 2) ||
    ($playerHand === 2 && $computerHand === 3) ||
    ($playerHand === 3 && $computerHand === 1)
  ){
    echo 'あなたの勝利です。' . PHP_EOL;
    }
  elseif(
    ($playerHand === 1 && $computerHand === 3) ||
    ($playerHand === 2 && $computerHand === 1) ||
    ($playerHand === 3 && $computerHand === 2)
  ){
    echo 'コンピューターの勝利です。' . PHP_EOL;
  }
  else{
    echo 'あいこです。' . PHP_EOL;
  }

  //もう一度遊ぶか確認ループ
  while(true){
    echo 'もう一度遊びますか？(Y/N):';
    $playAgain = mb_strtoupper(trim(fgets(STDIN)));

    if($playAgain === 'Y'){
      break;
    } elseif ($playAgain === 'N'){
      echo 'ゲームを終了します' . PHP_EOL;
      exit;
    } else {
      echo '--------------------------' . PHP_EOL;
      echo 'YもしくはNを入力してください' . PHP_EOL;
      echo '--------------------------' . PHP_EOL;
    }

  }

}


```


## つまずきポイント1：なぜか常に「あいこ」になる？
「グー」「チョキ」「パー」を選んでも、常に「あいこです」と表示されてしまう現象に直面しました。

### 原因
if 文の条件式で、複数の条件を || (論理OR) ではなく . (文字列結合) でつないでいました。
PHPでは論理値を文字列に変換すると、true は "1"、false は ""（空文字列）になります。
また、if 文での文字列の評価が 空ではない文字列は true、空文字列は false と評価されます。
なので、期待する条件が正しく評価されず、常に false と判定されていました。

### 解決策
下記のコードの通り、|| (論理OR) に変更しました。

```php
  // 勝敗判定
  if(
    ($playerHand === 1 && $computerHand === 2) ||
    ($playerHand === 2 && $computerHand === 3) ||
    ($playerHand === 3 && $computerHand === 1)
  ){
    echo 'あなたの勝利です。' . PHP_EOL;
    }
  elseif(
    ($playerHand === 1 && $computerHand === 3) ||
    ($playerHand === 2 && $computerHand === 1) ||
    ($playerHand === 3 && $computerHand === 2)
  ){
    echo 'コンピューターの勝利です。' . PHP_EOL;
  }
  else{
    echo 'あいこです。' . PHP_EOL;
  }
  ```

  ## つまずきポイント2：途中で処理が止まる？ループ制御 continue と break の使い分け

  「入力エラー時にゲームが終了してしまう」「入力エラー後にまた最初から手を出し直させたい」など、ループの挙動をコントロールしたいと思ったがうまくいきませんでした。


  ### 原因
continue と break の違いを明確に理解していませんでした。

  ### continue と break について整理
  **continue:**
現在のループの残りの処理をスキップし、次の繰り返しに進む。
じゃんけんゲームでの適用例: プレイヤーの入力が不正だった場合、その回の対戦を中断し、もう一度手を選び直させる（continue でループの先頭に戻る）。

  ```php
for ($i = 1; $i <= 5; $i++) {
    if ($i === 3) {
        echo $i . "スキップします" . PHP_EOL;
        continue; // $i が 3 の場合、ここから下の処理は実行されずに次のループ ($i = 4) に進む
    }
    echo $i . "：通常の処理" . PHP_EOL;
}
echo "ループ終了" . PHP_EOL;
  ```

 ```
1：通常の処理
2：通常の処理
3：スキップします
4：通常の処理
5：通常の処理
ループ終了
  ```
  **break:**
ループ自体を完全に終了させ、ループの直後のコードへ処理を移す。
じゃんけんゲームでの適用例: 「もう一度遊びますか？」で N を選んだ場合、じゃんけんゲームのメインループを完全に終了させる。


  ```php
for ($i = 1; $i <= 5; $i++) {
    if ($i === 3) {
        echo $i . "ループを終了します" . PHP_EOL;
        break; // $i が 3 の場合、ここでループ自体が完全に終了する
    }
    echo $i . "：通常の処理" . PHP_EOL;
}
echo "ループ終了" . PHP_EOL;
  ```

 ```
1：通常の処理
2：通常の処理
3：ループを終了します
ループ終了
  ```

## つまずきポイント3：入力が数字なのに判定がおかしい？型キャストの重要性

プレイヤーが 1 を入力したのに、$playerHand === 1 の比較が false になることがありました。

  ### 原因

fgets(STDIN) や trim() で取得したユーザー入力は、たとえ数字であっても文字列型。
また、PHPの厳密な比較演算子 === は、値だけでなく型も一致しないと true にならない。
文字列の "1" と数値の 1 は、型が異なるため === では false になる。

### 解決策
下記のように、ユーザー入力を明示的に整数型に型キャストしました。

  ```
$playerHand = (int)$playerHandInput;

  ```

  ##  補足：PHPの比較演算子
== は予期せぬエラーにつながるので ===で書く方が良い
  

| 特徴                     | `==`（等しい）                                | `===`（厳密に等しい）                          |
|--------------------------|-----------------------------------------------|------------------------------------------------|
| 比較基準                 | 値が等しいかどうか                             | 値と型の両方が等しいかどうか                   |
| 型変換（自動）           | 行う（true になるよう型を揃えようとする）       | 行わない                                       |
| 例1: 数値と文字列        | `5 == "5"` は `true`                           | `5 === "5"` は `false`                         |
| 例2: 真偽値と数値        | `true == 1` は `true`                         | `true === 1` は `false`                        |
| 例3: null と 0           | `null == 0` は `false`（※注：誤りが多いため要注意） | `null === 0` は `false`                        |
| 例4: null と false       | `null == false` は `true`（※注：誤解されやすい） | `null === false` は `false`                    |
| 推奨される利用場面       | 型変換を活かした比較を意図的に使う場合         | ほとんどのケースで推奨。バグ防止のために使う。 |
| 安全性                   | 低い（意図しない型変換でバグの元に）            | 高い（型も比較するため意図が明確）             |



## まとめ
じゃんけんゲーム作成を通して、PHPの基礎（変数スコープ、条件分岐、ループ制御、演算子）の理解が深まりました。
エラーが出たときに、まずは「構文エラー」「論理エラー」のどちらかを疑い、冷静にコードを見直すことが重要性だと感じました。
引き続き学習を続けていきたいと思います。