【JAVA】確認問題5-☆2:モンスターとのバトルゲームを作ろう

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

この記事の練習問題で使用する知識:
基礎文法と制御構造(レッスン1~2)メソッドの定義と呼び出しメソッドの戻り値クラスの定義と使用コンストラクタクラスメンバとインスタンスメンバクラスの継承抽象クラスインターフェース

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

<<前のページ JAVA Topへ 次のページ>>

確認問題5-☆2:モンスターとのバトルゲームを作ろう

勇者とドラゴンのバトルゲームを作成しましょう。

勇者とドラゴンはそれぞれ攻撃や防御ができ、どちらかのHPが0になるとゲームが終了します。

勇者は常に攻撃に成功しますが、ドラゴンは60%の確率でしか攻撃が成功しません。しかし攻撃が失敗するたびに成功率が上昇します。

また、防御を選んだキャラクターは次の攻撃を受けるときにダメージを無効化できます。

このルールに従って、バトルの流れを管理するプログラムを実装してください。

この問題の要件

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

  1. インターフェース CharacterActions を定義し、以下のメソッドを実装すること:
    • void attack(Character target):攻撃する。
    • void defend():防御する。
    • boolean isDefending():防御状態かを返す。
    • boolean isAlive():生存しているかを返す。
  2. Character クラスを抽象クラスとして定義し、以下の内容を含むこと:
    • String name:キャラクターの名前を保持するフィールド。
    • int hp:キャラクターのHP(体力)を保持するフィールド。
    • int attackPower:攻撃力を保持するフィールド。
    • boolean defending:防御状態かどうかを管理するフィールド。
    • attack メソッド:ターゲットが防御状態の場合、攻撃を無効化すること。
    • defend メソッド:キャラクターを防御状態にすること。
    • isDefending メソッド:防御状態かどうかを返すこと。
    • isAlive メソッド:HPが0より大きい場合に生存状態を返すこと。
    • getNamegetHp メソッド:名前とHPを取得するメソッド。
  3. Player クラスと Monster クラスを Character クラスを継承して作成すること:
    • Player クラス:HPが100、攻撃力が20の「勇者」として定義する。
    • Monster クラス:HPが100、攻撃力が40の「ドラゴン」として定義し、攻撃成功率を60%とする。攻撃が失敗するたびに成功率が20%ずつ増加すること。
  4. MonsterBattleGame クラスをメインクラスとして作成し、ゲームの進行を管理すること:
    • 勇者とドラゴンのHP、攻撃成功率を表示すること。
    • プレイヤーが攻撃か防御を選択し、入力を受け付けること。
    • 勇者またはドラゴンが倒された時点で勝者を表示してゲームを終了すること。

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

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

=== モンスターとのバトルゲーム ===
勇者のHP: 100, ドラゴンのHP: 100, ドラゴンの攻撃成功率: 60%
攻撃するか防御するかを選んでください (1: 攻撃, 2: 防御): 1
勇者の攻撃!
ドラゴンに20のダメージ!

ドラゴンのHP: 80, ドラゴンの攻撃!
攻撃が成功!
勇者に40のダメージ!

勇者のHP: 60, ドラゴンのHP: 80, ドラゴンの攻撃成功率: 60%
攻撃するか防御するかを選んでください (1: 攻撃, 2: 防御): 2
勇者は防御を選択した!
ドラゴンの攻撃!
攻撃がミス!
ドラゴンの攻撃成功率が20%上昇!

...

この問題を解くヒント

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

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

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

1:RandomクラスとScannerクラスをインポート
2:CharacterActionsインターフェースの定義
  □ void attack(Character target) メソッドの宣言
  □ void defend() メソッドの宣言
  □ boolean isDefending() メソッドの宣言
  □ boolean isAlive() メソッドの宣言
3:Character抽象クラスの定義
  □ protected String name フィールドの宣言
  □ protected int hp フィールドの宣言
  □ protected int attackPower フィールドの宣言
  □ protected boolean defending フィールドの宣言
  □ Characterクラスのコンストラクタの定義
  □ □ name、hp、attackPowerの値を初期化し、defendingをfalseに設定
  □ attackメソッドのオーバーライド
  □ □ ターゲットが防御中の場合は攻撃を無効化するメッセージを表示
  □ □ それ以外の場合はターゲットのhpからattackPower分のダメージを減算
  □ □ 攻撃後にdefendingをfalseにリセット
  □ defendメソッドのオーバーライド
  □ □ defendingをtrueに設定し、防御を選択したメッセージを表示
  □ isDefendingメソッドのオーバーライド
  □ □ defendingの状態を返す
  □ isAliveメソッドのオーバーライド
  □ □ hpが0より大きい場合にtrueを返す
  □ getNameメソッドの定義
  □ □ nameを返す
  □ getHpメソッドの定義
  □ □ hpを返す
