Ruby練習問題4-☆3:モンスターとの戦闘ゲームを作ろう

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

Rubyの初心者向け問題4-☆3:モンスターとの戦闘ゲームを作ろう

この問題を解くために必要な知識:
【レベル1~3の知識】
コメントの書き方、変数と定数、基本データ型、四則演算と演算子、入力と出力、文字列操作、分岐処理(if、if~else、elsif、case)、繰り返し処理(for、while、until、each)、範囲(Range)、例外処理(begin-rescue-end)、配列(Array)、ハッシュ(Hash)、コレクション操作(配列、ハッシュの操作)、Enumerableモジュール、メソッドの定義と呼び出し、メソッドの戻り値、クラスの定義と使用、コンストラクタ、クラスの継承、モジュールの定義と使用、ミックスイン

【レベル4の知識】
アクセスメソッド(ゲッター、セッター)、カプセル化、クラスメソッドとインスタンスメソッド、共通メソッド、ブロックとイテレータ、プロック(Proc)、ラムダ(Lambda)、ファイル操作(読み書き)、正規表現、メタプログラミングの基礎

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

Ruby練習問題4-☆3:モンスターとの戦闘ゲームを作ろう

シンプルな戦闘ゲームを作成しましょう。

このゲームではプレイヤーとモンスターはそれぞれターンごとに攻撃を行い、相手のHPを0にした方が勝利です。

プレイヤーとモンスターの攻撃力はメタプログラミングを使って動的に設定します。

この問題の要件

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

  • PlayerクラスとMonsterクラスを作成します。
  • 各クラスにはnameattack_powerのアクセサメソッドを定義します。
  • メタプログラミングを使って、プレイヤーとモンスターの攻撃力を動的に設定するメソッドを定義します。
  • プレイヤーの攻撃力は25とし、モンスターの攻撃力は40とします。
  • ゲームはターン制で進行し、プレイヤーとモンスターは交互に攻撃します。
  • モンスターの攻撃の成功率は50%ですが、攻撃に失敗した場合は次の攻撃の成功率が20%上がります。攻撃に成功した場合は次の攻撃の成功率は50%に戻ります。プレイヤーが防御を選んだ場合、その次のモンスターの攻撃の成功率は50%になります。
  • プレイヤーは毎回攻撃するか防御するかを選択できます。防御を選んだ場合、モンスターから受けるダメージが0になります。
  • 攻撃の結果は攻撃力の値が相手のHPから減算されます。HPが0以下になった方が負けです。
  • プレイヤーとモンスターの初期HPはそれぞれ100とします。

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

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

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

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

この問題を解くヒント

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

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

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

1.Playerクラスの定義
 1-1. attr_accessor で name, attack_power, hp を定義
 1-2. initialize メソッドで初期化(デフォルトの名前は”プレイヤー”, HPは100)

2.Monsterクラスの定義
 2-1. attr_accessor で name, attack_power, hp, attack_success_rate を定義
 2-2. initialize メソッドで初期化(デフォルトの名前は”モンスター”, HPは100, 攻撃成功率は0.5)
 2-3. update_success_rate メソッドで攻撃成功率を更新するメソッドを定義

3.メタプログラミングを使ったメソッド定義
 3-1. set_attack_power メソッドを定義し、プレイヤーとモンスターの攻撃力を動的に設定

4.ゲームの開始
 4-1. プレイヤーとモンスターのインスタンスを作成
 4-2. set_attack_power メソッドを使用して、プレイヤーとモンスターの攻撃力を設定
 4-3. ターン制の戦闘を開始
  4-3-1. プレイヤーとモンスターのHPが0以上である限り戦闘を続行
   4-3-1-1. モンスターの次の攻撃の成功率を表示
   4-3-1-2. プレイヤーのターンで攻撃か防御かを選択
    4-3-1-2-1. 攻撃を選んだ場合、モンスターにダメージを与える
    4-3-1-2-2. 防御を選んだ場合、防御メッセージを表示
    4-3-1-2-3. 無効な選択の場合、再度選択を促す
   4-3-1-3. モンスターのHPが0以下の場合、戦闘を終了
   4-3-1-4. モンスターのターンで攻撃の成功をランダムに判定
    4-3-1-4-1. 攻撃が成功した場合、プレイヤーにダメージを与え、成功率をリセット
    4-3-1-4-2. 防御を選んだ場合、ダメージを受けない
    4-3-1-4-3. 攻撃が失敗した場合、攻撃成功率を増加
   4-3-1-5. プレイヤーが防御を選んだ場合、モンスターの攻撃成功率をリセット

5.勝敗判定
 5-1. プレイヤーのHPが0より大きい場合、プレイヤーの勝利を表示
 5-2. モンスターのHPが0より大きい場合、モンスターの勝利を表示

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

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

# Playerクラスを定義
class Player
  attr_accessor :name, :attack_power, :hp

  def initialize(name = "プレイヤー")
    @name = name
    @hp = 100
  end
end

# Monsterクラスを定義
class Monster
  attr_accessor :name, :attack_power, :hp, :attack_success_rate

  def initialize(name = "モンスター")
    @name = name
    @hp = 100
    @attack_success_rate = 0.5 # 初期攻撃成功率
  end

  # 攻撃成功率を更新
  def update_success_rate(success)
    if success
      @attack_success_rate = 0.5
    else
      @attack_success_rate += 0.2
    end
  end
end

