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

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

この記事の練習問題で使用する知識:
基礎文法、制御構造、(レッスン1~3)メソッドの定義と呼出しデフォルト引数とキーワード引数クラスの定義と使用イニシャライザアクセスメソッドクラスの継承

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

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

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

この問題では簡単なターン制バトルゲームを作成します。

このプログラムではプレイヤーとモンスターが交互に攻撃を行い、どちらかのHPが0になるまで戦闘が続きます。

プレイヤーは攻撃するか防御するかを選べます。モンスターはランダムな攻撃成功率を持っており、攻撃が成功した場合、プレイヤーのHPが減少します。またモンスターの攻撃成功率は、成功または失敗によって変化します。

この問題の要件

以下の要件に従ったプログラムを作成すること。

  1. Character クラスを作成し、名前、HP、攻撃力を保持できるようにすること。
    • このクラスには攻撃力を動的に設定するメソッド set_power を追加すること。
  2. Player クラスを作成し、Character クラスを継承すること。
    • 名前の初期値は "プレイヤー" とする。
  3. Monster クラスを作成し、Character クラスを継承すること。
    • 攻撃成功率(attack_success_rate)を持つプロパティを追加し、初期値を 0.5 に設定する。
    • 成功率を更新するメソッド update_success_rate を実装し、失敗時は成功率を +0.2 増加(最大1.0まで)させること。
  4. ターン制の戦闘を実装すること。
    • プレイヤーは攻撃か防御を選択できる。
    • 防御を選択した場合、次のモンスターの攻撃成功率はリセットされる。
    • モンスターの攻撃が成功した場合、プレイヤーのHPを減らす。
    • いずれかのHPが0になった場合、勝者を表示して終了する。

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

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

ゲームを開始します。
次のモンスターの攻撃の成功率は 50% です。
攻撃しますか?防御しますか? (1: 攻撃, 2: 防御): 1
プレイヤーの攻撃: 25 ダメージ
モンスターのHP: 75
モンスターの攻撃: 40 ダメージ
プレイヤーのHP: 60
次のモンスターの攻撃の成功率は 50% です。
...
プレイヤーの勝利!

この問題を解くヒント

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

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

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

1:Characterクラスの定義
  □ name, attack_power, hpのアクセサメソッドを定義
  □ initializeメソッドでnameとhpを初期化(hpはデフォルト値100)
  □ set_powerメソッドでattack_powerを設定
2:Playerクラスの定義
  □ Characterクラスを継承
  □ initializeメソッドでデフォルトnameを”プレイヤー”に設定
3:Monsterクラスの定義
  □ Characterクラスを継承
  □ attack_success_rateのアクセサメソッドを定義
  □ initializeメソッドでnameとsuccess_rateを初期化(success_rateはデフォルト値0.5)
  □ update_success_rateメソッドで攻撃成功率を更新
4:ゲーム開始のメッセージを出力
5:Playerクラスのインスタンスplayerを生成
6:Monsterクラスのインスタンスmonsterを生成
7:playerとmonsterの攻撃力を設定
8:whileループ開始(player.hp > 0 && monster.hp > 0の条件下)
  □ monsterの次の攻撃成功率を出力
  □ プレイヤーに行動を選択させる(攻撃または防御)
  □ if文でactionが1(攻撃)の場合の処理
  □ □ monsterのhpをplayer.attack_power分減らす
  □ □ 攻撃内容とmonsterの現在hpを出力
  □ if文でactionが2(防御)の場合の処理
  □ □ 防御を選択したメッセージを出力
  □ else文(無効な選択肢の場合)
  □ □ 再選択を促すメッセージを出力し、次のループへ
  □ if文でmonster.hp <= 0ならループ終了
  □ randとmonster.attack_success_rateを比較しモンスターの攻撃を判定
  □ □ 攻撃が成功した場合
  □ □ □ if文でactionが2(防御)ならダメージを受けないメッセージを出力
  □ □ □ else文でplayerのhpをmonster.attack_power分減らす
  □ □ □ 攻撃内容とplayerの現在hpを出力
  □ □ □ update_success_rateメソッドで攻撃成功率を更新
  □ □ 攻撃が失敗した場合
  □ □ □ 攻撃失敗メッセージを出力
  □ □ □ update_success_rateメソッドで攻撃成功率を更新
  □ if文でactionが2(防御)ならmonster.attack_success_rateを0.5にリセット
9:whileループ終了
10:if文でplayer.hp > 0なら勝利メッセージを出力
11:else文で敗北メッセージを出力

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

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

# 共通のCharacterクラスを定義
class Character
  attr_accessor :name, :attack_power, :hp

  def initialize(name, hp = 100)
    @name = name
    @hp = hp
  end

  # メタプログラミングを使って動的に攻撃力を設定