4:Playerクラスの定義
  □ Characterクラスを継承し、”勇者”として初期化するコンストラクタの定義
5:Monsterクラスの定義
  □ Characterクラスを継承し、”ドラゴン”として初期化するコンストラクタの定義
  □ int attackSuccessRate フィールドの宣言(初期値60%)
  □ Randomクラスのインスタンス生成
  □ attackメソッドのオーバーライド
  □ □ 1から100までのランダムな数値を生成し、attackSuccessRate以下なら攻撃成功
  □ □ 攻撃が成功した場合は親クラスのattackメソッドを呼び出し、attackSuccessRateをリセット
  □ □ 攻撃が失敗した場合は「攻撃がミス」と表示し、attackSuccessRateを20%増加
  □ getAttackSuccessRateメソッドの定義
  □ □ attackSuccessRateを返す
6:MonsterBattleGameクラスの定義
  □ mainメソッドの定義
  □ □ Scannerオブジェクトの初期化
  □ □ PlayerインスタンスとMonsterインスタンスの生成
  □ □ 「モンスターとのバトルゲーム」と表示
  □ □ whileループでplayerとmonsterが生存している間バトルを継続
  □ □ □ playerとmonsterのHP、monsterの攻撃成功率を表示
  □ □ □ プレイヤーに攻撃または防御の選択を求めるメッセージを表示
  □ □ □ プレイヤーの入力を取得し、選択に応じた処理を実行
  □ □ □ □ choiceが1の場合、playerのattackメソッドでmonsterを攻撃
  □ □ □ □ choiceが2の場合、playerのdefendメソッドを実行
  □ □ □ □ 無効な入力があった場合はメッセージを表示して再度入力を求める
  □ □ □ モンスターの生存判定を行い、敗北時に勇者の勝利を表示してループを終了
  □ □ □ monsterのターンでplayerを攻撃
  □ □ □ playerの生存判定を行い、敗北時にドラゴンの勝利を表示

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

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

import java.util.Random;
import java.util.Scanner;

// アクションインターフェースの定義
/*【穴埋め問題1】
CharacterActionsインターフェースを定義し、キャラクターの動作として攻撃、守る、生存確認を行うメソッドを宣言してください。
*/

// キャラクターの共通部分を表す抽象クラス
abstract class Character implements CharacterActions {
    protected String name;
    protected int hp;
    protected int attackPower;
    protected boolean defending;

    public Character(String name, int hp, int attackPower) {
        this.name = name;
        this.hp = hp;
        this.attackPower = attackPower;
        this.defending = false;
    }

    public void attack(Character target) {
        if (target.isDefending()) {
            System.out.printf("%sの防御が成功し、攻撃は無効化された!\n", target.getName());
        } else {
            /*【穴埋め問題2】
            ここで攻撃が成功した場合の処理として、ターゲットのhpを減算するコードを書いてください。
            */
        }
        this.defending = false; // 次のターンには防御をリセット
    }

    public void defend() {
        /*【穴埋め問題3】
        ここでキャラクターを防御状態に設定するコードを書き、"○○は防御を選択した!"と出力するコードを書いてください。
        */
    }

    public boolean isDefending() {
        return defending;
    }

    public boolean isAlive() {
        return hp > 0;
    }

    public String getName() {
        return name;
    }

    public int getHp() {
        return hp;
    }
}

// プレイヤーとモンスタークラス
class Player extends Character {
    public Player() {
        /*【穴埋め問題4】
        Characterクラスのコンストラクタを呼び出して、「勇者」として初期化するコードを書いてください。
        */
    }
}

class Monster extends Character {
    private int attackSuccessRate;
    private Random random;

    public Monster() {
        /*【穴埋め問題5】
        Characterクラスのコンストラクタを呼び出して、「ドラゴン」として初期化し、attackSuccessRateを60に設定するコードを書いてください。
        */
    }

    public void attack(Character target) {
        int chance = random.nextInt(100) + 1;
        if (chance <= attackSuccessRate) {
            super.attack(target);
            attackSuccessRate = 60; // 成功したら確率をリセット
        } else {
            System.out.printf("%sの攻撃がミス!\n", name);
            attackSuccessRate += 20; // 失敗時は確率を上昇
        }
        this.defending = false;
    }

