【PHP】確認問題4-☆3:マルバツゲームを作ろう

記事内に商品プロモーションを含む場合があります

この記事の練習問題で使用する知識:
基礎文法(レッスン1)、制御構造(レッスン2)、関数(レッスン3)、データ構造(レッスン4)、クラス(レッスン5)

PHPのゲームコード一覧はこちら

<<前のページ PHP記事一覧 次のページ>>

確認問題:PHPで〇×ゲームを作ろう

PHPを使って、コンピュータと対戦するマルバツゲームを作成しましょう。

このプログラムではプレイヤーが「○」を使用し、コンピュータが「×」を使用して、順番にマス目を選びます。

プレイヤーが空いているマスを選択して印を付けた後、コンピュータがランダムにマスを選択します。

ゲームはプレイヤーまたはコンピュータの勝利、もしくは引き分けで終了します。

この問題の要件

以下の要件に従ってコードを完成させてください。

  1. ボードの初期化:1から9までの数字を格納した配列 $board を用意すること。
  2. ボードの表示:ボードは3×3形式で表示し、各マスには番号または記号を表示すること。
  3. 勝利条件のチェック:横、縦、斜めの3つが同じ記号でそろっているかを判定する関数 checkWin を作成すること。
  4. 引き分けの判定:ボードがすべて埋まっている場合に引き分けと判定する関数 isDraw を作成すること。
  5. コンピュータの動き:空いているマスからランダムに選択する computerMove 関数を実装すること。
  6. ゲームの進行:
    • プレイヤーとコンピュータが交互にマスを選択すること。
    • 入力値が無効またはすでに埋まっている場合は、再入力を促すこと。
  7. 終了条件:勝利または引き分け時にゲームを終了し、結果を表示すること。

ただし、以下のような実行結果となるコードを書くこと。

*****↓↓正解コードの実行結果の例↓↓*****

マルバツゲームを開始します!
1 | 2 | 3
--+---+--
4 | 5 | 6
--+---+--
7 | 8 | 9

どのマスに置きますか?(1-9で入力してください)> 1
コンピュータのターンです...
コンピュータが選んだマス: 5
○ | 2 | 3
--+---+--
4 | × | 6
--+---+--
7 | 8 | 9

どのマスに置きますか?(1-9で入力してください)> 3
コンピュータのターンです...
コンピュータが選んだマス: 7
○ | 2 | ○
--+---+--
4 | × | 6
--+---+--
× | 8 | 9

どのマスに置きますか?(1-9で入力してください)> 9
プレイヤーの勝ちです!
○ | 2 | ○
--+---+--
4 | × | 6
--+---+--
× | 8 | ○

この問題を解くヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

ヒント【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

<?php
// ボードの初期化
function initializeBoard() {
    /*
    【穴埋め問題1】ここで1から9までの数字を格納した配列を作成し、返すコードを書いてください。
    */
}

// ボードの表示
function displayBoard($board) {
    /*
    【穴埋め問題2】ここでボードを3行×3列の形式で表示するコードを書いてください。
    */
}

// 勝利チェック
function checkWin($board, $mark) {
    $winPatterns = [
        [0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], // 横
        [0, 3, 6], [ 1, 4, 7], [ 2, 5, 8], // 縦
        [0, 4, 8], [ 2, 4, 6]            // 斜め
    ];

    foreach ($winPatterns as $pattern) {
        /*
    【穴埋め問題3】ここで指定されたパターンが同じ記号で埋まっているかを判定するコードを書いてください。
    */
    }
    return false;
}

// 引き分けチェック
function isDraw($board) {
    /*
    【穴埋め問題4】ここでボードがすべて埋まっているかを判定するコードを書いてください。
    */
}

// コンピュータの選択
function computerMove($board) {
    /*
    【穴埋め問題5】ここで空いているマスをランダムに選択するコードを書いてください。
    */
}

// メイン処理
echo "マルバツゲームを開始します!" . PHP_EOL;

// 初期化
$board = initializeBoard();
$playerMark = "○";
$computerMark = "×";

