【Kotlin】確認問題4-☆3:宝探しゲームを作ろう

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

この記事の練習問題で使用する知識:
基礎文法、制御構造(レッスン1~2)関数の定義と呼出しデフォルト引数とキーワード引数関数の戻り値Listコレクション操作コレクションとジェネリクス

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

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

確認問題:宝探しゲームを作ろう

5×5のグリッド上で宝を探す「宝探しゲーム」を作成しましょう。

このゲームではプレイヤーが1~25の数字を選び、ランダムに配置された宝を見つけ出します。

選択した場所にはマークを付け、プレイヤーにヒントを表示します。プレイヤーは3回の試行内に宝を見つけることを目指します。

この問題の要件

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

  • グリッドの初期化:5×5のグリッドを生成し、1~25の番号を表示すること。
  • 宝の位置のランダム化:宝の位置をランダムに決定し、プレイヤーには非表示にすること。
  • プレイヤー入力の処理:プレイヤーが1~25の数字を入力できるようにし、対応するグリッド位置を選択すること。
    無効な入力(範囲外または既に選択された場所)にはエラーメッセージを表示すること。
  • ヒントの提供:宝の位置に基づいて、「右」「左」「上」「下」の方向を示すヒントを表示すること。
  • 終了条件:プレイヤーが宝を見つけるか、3回の試行が終了した時点でゲームを終了すること。
  • ゲームの出力:グリッドをプレイ中に更新し、選択済みの場所には「*」、宝の場所には「☆」を表示すること。

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

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

宝探しゲームへようこそ!1から25までの数字で宝を探し当ててください。
宝を探すチャンスは3回です。
現在のグリッド:
01 02 03 04 05
06 07 08 09 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25

1から25のマス番号を入力してください: 7
右 下 に宝があります。
残りのチャンス: 2
現在のグリッド:
01 02 03 04 05
06 * 08 09 10
11 12 13 14 15
16 17 18 19 20
21 22 23 24 25

1から25のマス番号を入力してください: 18
おめでとうございます!宝を見つけました!
現在のグリッド:
01 02 03 04 05
06 * 08 09 10
11 12 13 14 15
16 17 ☆ 19 20
21 22 23 24 25

この問題を解くヒント

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

ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。
(※下記の□はコード内のインデントを表しています)