    public int getAttackSuccessRate() {
        return attackSuccessRate;
    }
}

// メインクラス
public class MonsterBattleGame {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Player player = new Player();
        Monster monster = new Monster();

        System.out.println("=== モンスターとのバトルゲーム ===");

        while (player.isAlive() && monster.isAlive()) {
            System.out.printf("%sのHP: %d, %sのHP: %d, %sの攻撃成功率: %d%%\n",
                    player.getName(), player.getHp(), monster.getName(), monster.getHp(), monster.getName(), monster.getAttackSuccessRate());

            System.out.print("攻撃するか防御するかを選んでください (1: 攻撃, 2: 防御): ");
            int choice = scanner.nextInt();

            if (choice == 1) {
                player.attack(monster);
            } else if (choice == 2) {
                player.defend();
            } else {
                System.out.println("無効な入力です。再度入力してください。");
                continue;
            }

            if (!monster.isAlive()) {
                System.out.println("ドラゴンは倒された!勇者の勝利!");
                break;
            }

            monster.attack(player);

            if (!player.isAlive()) {
                System.out.println("勇者は倒された…ドラゴンの勝利!");
            }
        }
    }
}

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

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

解答例と解説

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

正解コードの例

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

import java.util.Random;
import java.util.Scanner;

// アクションインターフェースの定義
interface CharacterActions {
    void attack(Character target);
    void defend();
    boolean isDefending();
    boolean isAlive();
}

// キャラクターの共通部分を表す抽象クラス
abstract class Character implements CharacterActions {
    protected String name;
    protected int hp;
    protected int attackPower;
    protected boolean defending;

    public Character(String name, int hp, int attackPower) {
        this.name = name;
        this.hp = hp;
        this.attackPower = attackPower;
        this.defending = false;
    }

    public void attack(Character target) {
        if (target.isDefending()) {
            System.out.printf("%sの防御が成功し、攻撃は無効化された!\n", target.getName());
        } else {
            target.hp -= this.attackPower;
            System.out.printf("%sは%sに%dのダメージを与えた!\n", this.name, target.getName(), this.attackPower);
        }
        this.defending = false; // 次のターンには防御をリセット
    }

    public void defend() {
        this.defending = true;
        System.out.printf("%sは防御を選択した!\n", this.name);
    }

    public boolean isDefending() {
        return defending;
    }

    public boolean isAlive() {
        return hp > 0;
    }

    public String getName() {
        return name;
    }

    public int getHp() {
        return hp;
    }
}

// プレイヤーとモンスタークラス
class Player extends Character {
    public Player() {
        super("勇者", 100, 20);
    }
}

class Monster extends Character {
    private int attackSuccessRate;
    private Random random;

    public Monster() {
        super("ドラゴン", 100, 40);
        this.attackSuccessRate = 60;
        this.random = new Random();
    }

    public void attack(Character target) {
        int chance = random.nextInt(100) + 1;
        if (chance <= attackSuccessRate) {
            super.attack(target);
            attackSuccessRate = 60; // 成功したら確率をリセット
        } else {
            System.out.printf("%sの攻撃がミス!\n", name);
            attackSuccessRate += 20; // 失敗時は確率を上昇
        }
        this.defending = false;
    }

    public int getAttackSuccessRate() {
        return attackSuccessRate;
    }
}

// メインクラス
public class MonsterBattleGame {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Player player = new Player();
        Monster monster = new Monster();

        System.out.println("=== モンスターとのバトルゲーム ===");

        while (player.isAlive() && monster.isAlive()) {
            // ステータス表示
            System.out.printf("%sのHP: %d, %sのHP: %d, %sの攻撃成功率: %d%%\n",
                    player.getName(), player.getHp(), monster.getName(), monster.getHp(), monster.getName(), monster.getAttackSuccessRate());

            // プレイヤーの行動選択
            System.out.print("攻撃するか防御するかを選んでください (1: 攻撃, 2: 防御): ");
            int choice = scanner.nextInt();

            if (choice == 1) {
                player.attack(monster);
            } else if (choice == 2) {
                player.defend();
            } else {
                System.out.println("無効な入力です。再度入力してください。");
                continue;
            }

            // ゲーム終了判定
            if (!monster.isAlive()) {
                System.out.println("ドラゴンは倒された!勇者の勝利!");
                break;
            }

            // モンスターのターン
            monster.attack(player);

            // ゲーム終了判定
            if (!player.isAlive()) {
                System.out.println("勇者は倒された…ドラゴンの勝利!");
            }
        }
    }
}

