Kotlin練習問題4-☆2:石取りゲームを作ろう

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

Kotlinの初心者向け問題4-☆2:石取りゲームを作ろう

この問題を解くために必要な知識:

【レベル1~3の知識】
コメントの書き方、変数と定数、基本データ型、算術演算と論理演算、入力と出力、import文、配列、分岐処理(if、if~else、when)、繰り返し処理(for、while、do~while)、Null安全、スマートキャスト、関数の定義と呼び出し、関数の戻り値、関数のオーバーロード、ラベルとジャンプ、例外処理、クラスの定義と使用、インスタンス、コンストラクタ、プロパティ、クラスの継承、クラスの拡張、コレクションの基礎、リストコレクション(MutableList、ArrayList)、セットコレクション(HashSet、MutableSet、TreeSet)、マップコレクション(HashMap、MutableMap、TreeMap)

【レベル4の知識】
メンバ関数、ゲッターとセッター、カプセル化、クラスメンバ、抽象クラス、インターフェース、データクラス

<<前の問題 問題集Top 次の問題>>

Kotlin練習問題4-☆2:石取りゲームを作ろう





この課題では、2人のプレイヤーが交互に石を取り合う「石取りゲーム」を作成します。

各プレイヤーは1ターンで1~3個の石を取ることができます。最後の石を取ったプレイヤーが負けとなります。

この問題の要件

以下の要件に従ってプログラムを作成してください。

  1. 抽象クラス Player を定義すること
    • name という名前のプロパティ(String 型)を持つこと。
    • takeStones という抽象関数を持つこと。この関数は、プレイヤーが石を取る処理を定義します。
  2. Player クラスを継承する HumanPlayer クラスを定義すること
    • takeStones 関数をオーバーライドし、プレイヤーに1~3個の石を取るように指示すること。
  3. Player クラスを継承する AIPlayer クラスを定義すること
    • takeStones 関数をオーバーライドし、ランダムに1~3個の石を取ること。
  4. Game クラスを定義し、ゲームのロジックを実装すること
    • totalStones という名前のプロパティを持ち、ゲーム開始時の石の数を格納すること。
    • startGame という名前のメンバ関数を定義し、ゲームの進行を制御すること。
  5. main 関数でゲームを実行すること
    • Game オブジェクトを作成し、石の数を設定すること(例えば20個)。
    • HumanPlayerAIPlayer のオブジェクトを作成し、ゲームを開始すること。

ただし、以下のような実行結果となること。

----- ↓出力される結果の例↓ -----

残りの石の数: 20
あなた さん、何個の石を取りますか? (1~3個): 
2
残りの石の数: 18
コンピュータ は 3 個の石を取りました
残りの石の数: 15
あなた さん、何個の石を取りますか? (1~3個): 
3
残りの石の数: 12
コンピュータ は 1 個の石を取りました
残りの石の数: 11
あなた さん、何個の石を取りますか? (1~3個): 
2
残りの石の数: 9
コンピュータ は 2 個の石を取りました
残りの石の数: 7
あなた さん、何個の石を取りますか? (1~3個): 
3
残りの石の数: 4
コンピュータ は 1 個の石を取りました
残りの石の数: 3
あなた さん、何個の石を取りますか? (1~3個): 
3
あなた が最後の石を取りました!
あなた の負けです!

----- ↑出力される結果の例↑ -----

この問題を解くヒント

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

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

正解のコードは上から順に以下のような構成となっています。

1.Randomクラスのインポート
2.抽象クラスPlayerの定義
 2-1. nameプロパティの宣言
 2-2. 抽象関数takeStonesの定義
3.HumanPlayerクラスの定義
 3-1. Playerクラスを継承
 3-2. コンストラクタでnameを受け取る
 3-3. takeStones関数のオーバーライド
4.AIPlayerクラスの定義
 4-1. Playerクラスを継承
 4-2. コンストラクタでnameを受け取る
 4-3. takeStones関数のオーバーライド
5.Gameクラスの定義
 5-1. totalStonesプロパティの宣言
 5-2. ゲームの進行を制御するstartGame関数の定義
  5-2-1. remainingStones変数の初期化
  5-2-2. ゲーム進行中のループ処理
   5-2-2-1. 現在のプレイヤーが石を取る処理
   5-2-2-2. プレイヤー交代の処理
6.main関数の定義
 6-1. Gameオブジェクトの作成
 6-2. HumanPlayerAIPlayerオブジェクトの作成
 6-3. ゲーム開始のためにstartGame関数を呼び出す

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

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

import kotlin.random.Random

// 抽象クラス Player を定義
abstract class Player(val name: String) {
    // プレイヤーが石を取るための抽象関数
    abstract fun takeStones(totalStones: Int): Int
}

// 人間プレイヤーを表すクラス
class HumanPlayer(name: String) : Player(name) {
    // 人間プレイヤーが石を取る処理を実装
    override fun takeStones(totalStones: Int): Int {
        println("$name さん、何個の石を取りますか? (1~3個): ")
        /* 【穴埋め問題1】
           ここにユーザーの入力を受け取り、変数 stones に格納するコードを書いてください。
           1~3の範囲に制限するための処理も含めてください。
        */
    }
}

// AIプレイヤーを表すクラス
class AIPlayer(name: String) : Player(name) {
    // AIプレイヤーがランダムに石を取る処理を実装
    override fun takeStones(totalStones: Int): Int {
        /* 【穴埋め問題2】
           ここにAIが1~3個のランダムな数の石を取るコードを書いてください。
        */
    }
}

// ゲームのロジックを実装するクラス
class Game(val totalStones: Int) {
    private var remainingStones = totalStones