while (true) {
    // ボード表示
    displayBoard($board);

    // プレイヤーのターン
    while (true) {
        echo "どのマスに置きますか?(1-9で入力してください)> ";
        $input = trim(fgets(STDIN)) - 1;

        if (isset($board[$input]) && !in_array($board[$input], ["○", "×"])) {
            $board[$input] = $playerMark;
            break;
        } else {
            echo "そのマスはすでに埋まっています。別のマスを選んでください。" . PHP_EOL;
        }
    }

    // 勝利チェック
    if (checkWin($board, $playerMark)) {
        displayBoard($board);
        echo "プレイヤーの勝ちです!" . PHP_EOL;
        break;
    }

    // 引き分けチェック
    if (isDraw($board)) {
        displayBoard($board);
        echo "引き分けです!" . PHP_EOL;
        break;
    }

    // コンピュータのターン
    echo "コンピュータのターンです..." . PHP_EOL;
    $compMove = computerMove($board);
    $board[$compMove] = $computerMark;
    echo "コンピュータが選んだマス: " . ($compMove + 1) . PHP_EOL;

    // 勝利チェック
    if (checkWin($board, $computerMark)) {
        displayBoard($board);
        echo "コンピュータの勝ちです!" . PHP_EOL;
        break;
    }

    // 引き分けチェック
    if (isDraw($board)) {
        displayBoard($board);
        echo "引き分けです!" . PHP_EOL;
        break;
    }
}

この問題の穴埋めコードは以上です。

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

【コードの例】〇×ゲーム

この問題の一つの正解例とそのコードの解説を以下に示します。

正解コードの例

例えば以下のようなプログラムが考えられます。

<?php
// ボードの初期化
function initializeBoard() {
    return range(1, 9);
}

// ボードの表示
function displayBoard($board) {
    echo "{$board[ 0]} | {$board[ 1]} | {$board[ 2]}" . PHP_EOL;
    echo "--+---+--" . PHP_EOL;
    echo "{$board[ 3]} | {$board[ 4]} | {$board[ 5]}" . PHP_EOL;
    echo "--+---+--" . PHP_EOL;
    echo "{$board[6]} | {$board[7]} | {$board[8]}" . PHP_EOL;
}

// 勝利チェック
function checkWin($board, $mark) {
    $winPatterns = [
        [0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], // 横
        [0, 3, 6], [ 1, 4, 7], [ 2, 5, 8], // 縦
        [0, 4, 8], [ 2, 4, 6]            // 斜め
    ];

    foreach ($winPatterns as $pattern) {
        if ($board[$pattern[ 0]] === $mark && $board[$pattern[ 1]] === $mark && $board[$pattern[ 2]] === $mark) {
            return true;
        }
    }
    return false;
}

// 引き分けチェック
function isDraw($board) {
    foreach ($board as $cell) {
        if (!in_array($cell, ["○", "×"])) {
            return false;
        }
    }
    return true;
}

// コンピュータの選択
function computerMove($board) {
    $available = array_filter($board, fn($cell) => !in_array($cell, ["○", "×"]));
    return array_rand($available);
}

// メイン処理
echo "マルバツゲームを開始します!" . PHP_EOL;

// 初期化
$board = initializeBoard();
$playerMark = "○";
$computerMark = "×";

while (true) {
    // ボード表示
    displayBoard($board);

    // プレイヤーのターン
    while (true) {
        echo "どのマスに置きますか?(1-9で入力してください)> ";
        $input = trim(fgets(STDIN)) - 1;

        if (isset($board[$input]) && !in_array($board[$input], ["○", "×"])) {
            $board[$input] = $playerMark;
            break;
        } else {
            echo "そのマスはすでに埋まっています。別のマスを選んでください。" . PHP_EOL;
        }
    }

    // 勝利チェック
    if (checkWin($board, $playerMark)) {
        displayBoard($board);
        echo "プレイヤーの勝ちです!" . PHP_EOL;
        break;
    }

    // 引き分けチェック
    if (isDraw($board)) {
        displayBoard($board);
        echo "引き分けです!" . PHP_EOL;
        break;
    }

    // コンピュータのターン
    echo "コンピュータのターンです..." . PHP_EOL;
    $compMove = computerMove($board);
    $board[$compMove] = $computerMark;
    echo "コンピュータが選んだマス: " . ($compMove + 1) . PHP_EOL;

    // 勝利チェック
    if (checkWin($board, $computerMark)) {
        displayBoard($board);
        echo "コンピュータの勝ちです!" . PHP_EOL;
        break;
    }

    // 引き分けチェック
    if (isDraw($board)) {
        displayBoard($board);
        echo "引き分けです!" . PHP_EOL;
        break;
    }
}

正解コードの解説

このPHPコードはコンピュータと対戦するマルバツゲームを作成するものです。

以下にコードをブロックごとに分けて詳しく解説します。

ボードの初期化

function initializeBoard() {
    return range(1, 9);
}
  • range(1, 9) は、1から9までの連続した数値を含む配列を作成します。
  • この配列は、マス目の番号を表す初期状態のボードとして使われます。
  • return 文は関数の結果を返します。この場合は1〜9の数字が格納された配列です。

ボードの表示