正解コードの解説

「MonsterBattleGame」のJavaコードを解説します。

以下の内容は各コードブロックの役割とその文法に焦点を当てています。

インターフェースの定義

interface CharacterActions {
    void attack(Character target);
    void defend();
    boolean isDefending();
    boolean isAlive();
}
  1. CharacterActions インターフェース:キャラクターの行動(攻撃や防御など)を定義しています。インターフェースは具体的な実装を持たず、共通の操作を定義するために使われます。
  2. 各メソッド
    • attack():攻撃アクションを行うためのメソッド。
    • defend():防御を行うメソッド。
    • isDefending()isAlive():それぞれ、防御状態と生存状態を確認するメソッドです。

抽象クラス Character

abstract class Character implements CharacterActions {
    protected String name;
    protected int hp;
    protected int attackPower;
    protected boolean defending;
    
    public Character(String name, int hp, int attackPower) {
        this.name = name;
        this.hp = hp;
        this.attackPower = attackPower;
        this.defending = false;
    }
    // メソッド内容は省略
}
  1. 抽象クラス Character:共通のプロパティやメソッドを持つキャラクターの基盤クラス。
  2. プロパティ
    • name:キャラクターの名前。
    • hp:体力を示すヒットポイント。
    • attackPower:攻撃力。
    • defending:防御状態を表すブール値。
  3. コンストラクタ:各キャラクターの名前、HP、攻撃力を初期化し、防御状態を「false(防御していない)」に設定。

Player クラス

class Player extends Character {
    public Player() {
        super("勇者", 100, 20);
    }
}

Player クラスCharacterクラスを継承し、プレイヤーの属性(名前、HP、攻撃力)を定義しています。

Monster クラス

class Monster extends Character {
    private int attackSuccessRate;
    private Random random;

    public Monster() {
        super("ドラゴン", 100, 40);
        this.attackSuccessRate = 60;
        this.random = new Random();
    }
    // メソッド内容は省略
}

Monster クラス:モンスター(ドラゴン)のプロパティと、攻撃成功率を管理。

  • attackSuccessRate:初期値60で、攻撃が成功する確率を管理。
  • Random:乱数生成のために使用し、攻撃の成否をランダムに決定。

メインクラス MonsterBattleGame

public class MonsterBattleGame {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Player player = new Player();
        Monster monster = new Monster();
        
        System.out.println("=== モンスターとのバトルゲーム ===");
        
        while (player.isAlive() && monster.isAlive()) {
            // ステータス表示
            System.out.printf("%sのHP: %d, %sのHP: %d, %sの攻撃成功率: %d%%\n",
                player.getName(), player.getHp(), monster.getName(), monster.getHp(), monster.getName(), monster.getAttackSuccessRate());

            // プレイヤーの行動選択
            System.out.print("攻撃するか防御するかを選んでください (1: 攻撃, 2: 防御): ");
            int choice = scanner.nextInt();

            if (choice == 1) {
                player.attack(monster);
            } else if (choice == 2) {
                player.defend();
            } else {
                System.out.println("無効な入力です。再度入力してください。");
                continue;
            }
            
            // ゲーム終了判定
            if (!monster.isAlive()) {
                System.out.println("ドラゴンは倒された!勇者の勝利!");
                break;
            }

            // モンスターのターン
            monster.attack(player);

            // ゲーム終了判定
            if (!player.isAlive()) {
                System.out.println("勇者は倒された…ドラゴンの勝利!");
            }
        }
    }
}

メインメソッド:ゲーム全体の進行を制御し、プレイヤーとモンスターが交互に行動します。

  • ゲームの流れ:プレイヤーとモンスターが生きている間、プレイヤーの行動を選び、その後モンスターが攻撃します。
  • 終了判定:キャラクターのHPがゼロになると勝敗を決定し、ゲームが終了します。

まとめ

このコードではJavaのインターフェースと抽象クラスを用いてキャラクターの行動を表現し、プレイヤーとモンスターの戦闘をシミュレートしています。

ゲームを通してクラスの継承やメソッドのオーバーライド、乱数生成など、多くの基本的なJavaの概念を学ぶことができます。

今後はこのコードを参考にしながら、異なる攻撃や防御パターンを追加して自分だけのバトルシステムを作ってみましょう。

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

<<前のページ JAVA Topへ 次のページ>>

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

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

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






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