=begin
【穴埋め問題1】
ここに攻撃力を設定するためのメソッド set_power を定義し、引数 attack を受け取りそれを @attack_power に代入するコードを書いてください。
=end
end

# Playerクラス
=begin
【穴埋め問題2】
ここでCharacterクラスを継承したPlayerクラスを作成し、initializeメソッドを定義してください。nameのデフォルト値を"プレイヤー"に設定してください。
=end

# Monsterクラス
class Monster < Character
  attr_accessor :attack_success_rate

  def initialize(name = "モンスター", success_rate = 0.5)
    super(name)
    @attack_success_rate = success_rate
  end

  # 攻撃成功率を更新
=begin
【穴埋め問題3】
ここにモンスターの攻撃成功率を更新するためのメソッド update_success_rate を定義してください。
  - 引数 success を受け取ります。
  - success が true の場合、成功率を 0.5 に設定してください。
  - 失敗した場合は、成功率を現在の成功率に 0.2 を加算した値(最大値 1.0)に設定してください。
=end
end

# ゲームを開始
puts "ゲームを開始します。"
=begin
【穴埋め問題4】
ここでPlayerクラスのインスタンス player と Monsterクラスのインスタンス monster を生成してください。
=end

# プレイヤーとモンスターの攻撃力を設定
=begin
【穴埋め問題5】
ここでplayerとmonsterのそれぞれに対して、set_powerメソッドを呼び出し攻撃力を設定してください。
  - player の攻撃力は 25
  - monster の攻撃力は 40
=end

# ターン制の戦闘
while player.hp > 0 && monster.hp > 0
  # モンスターの次の攻撃の成功率を表示
  puts "次のモンスターの攻撃の成功率は #{(monster.attack_success_rate * 100).round}% です。"
  
  # プレイヤーのターン
=begin
【穴埋め問題6】
ここでプレイヤーに攻撃(1)か防御(2)を選択させ、その結果に応じて以下を実行してください:
  - 攻撃(1)の場合は monster の HP を player.attack_power 分減少させる
  - 防御(2)の場合は「防御を選択しました」と表示する
=end

  break if monster.hp <= 0

  # モンスターのターン
=begin
【穴埋め問題7】
ここでモンスターがランダムな成功率に基づいて攻撃を行い、プレイヤーのHPを減らすかを処理してください。
  - 成功の場合、プレイヤーの HP を monster.attack_power 分減少させる(ただし、防御中はノーダメージ)
  - 成功または失敗に応じて、update_success_rate を適切に呼び出す
=end
end

# 勝敗判定
=begin
【穴埋め問題8】
ここでプレイヤーとモンスターのHPを比較し、勝利メッセージまたは敗北メッセージを表示してください。
=end

モンスターとのバトルゲームの正解コードと解説

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

正解コードの例

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

# 共通のCharacterクラスを定義
class Character
  attr_accessor :name, :attack_power, :hp

  def initialize(name, hp = 100)
    @name = name
    @hp = hp
  end

  # メタプログラミングを使って動的に攻撃力を設定
  def set_power(attack)
    self.attack_power = attack
  end
end

# Playerクラス
class Player < Character
  def initialize(name = "プレイヤー")
    super(name)
  end
end

# Monsterクラス
class Monster < Character
  attr_accessor :attack_success_rate

  def initialize(name = "モンスター", success_rate = 0.5)
    super(name)
    @attack_success_rate = success_rate
  end

  # 攻撃成功率を更新
  def update_success_rate(success)
    @attack_success_rate = success ? 0.5 : [@attack_success_rate + 0.2, 1.0].min
  end
end

# ゲームを開始
puts "ゲームを開始します。"
player = Player.new
monster = Monster.new

# プレイヤーとモンスターの攻撃力を設定
player.set_power(25)
monster.set_power(40)

# ターン制の戦闘
while player.hp > 0 && monster.hp > 0
  # モンスターの次の攻撃の成功率を表示
  puts "次のモンスターの攻撃の成功率は #{(monster.attack_success_rate * 100).round}% です。"
  
  # プレイヤーのターン
  puts "攻撃しますか?防御しますか? (1: 攻撃, 2: 防御): "
  action = gets.chomp.to_i
  if action == 1
    damage_to_monster = player.attack_power
    monster.hp -= damage_to_monster
    puts "#{player.name}の攻撃: #{damage_to_monster} ダメージ"
    puts "#{monster.name}のHP: #{monster.hp}"
  elsif action == 2
    puts "#{player.name}は防御を選択しました。"
  else
    puts "無効な選択です。再度選択してください。"
    next
  end

  break if monster.hp <= 0

  # モンスターのターン
  if rand < monster.attack_success_rate
    damage_to_player = monster.attack_power
    if action == 2
      puts "#{player.name}は防御に成功し、ダメージを受けませんでした。"
    else
      player.hp -= damage_to_player
      puts "#{monster.name}の攻撃: #{damage_to_player} ダメージ"
      puts "#{player.name}のHP: #{player.hp}"
    end
    monster.update_success_rate(true)
  else
    puts "#{monster.name}の攻撃は失敗しました。"
    monster.update_success_rate(false)
  end

  # プレイヤーが防御を選択した場合、モンスターの攻撃成功率をリセット
  monster.attack_success_rate = 0.5 if action == 2