function displayBoard($board) {
    echo "{$board[ 0]} | {$board[ 1]} | {$board[ 2]}" . PHP_EOL;
    echo "--+---+--" . PHP_EOL;
    echo "{$board[ 3]} | {$board[ 4]} | {$board[ 5]}" . PHP_EOL;
    echo "--+---+--" . PHP_EOL;
    echo "{$board[ 6]} | {$board[ 7]} | {$board[ 8]}" . PHP_EOL;
}
  • ボードを3行×3列の形式で表示します。
  • $board[0] のように、配列から指定されたインデックスの値を取り出して表示しています。
  • PHP_EOL は改行コードを表します。プラットフォームごとに適切な改行が挿入されます。
  • この関数を呼び出すたびに、最新のボードの状態が表示されます。

勝利チェック

function checkWin($board, $mark) {
    $winPatterns = [
        [0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], // 横
        [0, 3, 6], [ 1, 4, 7], [ 2, 5, 8], // 縦
        [0, 4, 8], [ 2, 4, 6]            // 斜め
    ];

    foreach ($winPatterns as $pattern) {
        if ($board[$pattern[0]] === $mark && $board[$pattern[ 1]] === $mark && $board[$pattern[ 2]] === $mark) {
            return true;
        }
    }
    return false;
}
  • $winPatterns は、勝利パターンを表す配列です。各パターンは勝利条件(横、縦、斜め)を表しています。
  • foreach ループを使って、各パターンをチェックします。
  • $board[$pattern[0]] === $mark では、指定された位置のマスがすべて同じ記号 ( または ×) で埋まっているか確認します。
  • 勝利条件が満たされた場合は true を返し、満たされない場合は最後に false を返します。

引き分けチェック

function isDraw($board) {
    foreach ($board as $cell) {
        if (!in_array($cell, ["○", "×"])) {
            return false;
        }
    }
    return true;
}
  • ボード上に空きマスがあるかどうかを確認します。
  • in_array 関数は、値が配列に含まれているかどうかを判定します。
  • 空きマス(1〜9の数字)が残っている場合は false を返し、すべて埋まっている場合は true を返します。

コンピュータの動き

function computerMove($board) {
    $available = array_filter($board, fn($cell) => !in_array($cell, ["○", "×"]));
    return array_rand($available);
}
  • array_filter を使用して、空いているマスだけを抽出します。
  • fn($cell) はPHPの無名関数(アロー関数)で、条件に一致する要素だけを選択します。
  • array_rand はランダムにキーを選択します。これによりコンピュータはランダムにマスを選択します。

メイン処理の開始

echo "マルバツゲームを開始します!" . PHP_EOL;

// 初期化
$board = initializeBoard();
$playerMark = "○";
$computerMark = "×";
  • echo で開始メッセージを表示します。
  • ボードを初期化し、プレイヤーとコンピュータの記号を設定します。

ゲームの進行

while (true) {
    displayBoard($board);

    // プレイヤーのターン
    while (true) {
        echo "どのマスに置きますか?(1-9で入力してください)> ";
        $input = trim(fgets(STDIN)) - 1;

        if (isset($board[$input]) && !in_array($board[$input], ["○", "×"])) {
            $board[$input] = $playerMark;
            break;
        } else {
            echo "そのマスはすでに埋まっています。別のマスを選んでください。" . PHP_EOL;
        }
    }
  • プレイヤーが入力した値をチェックし、空いているマスにのみ記号を置くことができます。
  • trim(fgets(STDIN)) でユーザー入力を受け取り、余分な空白を削除します。
  • 入力が無効な場合は再入力を促します。

勝敗の判定と終了処理

if (checkWin($board, $playerMark)) {
    displayBoard($board);
    echo "プレイヤーの勝ちです!" . PHP_EOL;
    break;
}

if (isDraw($board)) {
    displayBoard($board);
    echo "引き分けです!" . PHP_EOL;
    break;
}
  • 勝利条件を満たした場合は勝者を表示してゲーム終了。
  • 引き分け条件も確認し、満たしている場合は結果を表示して終了します。

まとめ

このコードはPHPの基本文法(関数、配列、ループ、条件分岐)を学ぶのに適したサンプルです。

ゲームの進行に必要な機能を関数に分けて実装することで、コードの見通しがよくなっています。

  • 勝利判定をより効率化する方法を学ぶ。
  • プレイヤー名を設定できるように改良する。
  • コンピュータの戦略を強化し、難易度を調整する機能を追加する。

このコードを土台にして、より高度なゲーム作りに挑戦してみてください!

PHPのゲームコード一覧はこちら

<<前のページ PHP記事一覧 次のページ>>

この記事への質問・コメント

この記事を作成するにあたりAIを活用しています。

問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。






    PHPテキスト&問題集へ戻る
    トップページへ戻る