    // ゲームを開始するメンバ関数
    fun startGame(player1: Player, player2: Player) {
        var currentPlayer = player1

        while (remainingStones > 0) {
            println("残りの石の数: $remainingStones")

            /* 【穴埋め問題3】
               ここに現在のプレイヤーが石を取る処理と、
               その結果、残りの石を減らすコードを書いてください。
               最後の石を取ったプレイヤーを判定し、負けと表示する処理も含めてください。
            */

            // プレイヤー交代
            currentPlayer = if (currentPlayer == player1) player2 else player1
        }
    }
}

fun main() {
    // 石の数を設定してゲームを開始
    val game = Game(20)

    // 人間プレイヤーとAIプレイヤーを作成
    val human = HumanPlayer("あなた")
    val ai = AIPlayer("コンピュータ")

    // ゲーム開始
    game.startGame(human, ai)
}

 

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

解答例

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

正解コードの例

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

********************

import kotlin.random.Random

// 抽象クラス Player を定義
abstract class Player(val name: String) {
    // プレイヤーが石を取るための抽象関数
    abstract fun takeStones(totalStones: Int): Int
}

// 人間プレイヤーを表すクラス
class HumanPlayer(name: String) : Player(name) {
    // 人間プレイヤーが石を取る処理を実装
    override fun takeStones(totalStones: Int): Int {
        println("$name さん、何個の石を取りますか? (1~3個): ")
        val stones = readLine()?.toIntOrNull() ?: 1
        return stones.coerceIn(1, 3) // 1~3の範囲に制限
    }
}

// AIプレイヤーを表すクラス
class AIPlayer(name: String) : Player(name) {
    // AIプレイヤーがランダムに石を取る処理を実装
    override fun takeStones(totalStones: Int): Int {
        val stones = Random.nextInt(1, 4) // 1~3のランダムな数
        println("$name は $stones 個の石を取りました")
        return stones
    }
}

// ゲームのロジックを実装するクラス
class Game(val totalStones: Int) {
    private var remainingStones = totalStones

    // ゲームを開始するメンバ関数
    fun startGame(player1: Player, player2: Player) {
        var currentPlayer = player1

        while (remainingStones > 0) {
            println("残りの石の数: $remainingStones")

            val stonesTaken = currentPlayer.takeStones(remainingStones)
            remainingStones -= stonesTaken

            if (remainingStones <= 0) {
                println("${currentPlayer.name} が最後の石を取りました!")
                println("${currentPlayer.name} の負けです!")
                break
            }

            // プレイヤー交代
            currentPlayer = if (currentPlayer == player1) player2 else player1
        }
    }
}

fun main() {
    // 石の数を設定してゲームを開始
    val game = Game(20)

    // 人間プレイヤーとAIプレイヤーを作成
    val human = HumanPlayer("あなた")
    val ai = AIPlayer("コンピュータ")

    // ゲーム開始
    game.startGame(human, ai)
}

********************

正解コードの解説

このコードでは、プレイヤーとコンピュータが交互に石を取る「石取りゲーム」を実装しています。

ゲームは、プレイヤーとAI(コンピュータ)が交互に1~3個の石を取るシンプルなルールです。

ここでは、抽象クラスを使ってプレイヤーの共通の機能を実装し、それを継承して具体的なプレイヤーとAIの動作を定義しています。

import kotlin.random.Random

Kotlin標準ライブラリのRandomクラスをインポートしています。これにより、プログラムでランダムな数値を生成することができます。

抽象クラス Player の定義

abstract class Player(val name: String)

抽象クラスは、共通の機能を定義するためのクラスです。このクラス自体はインスタンス化されませんが、他のクラスがこれを継承して具体的な機能を実装します。

Playerクラスは、nameという名前のプロパティを持ち、各プレイヤーの名前を保持します。

また、takeStonesという抽象メソッドを定義しており、具体的なプレイヤークラスでこのメソッドを実装する必要があります。

takeStones 関数

abstract fun takeStones(totalStones: Int): Int

この関数は、プレイヤーがどれだけの石を取るかを決定するための抽象メソッドです。HumanPlayerAIPlayerクラスで具体的に実装されます。

HumanPlayer クラス

class HumanPlayer(name: String) : Player(name)

Playerクラスを継承するクラスです。プレイヤーが石を取る際の動作を実装しています。ユーザーに対して1~3の範囲で石を取る数を入力させ、その数を返します。

AIPlayer クラス

class AIPlayer(name: String) : Player(name)

同様に、Playerクラスを継承するクラスです。AIがランダムに1~3個の石を取る動作を実装しています。

Random.nextInt(1, 4)を使ってランダムな数値を生成し、その数を返します。

Game クラス

class Game(val totalStones: Int)

このクラスはゲームのロジック全体を管理します。totalStonesはゲーム開始時の石の総数です。startGame関数を使ってゲームを進行させます。

startGame 関数

この関数は、ゲームを開始し、プレイヤーが交互に石を取る動作を制御します。石が0になるまでゲームを続け、最後に石を取ったプレイヤーが負けとなります。

main 関数

main関数はプログラムのエントリーポイントです。ここでは、ゲームの初期化やプレイヤーの作成、ゲームの開始が行われます。

まとめ

このコードでは、Kotlinの抽象クラスと継承の基本的な使い方を学ぶことができます。

Playerクラスを抽象クラスとして定義し、それを継承して具体的なHumanPlayerAIPlayerを実装することで、共通の機能を効率的に管理できる点が理解できるでしょう。

<<前の問題 問題集Top 次の問題>>

この問題への質問・コメント

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

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






    Kotlin練習問題集へ戻る
    トップページへ戻る