# メタプログラミングを使って動的に攻撃力を設定
def set_attack_power(object, attack_power)
  object.instance_eval do
    ***ここにメソッドを動的に定義して攻撃力を設定するコードを書いてください***
  end
end

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

# プレイヤーとモンスターの攻撃力を設定
***ここにプレイヤーの攻撃力を設定するコードを書いてください***
***ここにモンスターの攻撃力を設定するコードを書いてください***

# ターン制の戦闘
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
    ***ここにプレイヤーが攻撃するコードを書いてください***
  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
      ***ここにモンスターが攻撃するコードを書いてください***
    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

 

この問題の解答例

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

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

# Playerクラスを定義
class Player
  attr_accessor :name, :attack_power, :hp

  def initialize(name = "プレイヤー")
    @name = name
    @hp = 100
  end

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

# Monsterクラスを定義
class Monster
  attr_accessor :name, :attack_power, :hp, :attack_success_rate

  def initialize(name = "モンスター")
    @name = name
    @hp = 100
    @attack_success_rate = 0.5 # 初期攻撃成功率
  end

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

  # 攻撃成功率を更新
  def update_success_rate(success)
    if success
      @attack_success_rate = 0.5
    else
      @attack_success_rate += 0.2
    end
  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

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

正解のコードの解説

このRubyコードでは、プレイヤーとモンスターが戦うターン制のバトルゲームを実装しています。

メタプログラミングを使って動的に攻撃力を設定する方法や、攻撃成功率の管理を行いながらゲームが進行する仕組みを解説します。

PlayerクラスとMonsterクラスの定義

class Player
  attr_accessor :name, :attack_power, :hp

  def initialize(name = "プレイヤー")
    @name = name
    @hp = 100
  end

  def set_power(attack)
    self.attack_power = attack
  end
end

Playerクラスはプレイヤーキャラクターを定義します。名前、攻撃力、HP(体力)を持ちます。

  • initialize: プレイヤーを初期化します。@nameにはデフォルトで「プレイヤー」が設定され、@hpは100です。
  • attr_accessor: name(名前)、attack_power(攻撃力)、hp(体力)にアクセスできるようにします。
  • set_power: このメソッドはプレイヤーの攻撃力を動的に設定するために使います。メタプログラミングを活用して、後から攻撃力を自由に変更できるようにしています。
class Monster
  attr_accessor :name, :attack_power, :hp, :attack_success_rate

  def initialize(name = "モンスター")
    @name = name
    @hp = 100
    @attack_success_rate = 0.5
  end

  def set_power(attack)
    self.attack_power = attack
  end

  def update_success_rate(success)
    if success
      @attack_success_rate = 0.5
    else
      @attack_success_rate += 0.2
    end
  end
end

Monsterクラスはモンスターキャラクターを定義します。Playerクラスと似た構造ですが、モンスターには追加で@attack_success_rate(攻撃成功率)があります。

モンスターの攻撃成功率は0.5(50%)からスタートし、攻撃が失敗するたびに増加します。

ゲーム開始の設定

puts "ゲームを開始します。"
player = Player.new
monster = Monster.new
player.set_power(25)
monster.set_power(40)

ここではゲームの開始を告げ、プレイヤーとモンスターのインスタンスを作成します。

  • player.set_power(25): プレイヤーの攻撃力を25に設定しています。
  • monster.set_power(40): モンスターの攻撃力は40に設定されています。

プレイヤーとモンスターの戦闘ロジック

while player.hp > 0 && monster.hp > 0

このループでは、プレイヤーとモンスターのHPがどちらも0より大きい間、戦闘が続行されます。以下の順番でプレイヤーとモンスターのターンが進行します。

プレイヤーのターン

puts "攻撃しますか?防御しますか? (1: 攻撃, 2: 防御): "
action = gets.chomp.to_i

プレイヤーは「攻撃」または「防御」を選択します。入力が1なら攻撃、2なら防御が行われます。無効な選択をした場合、再度入力を求められます。

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}は防御を選択しました。"
end
  • 攻撃の場合: プレイヤーの攻撃力(player.attack_power)に応じてモンスターにダメージを与え、その分モンスターのHPを減らします。
  • 防御の場合: プレイヤーは防御を選択します。この後、モンスターの攻撃からダメージを守る役割を果たします。

モンスターのターン

if rand < monster.attack_success_rate

モンスターの攻撃が成功するかどうかは、randでランダムな値を生成し、monster.attack_success_rate(攻撃成功率)と比較することで決まります。

例えば、成功率が50%(0.5)なら、randが0.5未満であれば攻撃が成功します。

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
  • 防御成功: プレイヤーが防御を選択している場合、モンスターの攻撃は失敗し、ダメージを受けません。
  • 攻撃成功: 防御をしていない場合は、モンスターが攻撃を成功させ、プレイヤーのHPにダメージが加えられます。
monster.update_success_rate(true)

モンスターの攻撃が成功した場合、attack_success_rate(攻撃成功率)はリセットされ、次回の攻撃も50%の確率で成功します。失敗した場合、成功率が0.2増加します。

勝敗の判定

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

戦闘終了後、プレイヤーかモンスターのどちらかのHPが0以下になった時点で、勝者が決まります。

プレイヤーが勝てばその旨が表示され、逆の場合はモンスターの勝利が表示されます。

まとめ

このコードを通じて、Rubyでのクラス定義メタプログラミングによる動的なメソッド操作、条件分岐ランダムな処理など、基本的なプログラミング技術を学ぶことができます。

ゲームを通して、コードがどのように動作しているか理解を深めることができます。

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

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

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

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






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