end

# 勝敗判定
if player.hp > 0
  puts "#{player.name}の勝利!"
else
  puts "#{monster.name}の勝利!"
end

正解のコードの解説

今回解説するのは簡単なターン制バトルゲームのコードです。

このコードは「プレイヤー」と「モンスター」が交互に攻撃し、どちらかのHPが0になるまで戦闘を行う仕組みを実装しています。

それでは、各部分について詳しく解説します。

Characterクラスの定義

class Character
  attr_accessor :name, :attack_power, :hp

  def initialize(name, hp = 100)
    @name = name
    @hp = hp
  end

  def set_power(attack)
    self.attack_power = attack
  end
end
  • attr_accessor: nameattack_powerhpという属性を定義し、それらに読み書き可能なメソッドを自動生成します。
  • initializeメソッド: オブジェクトを生成する際に呼び出される特別なメソッドです。namehpを初期化します。hpにはデフォルト値100が設定されています。
  • set_powerメソッド: 攻撃力を設定するためのメソッドで、self.attack_powerを使って動的に値を設定します。

Playerクラスの定義

class Player < Character
  def initialize(name = "プレイヤー")
    super(name)
  end
end
  • < Character: PlayerクラスはCharacterクラスを継承しており、すべての機能を引き継ぎます。
  • super: 親クラス(Characterクラス)のinitializeメソッドを呼び出します。デフォルトの名前を"プレイヤー"としています。

Monsterクラスの定義

class Monster < Character
  attr_accessor :attack_success_rate

  def initialize(name = "モンスター", success_rate = 0.5)
    super(name)
    @attack_success_rate = success_rate
  end

  def update_success_rate(success)
    @attack_success_rate = success ? 0.5 : [@attack_success_rate + 0.2, 1.0].min
  end
end
  • attr_accessor: 攻撃成功率(attack_success_rate)を追加。
  • initializeメソッド: モンスターの名前と攻撃成功率を初期化します。成功率はデフォルトで0.5です。
  • update_success_rateメソッド: 攻撃が成功した場合は成功率をリセット(0.5)、失敗した場合は最大値1.0まで増加させます。

ゲームの開始

player = Player.new
monster = Monster.new
player.set_power(25)
monster.set_power(40)
  • Player.newMonster.new: プレイヤーとモンスターのインスタンスを作成します。
  • set_power: 攻撃力をそれぞれ設定します。

ターン制バトルの実装

while player.hp > 0 && monster.hp > 0
  # モンスターの攻撃成功率を表示
  puts "次のモンスターの攻撃の成功率は #{(monster.attack_success_rate * 100).round}% です。"

  # プレイヤーのターン
  action = gets.chomp.to_i
  if action == 1
    damage_to_monster = player.attack_power
    monster.hp -= damage_to_monster
  elsif action == 2
    # 防御時の処理
  end

  break if monster.hp <= 0

  # モンスターのターン
  if rand < monster.attack_success_rate
    # 成功時の処理
  else
    # 失敗時の処理
  end
end
  1. whileループ: プレイヤーとモンスターのHPが両方とも正の値である間、戦闘を続けます。
  2. プレイヤーの行動選択:
    • 攻撃 (1): モンスターにダメージを与えます。
    • 防御 (2): モンスターの攻撃成功率をリセットします。
  3. モンスターの攻撃:
    • rand < attack_success_rate: 攻撃が成功するかランダムで判定します。
    • 攻撃が失敗した場合、成功率を増加させます。

勝敗の判定

if player.hp > 0
  puts "#{player.name}の勝利!"
else
  puts "#{monster.name}の勝利!"
end

条件分岐: プレイヤーのHPが0より大きければ勝利、そうでなければモンスターの勝利とします。

まとめ

このコードは、Rubyのオブジェクト指向プログラミングを学ぶのに最適なコードです。

クラスの継承や動的プロパティの設定、そしてランダム処理を組み合わせたゲームロジックを実装しています。

このプログラムを通じて、プログラム構造の設計やクラス間の関係性を理解する第一歩を踏み出しましょう。

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

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

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

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

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






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