1:import kotlin.random.Randomをインポート
2:main関数の定義
  □ 変数gridSizeを初期化(値は5)
  □ 変数gridMutableListで初期化し、5×5のグリッドを生成
  □ 変数treasureXをランダムな数値で初期化(範囲:0~4)
  □ 変数treasureYをランダムな数値で初期化(範囲:0~4)
  □ 変数attemptsを初期化(値は3)
  □ 変数treasureFoundを初期化(値はfalse
  □ 「宝探しゲームの開始メッセージ」を出力
  □ 「チャンスの回数を出力」
  □ displayGrid関数を呼び出し、初期状態のグリッドを表示
  □ whileループの開始条件:attempts > 0かつ!treasureFound
    □ 「1から25のマス番号を入力してください: 」とプロンプトを表示
    □ ユーザー入力を読み取り、toIntOrNullで数値化し、変数inputに代入
    □ if文でinputの値を検証(条件:無効な値かどうか)
      □ 条件がtrueの場合、エラーメッセージを出力してcontinue
    □ 入力されたinputplayerXplayerYに変換(グリッドの座標を計算)
    □ if文で座標が既に選択されているか判定
      □ 条件がtrueの場合、警告メッセージを出力してcontinue
    □ if文でプレイヤーの選択が宝の位置と一致するか判定
      □ 条件がtrueの場合、「宝を発見したメッセージ」を出力
      □ グリッドに"☆"を設定し、treasureFoundtrueに設定
      □ elseブロック:
        □ 外れた場合、グリッドに"*"を設定
        □ giveHint関数を呼び出し、ヒントを出力
        □ 残りのチャンスを1減らし、現在のチャンス数を出力
    □ displayGrid関数を呼び出し、最新のグリッドを表示
  □ if文で!treasureFoundの場合、「ゲームオーバーメッセージ」を出力
3:displayGrid関数の定義
  □ 「現在のグリッド」としてラベルを出力
  □ グリッドの各行を出力するためのループ処理
4:giveHint関数の定義
  □ リストhintmutableListOfで初期化
  □ if文でヒントの方向(右/左/上/下)を判定しリストに追加
  □ ヒントメッセージを生成して戻り値として返す

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

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

import kotlin.random.Random

fun main() {
    val gridSize = 5

    /*
    【穴埋め問題1】ここにMutableListを使って5×5のグリッドを初期化するコードを書いてください。
    */

    /*
    【穴埋め問題2】ここにRandomを使用して、宝物のX座標をランダムに決定するコードを書いてください。
    */

    /*
    【穴埋め問題3】ここにRandomを使用して、宝物のY座標をランダムに決定するコードを書いてください。
    */

    var attempts = 3
    var treasureFound = false

    println("宝探しゲームへようこそ!1から25までの数字で宝を探し当ててください。")
    println("宝を探すチャンスは3回です。")
    displayGrid(grid)

    while (attempts > 0 && !treasureFound) {
        print("1から25のマス番号を入力してください: ")

        /*
        【穴埋め問題4】ここでユーザーからの入力を数値に変換し、変数inputに代入するコードを書いてください。
        */

        /*
        【穴埋め問題5】ここで入力が無効である場合にエラーメッセージを出力し、ループをスキップするコードを書いてください。
        */

        val playerX = (input - 1) % gridSize
        val playerY = (input - 1) / gridSize

        if (grid[playerY][playerX] == "*") {
            println("そのマスは既に選択されています。別のマスを選んでください。")
            continue
        }

        /*
        【穴埋め問題6】ここでプレイヤーが宝物を見つけた場合の処理を書くコードを書いてください。
        */

        grid[playerY][playerX] = "*"
        println(giveHint(playerX, playerY, treasureX, treasureY))
        attempts--
        println("残りのチャンス: $attempts")
        displayGrid(grid)
    }

    if (!treasureFound) {
        println("残念ながら、チャンスを使い切りました。宝は見つかりませんでした。")
    }
}

fun displayGrid(grid: List<List<String>>) {
    println("現在のグリッド:")
    for (row in grid) {
        println(row.joinToString(" "))
    }
    println()
}

fun giveHint(playerX: Int, playerY: Int, treasureX: Int, treasureY: Int): String {
    val hint = mutableListOf<String>()
    if (playerX < treasureX) hint.add("右")
    if (playerX > treasureX) hint.add("左")
    if (playerY < treasureY) hint.add("下")
    if (playerY > treasureY) hint.add("上")
    return "${hint.joinToString(" ")}に宝があります。"
}

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

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

宝探しゲームの正解コードと解説

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

正解コードの例

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

import kotlin.random.Random

fun main() {
    val gridSize = 5
    val grid = MutableList(gridSize) { row -> MutableList(gridSize) { col -> String.format("%02d", row * gridSize + col + 1) } }
    val treasureX = Random.nextInt(gridSize)
    val treasureY = Random.nextInt(gridSize)
    var attempts = 3
    var treasureFound = false

    println("宝探しゲームへようこそ!1から25までの数字で宝を探し当ててください。")
    println("宝を探すチャンスは3回です。")
    displayGrid(grid)

    while (attempts > 0 && !treasureFound) {
        print("1から25のマス番号を入力してください: ")
        val input = readLine()?.toIntOrNull()

        if (input == null || input < 1 || input > 25) {
            println("無効な番号です。1から25までの数字を入力してください。")
            continue
        }

        val playerX = (input - 1) % gridSize
        val playerY = (input - 1) / gridSize

        if (grid[playerY][playerX] == "*") {
            println("そのマスは既に選択されています。別のマスを選んでください。")
            continue
        }

        if (playerX == treasureX && playerY == treasureY) {
            println("おめでとうございます!宝を見つけました!")
            grid[playerY][playerX] = "☆"
            treasureFound = true
        } else {
            grid[playerY][playerX] = "*"
            println(giveHint(playerX, playerY, treasureX, treasureY))
            attempts--
            println("残りのチャンス: $attempts")
        }

        displayGrid(grid)
    }

    if (!treasureFound) {
        println("残念ながら、チャンスを使い切りました。宝は見つかりませんでした。")
    }
}

fun displayGrid(grid: List<List<String>>) {
    println("現在のグリッド:")
    for (row in grid) {
        println(row.joinToString(" "))
    }
    println()
}

fun giveHint(playerX: Int, playerY: Int, treasureX: Int, treasureY: Int): String {
    val hint = mutableListOf<String>()
    if (playerX < treasureX) hint.add("右")
    if (playerX > treasureX) hint.add("左")
    if (playerY < treasureY) hint.add("下")
    if (playerY > treasureY) hint.add("上")
    return "${hint.joinToString(" ")}に宝があります。"
}

正解コードの解説

このコードではKotlinを使用して5×5のグリッド上で宝を探すゲームを実装しています。

以下にコードの各部分を説明していきます。

ランダム値を生成するためのインポート

import kotlin.random.Random

Randomクラス: Kotlinのランダム値を生成するクラスです。

ここでは宝の位置をランダムに設定するために使用します。

メイン関数の開始

fun main() {
    ...
}

fun main(): プログラムのエントリーポイント。ここからゲームの処理が始まります。

グリッドの初期化

val gridSize = 5
val grid = MutableList(gridSize) { row -> MutableList(gridSize) { col -> String.format("%02d", row * gridSize + col + 1) } }
  • gridSize: グリッドのサイズを定義します。5を設定しているので5×5のグリッドを作成します。
  • MutableList: 変更可能なリスト。ここでは5×5のグリッドを二次元リストで作成しています。
  • String.format: 各マスに「01」や「25」のように番号を割り当て、視覚的にわかりやすくしています。

宝の位置をランダムに決定

val treasureX = Random.nextInt(gridSize)
val treasureY = Random.nextInt(gridSize)

Random.nextInt(gridSize): グリッドの範囲内でランダムなx座標(列)とy座標(行)を生成します。

これが宝の位置になります。

ゲーム開始メッセージの表示

println("宝探しゲームへようこそ!1から25までの数字で宝を探し当ててください。")
println("宝を探すチャンスは3回です。")
displayGrid(grid)
  • println: コンソールにメッセージを表示します。
  • displayGrid(grid): 現在のグリッドの状態を表示するためのカスタム関数(後述)。

プレイヤーの試行ループ

while (attempts > 0 && !treasureFound) {
    ...
}

while: 試行回数が0より大きい、または宝が見つかっていない間はループを続けます。

ユーザー入力の取得

val input = readLine()?.toIntOrNull()
  • readLine(): ユーザーからの入力を取得します。
  • toIntOrNull(): 入力された文字列を整数に変換します。無効な値の場合はnullを返します。

入力値のバリデーション

if (input == null || input < 1 || input > 25) {
    println("無効な番号です。1から25までの数字を入力してください。")
    continue
}

if文: 入力が無効な場合(1~25の範囲外やnull)、エラーメッセージを表示して再入力を促します。

宝の判定とヒントの表示

if (playerX == treasureX && playerY == treasureY) {
    println("おめでとうございます!宝を見つけました!")
    grid[playerY][playerX] = "☆"
    treasureFound = true
} else {
    grid[playerY][playerX] = "*"
    println(giveHint(playerX, playerY, treasureX, treasureY))
    attempts--
}
  • if (playerX == treasureX && playerY == treasureY): プレイヤーの選択した場所が宝の位置と一致するかを判定します。
  • grid[playerY][playerX] = “☆”: 宝の場所を「☆」で表示します。
  • giveHint関数: 宝の方向をヒントとして提供します。

グリッドの更新表示

displayGrid(grid)

displayGrid: 現在のグリッド状態を再度表示します。

ゲーム終了の条件

if (!treasureFound) {
    println("残念ながら、チャンスを使い切りました。宝は見つかりませんでした。")
}

if (!treasureFound): 試行回数が尽き、宝が見つからなかった場合に終了メッセージを表示します。

カスタム関数: グリッドの表示

fun displayGrid(grid: List<List<String>>) {
    println("現在のグリッド:")
    for (row in grid) {
        println(row.joinToString(" "))
    }
    println()
}

joinToString: リスト内の要素を文字列として結合し、1行ずつグリッドを表示します。

カスタム関数: ヒントの提供

fun giveHint(playerX: Int, playerY: Int, treasureX: Int, treasureY: Int): String {
    val hint = mutableListOf<String>()
    if (playerX < treasureX) hint.add("右")
    if (playerX > treasureX) hint.add("左")
    if (playerY < treasureY) hint.add("下")
    if (playerY > treasureY) hint.add("上")
    return "${hint.joinToString(" ")}に宝があります。"
}
  • mutableListOf: 動的に要素を追加可能なリスト。
  • hint.add: プレイヤーの選択に基づいて方向のヒントを追加します。

まとめ

このコードでは、Kotlinの基本文法(リスト、ループ、条件分岐、関数)を使用しながら、簡単なゲームロジックを構築しています。

初心者にとって、以下の点が特に学びやすいポイントです:

  1. リストの操作: グリッドの作成と更新を学べる。
  2. ランダム値の利用: ゲームの変化を作り出す方法を学べる。
  3. 条件分岐とループ: ゲームロジックを処理する方法を学べる。

これをもとにさらに複雑なゲームや機能を作成することでKotlinの理解が深まります。ぜひ挑戦してみてください!

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

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

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

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

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






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