# frozen_string_literal: true # 戦士 # 体力があり、3つの攻撃スキルを持っている class Fighter attr_reader :id, :skills attr_accessor :health def initialize(id, health, skills) @id = id @health = health @skills = skills @current_skill = nil end def attack_skill(index) @current_skill = skills_at(index) end def attack_power @current_skill.attack_power end def attack_delay @current_skill.attack_delay end def available_to_fight? health.positive? end private def skills_at(index) special_attack! if special_attack?(index) @skills.at(index - 1) end def special_attack?(index) @skills.at(index - 1).then(&:special_attack?) end def special_attack! @skills.each(&:obtain_buff!) end end class Fighter # 戦士が使う攻撃スキル # 攻撃速度と攻撃力を持っている # 攻撃速度と攻撃力がゼロの場合は、強化技を表す class AttackSkill ATTACK_SPEED_BUFF = -3 ATTACK_POWER_BUFF = 5 attr_reader :attack_delay, :attack_power def initialize(attack_delay, attack_power) @attack_delay = attack_delay @attack_power = attack_power end def special_attack? attack_delay.zero? && attack_power.zero? end # スキルを強化する # 攻撃速度と攻撃力が上昇する def obtain_buff! return if special_attack? # 強化技自身の場合は何もしない @attack_delay += ATTACK_SPEED_BUFF @attack_power += ATTACK_POWER_BUFF @attack_delay = 1 if @attack_delay <= 0 # 攻撃速度が速くなりすぎないように補正を入れる end end end # 戦闘ステージ # 体力の続く限り、戦士同士を戦わせて、最後に生き残った戦士の数を返す class FightStage def initialize(fighters, attacks_rounds) @fighters = fighters @attack_rounds = attacks_rounds end def fight! run_fight_loop! @fighters.find_all(&:available_to_fight?).size end private # 指定されたインデックスに対応する戦士とスキルを取得・設定する def find_fighter_and_set_skill(fighter_id, skill_index) fighter = @fighters.find { _1.id.eql?(fighter_id) } fighter.attack_skill(skill_index) fighter end # 戦闘に参加する2組の戦士を取得する def find_fighters(attack_round) attack_round.fighters.map do |fighter| find_fighter_and_set_skill(*fighter) end end def run_fight_loop! @attack_rounds.each do |attack_round| fighter1, fighter2 = find_fighters(attack_round) # どちらかが戦闘不能の場合、なにもしない next unless [fighter1, fighter2].any?(&available_to_fight?) # 攻撃速度に応じて、どちらの戦士が攻撃できるかを判定する # 攻撃速度が相手より優っている場合は、相手の体力を削る事が出来る if fighter1.attack_delay.eql?(fighter2.attack_delay) # なにもしない elsif fighter1.attack_delay < fighter2.attack_delay fighter2.health -= fighter1.attack_power else fighter1.health -= fighter2.attack_power end end end end class FightStage # 戦士が戦う相手とスキル、それぞれ1回戦分を保持する class AttackRound attr_reader :fighter1_id, :skill1_index, :fighter2_id, :skill2_index def initialize(fighter1_id, skill1_index, fighter2_id, skill2_index) @fighter1_id = fighter1_id @skill1_index = skill1_index @fighter2_id = fighter2_id @skill2_index = skill2_index end def fighter1 [@fighter1_id, @skill1_index] end def fighter2 [@fighter2_id, @skill2_index] end def fighters [fighter1, fighter2] end end end # メイン処理 # 戦士達のステータスやスキル情報・行動取得と戦闘ステージでの戦闘を実行する class Solver def solve! input fight_stage = FightStage.new(@fighters, @attack_rounds) fight_stage.fight! end private def input @number_of_fighters, @number_of_rounds = gets.chomp.split.map(&:to_i) input_fighters input_attack_rounds end def input_fighters @fighters = @number_of_fighters.times.map do |index| health, *attack_elements = splat_fighter_params attack_skills = attack_elements.map do |attack_delay, attack_power| Fighter::AttackSkill.new(attack_delay, attack_power) end Fighter.new(index + 1, health, attack_skills) end end # 戦士達が、誰に対してどのスキルを使い、戦いを進めていくかを取得する # ここでは 2人の戦士のIDと、それぞれのスキルのインデックスを取得する def input_attack_rounds @attack_rounds = @number_of_rounds.times.map do fighter1, skill1, fighter2, skill2 = gets.split.map(&:to_i) FightStage::AttackRound.new(fighter1, skill1, fighter2, skill2) end end # 戦士の体力と3つのスキルを取得する def splat_fighter_params health, *params = gets.split.map(&:to_i) attack_delay1, attack_power1, *params = params attack_delay2, attack_power2, *params = params attack_delay3, attack_power3 = params [ health, [attack_delay1, attack_power1], [attack_delay2, attack_power2], [attack_delay3, attack_power3] ] end end solver = Solver.new # # ruby main.rb < input.txt # puts solver.solve! # => 3